我的 nodejs 项目有以下文件
I have the following file for my nodejs project
FROM node:boron
# Create app directory
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
# Install app dependencies
COPY package.json /usr/src/app/
RUN npm install
# Bundle app source
COPY . /usr/src/app
# Replace with env variable
RUN envsubs < fil1 > file2
EXPOSE 8080
CMD [ "npm", "start" ]
我使用 -e 标志运行 docker 容器,提供环境变量
I run the docker container with the -e flag providing the environment variable
但我没有看到替代品.当 env 变量可用时会执行 Run c 命令吗?
But I do not see the replacement. Will the Run ccommand be excuted when the env variable is available?
Dockerfile 定义了镜像的构建过程.一旦构建,图像是不可变的(无法更改).运行时变量不会被烘焙到这个不可变的图像中.所以 Dockerfile 是解决这个问题的错误地方.
Dockerfile defines the build process for an image. Once built, the image is immutable (cannot be changed). Runtime variables are not something that would be baked into this immutable image. So Dockerfile is the wrong place to address this.
您可能想要做的是用您自己的脚本覆盖默认的 ENTRYPOINT
,并让该脚本对环境变量执行一些操作.由于入口点脚本将在运行时(容器启动时)执行,因此这是收集环境变量并对其进行处理的正确时间.
What you probably want to to do is override the default ENTRYPOINT
with your own script, and have that script do something with environment variables. Since the entrypoint script would execute at runtime (when the container starts), this is the correct time to gather environment variables and do something with them.
首先,您需要调整 Dockerfile 以了解入口点脚本.虽然 Dockerfile 不直接参与处理环境变量,但它仍然需要了解该脚本,因为该脚本将被烘焙到您的镜像中.
First, you need to adjust your Dockerfile to know about an entrypoint script. While Dockerfile is not directly involved in handling the environment variable, it still needs to know about this script, because the script will be baked into your image.
Dockerfile:
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
CMD ["npm", "start"]
现在,编写一个入口点脚本,它会在命令运行之前完成所需的任何设置,最后,exec
命令本身.
Now, write an entrypoint script which does whatever setup is needed before the command is run, and at the end, exec
the command itself.
entrypoint.sh:
#!/bin/sh
# Where $ENVSUBS is whatever command you are looking to run
$ENVSUBS < fil1 > file2
npm install
# This will exec the CMD from your Dockerfile, i.e. "npm start"
exec "$@"
在这里,我已包含 npm install
,因为您在评论中询问了这个问题.我会注意到这将在每次运行时运行 npm install
.如果合适的话,很好,但我想指出它每次都会运行,这会给您的启动时间增加一些延迟.
Here, I have included npm install
, since you asked about this in the comments. I will note that this will run npm install
on every run. If that's appropriate, fine, but I wanted to point out it will run every time, which will add some latency to your startup time.
现在重新构建您的映像,因此入口点脚本是其中的一部分.
Now rebuild your image, so the entrypoint script is a part of it.
入口点脚本知道如何使用环境变量,但您仍然需要告诉 Docker 在运行时导入该变量.您可以使用 -e
标志到 docker run
来执行此操作.
The entrypoint script knows how to use the environment variable, but you still have to tell Docker to import the variable at runtime. You can use the -e
flag to docker run
to do so.
docker run -e "ENVSUBS=$ENVSUBS" <image_name>
这里告诉Docker定义一个环境变量ENVSUBS
,赋值给当前shell环境$ENVSUBS
的值.
Here, Docker is told to define an environment variable ENVSUBS
, and the value it is assigned is the value of $ENVSUBS
from the current shell environment.
我将对此进行详细说明,因为在评论中,您似乎对这如何组合在一起有些模糊.
I'll elaborate a bit on this, because in the comments, it seemed you were a little foggy on how this fits together.
当 Docker 启动一个容器时,它会在容器内执行一个(并且只有一个)命令.该命令变为 PID 1,就像典型 Linux 系统上的 init
或 systemd
一样.该进程负责运行容器所需的任何其他进程.
When Docker starts a container, it executes one (and only one) command inside the container. This command becomes PID 1, just like init
or systemd
on a typical Linux system. This process is responsible for running any other processes the container needs to have.
默认情况下,ENTRYPOINT
是 /bin/sh -c
.您可以在 Dockerfile 或 docker-compose.yml 中覆盖它,或者使用 docker 命令.
By default, the ENTRYPOINT
is /bin/sh -c
. You can override it in Dockerfile, or docker-compose.yml, or using the docker command.
当容器启动时,Docker 运行入口点命令,并将命令 (CMD
) 作为参数列表传递给它.之前,我们将自己的 ENTRYPOINT
定义为 /entrypoint.sh
.这意味着在您的情况下,这是 Docker 启动时将在容器中执行的内容:
When a container is started, Docker runs the entrypoint command, and passes the command (CMD
) to it as an argument list. Earlier, we defined our own ENTRYPOINT
as /entrypoint.sh
. That means that in your case, this is what Docker will execute in the container when it starts:
/entrypoint.sh npm start
因为 ["npm", "start"]
被定义为命令,这就是作为参数列表传递给入口点脚本的内容.
Because ["npm", "start"]
was defined as the command, that is what gets passed as an argument list to the entrypoint script.
因为我们使用 -e
标志定义了一个环境变量,所以这个入口点脚本(及其子脚本)将可以访问该环境变量.
Because we defined an environment variable using the -e
flag, this entrypoint script (and its children) will have access to that environment variable.
在入口点脚本的最后,我们运行 exec "$@"
.因为 $@
扩展为传递给脚本的参数列表,这将运行
At the end of the entrypoint script, we run exec "$@"
. Because $@
expands to the argument list passed to the script, this will run
exec npm start
并且由于 exec
将其参数作为命令运行,用自身替换当前进程,当你完成时,npm start
变为 PID1 在您的容器中.
And because exec
runs its arguments as a command, replacing the current process with itself, when you are done, npm start
becomes PID 1 in your container.
在评论中,您询问是否可以定义多个 CMD
条目来运行多个操作.
In the comments, you asked whether you can define multiple CMD
entries to run multiple things.
您只能定义一个 ENTRYPOINT
和一个 CMD
.在构建过程中根本不使用这些.与 RUN
和 COPY
不同,它们不会在构建期间执行.它们在构建后作为元数据项添加到图像中.
You can only have one ENTRYPOINT
and one CMD
defined. These are not used at all during the build process. Unlike RUN
and COPY
, they are not executed during the build. They are added as metadata items to the image once it is built.
只有稍后,当图像作为容器运行时,这些元数据字段才会被读取,并用于启动容器.
It is only later, when the image is run as a container, that these metadata fields are read, and used to start the container.
如前所述,入口点是真正运行的,它作为参数列表传递给 CMD
.它们分开的部分原因是历史原因.在 Docker 的早期版本中,CMD
是唯一可用的选项,ENTRYPOINT
被固定为 /bin/sh -c
.但是由于这种情况,Docker 最终允许 ENTRYPOINT
由用户定义.
As mentioned earlier, the entrypoint is what is really run, and it is passed the CMD
as an argument list. The reason they are separate is partly historical. In early versions of Docker, CMD
was the only available option, and ENTRYPOINT
was fixed as being /bin/sh -c
. But due to situations like this one, Docker eventually allowed ENTRYPOINT
to be defined by the user.
这篇关于如何通过 dockerfile 在 ENTRYPOINT 之前执行 shell 命令的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持跟版网!