[英]Dockerfile and Docker Compose for NestJS app with PSQL DB where env vars are expected at runtime
I'm Dockerizing a simple Node/JS (NestJS -- but I don't think that matters for this question) web service and have some questions.我正在 Dockerizing 一个简单的 Node/JS(NestJS——但我认为这对这个问题并不重要)web 服务并有一些问题。 This service talks to a Postgres DB.
该服务与 Postgres DB 对话。 I would like to write a
Dockerfile
that can be used to build an image of the service (let's call it my-service
) and then write a docker-compose.yml
that defines a service for the Postgres DB as well as a service for my-service
that uses it.我想编写一个
Dockerfile
可用于构建服务的映像(我们称之为my-service
),然后编写一个docker-compose.yml
定义 Postgres DB 的服务以及my-service
使用它。 That way I can build images of my-service
but also have a Docker Compose config for running the service and its DB at the same time together.这样我可以构建
my-service
的映像,但也有一个 Docker Compose 配置,用于同时运行服务及其数据库。 I think that's the way to do this (keep me honest though.).我认为这是做到这一点的方法(但请保持诚实。)。 Kubernetes is not an option for me, just FYI.
Kubernetes 对我来说不是一个选项,仅供参考。
The web service has a top-level directory structure like so: web 服务的顶级目录结构如下:
my-service/
.env
package.json
package-lock.json
src/
<lots of other stuff>
Its critical to note that in its present, non-containerized form, you have to set several environment variables ahead of time, including the Postgres DB connection info (host, port, database name, username, password, etc.).需要注意的是,在目前的非容器化形式中,您必须提前设置几个环境变量,包括 Postgres DB 连接信息(主机、端口、数据库名称、用户名、密码等)。 The application code fetches the values of these env vars at runtime and uses them to connect to Postgres.
应用程序代码在运行时获取这些环境变量的值并使用它们连接到 Postgres。
So, I need a way to write a Dockerfile
and docker-compose.yml
such that:所以,我需要一种方法来编写
Dockerfile
和docker-compose.yml
,这样:
my-service
image by itself, and want to tell it to connect to any arbitrary Postgres DB, I can pass those env vars in as (ideally) runtime arguments on the Docker CLI command (however remember the app expects them to be set as env vars);my-service
映像的容器,并想告诉它连接到任意 Postgres DB,我可以在 Docker CLI 命令上将这些环境变量作为(理想情况下)运行时 arguments 传递(但是请记住应用程序希望将它们设置为环境变量); andmy-service
and its Postgres together via the Docker Compose file, I need to also specify those as runtime args in the Docker Compose CLI, then Docker Compose needs to pass them on to the container's run arguments, and then the container needs to set them as env vars for web service to use my-service
and its Postgres together via the Docker Compose file, I need to also specify those as runtime args in the Docker Compose CLI, then Docker Compose needs to pass them on to the container's run arguments, and然后容器需要将它们设置为 web 服务使用的环境变量Again, I think this is the correct way to go, but keep me honest!同样,我认为这是 go 的正确方法,但请保持诚实!
So my best attempt -- a total WIP so far -- looks like this:所以我最好的尝试——到目前为止的总在制品——看起来像这样:
Dockerfile
FROM node:18
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install
COPY . .
# creates "dist" to run out of
RUN npm run build
# ideally the env vars are already set at this point via
## docker CLI arguments, so nothing to pass in here (???)
CMD [ "node", "dist/main.js" ]
docker-compose.yml
version: '3.7'
services:
postgres:
container_name: postgres
image: postgres:14.3
environment:
POSTGRES_PASSWORD: ${psql.password}
POSTGRES_USER: ${psql.user}
POSTGRES_DB: my-service-db
PG_DATA: /var/lib/postgresql2/data
ports:
- 5432:5432
volumes:
- pgdata:/var/lib/postgresql2/data
my-service:
container_name: my-service
image: ??? anyway to say "build whats in the repo?"
environment:
??? do I need to set anything here so it gets passed to the my-service
container as env vars?
volumes:
pgdata:
Can anyone help nudge me over the finish line here?谁能帮我推过终点线? Thanks in advance!
提前致谢!
???
??? do I need to set anything here so it gets passed to the my-service container as env vars?
我是否需要在此处设置任何内容以便将其作为环境变量传递给 my-service 容器?
Yes, you should pass the variables there.是的,您应该在那里传递变量。 This is a principle of 12 factor design
这是12因素设计的原则
need to also specify those as runtime args in the Docker Compose CLI, then Docker Compose needs to pass them on to the container's run arguments
还需要在 Docker Compose CLI 中将它们指定为运行时参数,然后 Docker Compose 需要将它们传递给容器的运行 arguments
If you don't put them directly in the YAML, will this option work for you?如果您不直接将它们放在 YAML 中,此选项是否适合您?
docker-compose --env-file app.env up
Ideally, you also put理想情况下,你也把
depends_on:
postgres
So that when you start your service, the database will also start up.这样当您启动服务时,数据库也将启动。
If you want to connect to a different database instance, then you can either create a separate compose file without that database, or use a different set of variables (written out, or using env_file
, as mentioned)如果要连接到不同的数据库实例,则可以在没有该数据库的情况下创建单独的 compose 文件,或使用不同的变量集(如前所述,写出或使用
env_file
)
Or you can use NPM dotenv
or config
packages and set different .env
files for different database environments, based on other variables, such as NODE_ENV
, at runtime.或者您可以使用 NPM
dotenv
或config
包,并在运行时根据其他变量(例如NODE_ENV
)为不同的数据库环境设置不同的.env
文件。
???
??? anyway to say "build whats in the repo?"
无论如何要说“在回购中构建什么?”
Use build
instead of image
directive.使用
build
而不是image
指令。
Kubernetes is not an option for me, just FYI
Kubernetes 不适合我,仅供参考
You could use Minikube instead of Compose... Doesn't really matter, but kompose
exists to convert a Docker Compose into k8s resources.您可以使用 Minikube 代替 Compose ... 没关系,但
kompose
存在将 Docker Compose 转换为 k8s 资源。
Your Dockerfile
is correct.您的
Dockerfile
是正确的。 You can specify the environment variables while doing docker run
like this:您可以在执行
docker run
时指定环境变量,如下所示:
docker run --name my-service -it <image> -e PG_USER='user' -e PG_PASSWORD='pass'
-e PG_HOST='dbhost' -e PG_DATABASE='dbname' --expose <port>
Or you can specify the environment variables with the help of .env
file.或者您可以在
.env
文件的帮助下指定环境变量。 Let's call it app.env
.我们称之为
app.env
。 Its content would be:它的内容是:
PG_USER=user
PG_PASSWORD=pass
PG_DATABASE=dbname
PG_HOST=dbhost
OTHER_ENV_VAR1=someval
OTHER_ENV_VAR2=anotherval
Now instead of specifying multiple -e
options to docker run
command, you can simply tell the name of the file from where the environment variables need to be picked up.现在,无需为
docker run
命令指定多个-e
选项,您只需告知需要从何处获取环境变量的文件名。
docker run --name my-service -it <image> --env-file app.env --expose <port>
In order to run postgres and your service with a single docker compose command, a few modifications need to be done in your docker-compose.yml
.为了使用单个 docker compose 命令运行 postgres 和您的服务,需要在
docker-compose.yml
中进行一些修改。 Let's first see the full YAML
.我们先来看看完整的
YAML
。
version: '3.7'
services:
postgres:
container_name: postgres
image: postgres:14.3
environment:
POSTGRES_PASSWORD: $PG_PASSWORD
POSTGRES_USER: $PG_USER
POSTGRES_DB: my-service-db
PG_DATA: /var/lib/postgresql2/data
ports:
- 5432:5432
volumes:
- pgdata:/var/lib/postgresql2/data
my-service:
container_name: my-service
build: . #instead of image directive, use build to tell docker what folder to build
environment:
PG_USER: $PG_USER
PG_PASSWORD: $PG_PASSWORD
PG_HOST: postgres #note the name of the postgres service in compose yaml
PG_DATABASE: my-service-db
OTHER_ENV_VAR1: $OTHER_ENV_VAR1
OTHER_ENV_VAR2: $OTHER_ENV_VAR2
depends_on:
postgres
volumes:
pgdata:
Now you can use docker compose up
command to run the services.现在您可以使用
docker compose up
命令来运行服务。 If you wish to build the my-service
container each time you can pass an optional argument --build
like this: docker compose up --build
.如果您希望每次都构建
my-service
容器,您可以像这样传递一个可选参数--build
: docker compose up --build
。
In order to pass the environment variables from the CLI, there's only one way which is by the use of .env
file.为了从 CLI 传递环境变量,只有一种方法是使用
.env
文件。 In your case of docker-compose.yml
the app.env
would look like:在您的
app.env
的情况下, docker-compose.yml
看起来像:
PG_USER=user
PG_PASSWORD=pass
#PG_DATABASE=dbname #not required as you're using 'my-service-db' as db name in compose file
#PG_HOST=dbhost #not required as service name of postgres in compose file is being used as db host
OTHER_ENV_VAR1=someval
OTHER_ENV_VAR2=anotherval
Passing this app.env
file using docker compose CLI command would look like this:使用 docker compose CLI 命令传递此
app.env
文件如下所示:
docker compose --env-file app.env up --build
PS: If you're building your my-service
each time just for the code changes to reflect in the docker container, you could make use of bind mount instead. PS:如果您每次构建
my-service
只是为了代码更改以反映在 docker 容器中,您可以改用绑定挂载。 The updated docker-compose.yml
in that case would look like this:在这种情况下,更新后的
docker-compose.yml
如下所示:
version: '3.7'
services:
postgres:
container_name: postgres
image: postgres:14.3
environment:
POSTGRES_PASSWORD: $PG_PASSWORD
POSTGRES_USER: $PG_USER
POSTGRES_DB: my-service-db
PG_DATA: /var/lib/postgresql2/data
ports:
- 5432:5432
volumes:
- pgdata:/var/lib/postgresql2/data
my-service:
container_name: my-service
build: .
volumes:
- .:/usr/src/app #note the use of volumes here
environment:
PG_USER: $PG_USER
PG_PASSWORD: $PG_PASSWORD
PG_HOST: postgres
PG_DATABASE: my-service-db
OTHER_ENV_VAR1: $OTHER_ENV_VAR1
OTHER_ENV_VAR2: $OTHER_ENV_VAR2
depends_on:
postgres
volumes:
pgdata:
This way, you don't need to run docker compose build each time, making a code change in the source folder would get reflected in the docker container.这样,您不需要每次都运行 docker compose build,在源文件夹中进行代码更改将反映在 docker 容器中。
You just need to add path of your docker file in to build parameter in docker-compose.yaml file and all the environment variables in environment您只需在 docker-compose.yaml 文件和环境中的所有环境变量中添加 docker 文件的路径即可构建参数
version: '3.7'
services:
postgres:
container_name: postgres
image: postgres:14.3
environment:
POSTGRES_PASSWORD: ${psql.password}
POSTGRES_USER: ${psql.user}
POSTGRES_DB: my-service-db
PG_DATA: /var/lib/postgresql2/data
ports:
- 5432:5432
volumes:
- pgdata:/var/lib/postgresql2/data
my-service:
container_name: my-service
image: path_to_your_dockerfile
environment:
your_environment_variables_here
container as env vars?
volumes:
pgdata
I am guessing that you have folder structure like this我猜你有这样的文件夹结构
project_folder/
docker-compose.yaml
my-service/
Dockerfile
.env
package.json
package-lock.json
src/
<lots of other stuff>
and your.env contains following并且 your.env 包含以下内容
API_PORT=8082
Environment_var1=Environment_var1_value
Environment_var2=Environment_var2_value
So in your case your docker-compose file should look like this因此,在您的情况下,您的 docker-compose 文件应如下所示
version: '3.7'
services:
postgres:
container_name: postgres
image: postgres:14.3
environment:
POSTGRES_PASSWORD: ${psql.password}
POSTGRES_USER: ${psql.user}
POSTGRES_DB: my-service-db
PG_DATA: /var/lib/postgresql2/data
ports:
- 5432:5432
volumes:
- pgdata:/var/lib/postgresql2/data
my-service:
container_name: my-service
image: ./my-service/
environment:
- API_PORT=8082
- Environment_var1=Environment_var1_value
- Environment_var2=Environment_var2_value
volumes:
pgdata
FYI: for this docker configuration your database connection host should be postgres (as per service name) not localhost and your仅供参考:对于这个 docker 配置,您的数据库连接主机应该是postgres (根据服务名称)而不是localhost和您的
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.