我的 nodejs 项目有以下文件
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 容器
但我没有看到替代品。当 env 变量可用时,会执行 Run c 命令吗?
原文由 user_mda 发布,翻译遵循 CC BY-SA 4.0 许可协议
图像是不可变的
Dockerfile 定义了镜像的构建过程。一旦构建,图像是不可变的(无法更改)。运行时变量不会被烘焙到这个不可变的图像中。所以 Dockerfile 是解决这个问题的错误地方。
使用入口点脚本
您可能想要做的是用您自己的脚本覆盖默认值
ENTRYPOINT
,并让该脚本对环境变量执行一些操作。由于入口点脚本将在运行时(容器启动时)执行,因此这是收集环境变量并对其进行处理的正确时间。首先,您需要调整 Dockerfile 以了解入口点脚本。虽然 Dockerfile 不直接参与处理环境变量,但它仍然需要了解此脚本,因为该脚本将被烘焙到您的映像中。
Dockerfile:
现在,编写一个入口点脚本,在命令运行 之前 执行所需的任何设置,最后,
exec
命令本身。入口点.sh:
在这里,我包括
npm install
,因为您在评论中询问了这个问题。我会注意到这将 在每次运行 时运行npm install
。如果合适的话,很好,但我想指出它每次都会运行,这会给你的启动时间增加一些延迟。现在重建您的图像,因此入口点脚本是其中的一部分。
在运行时使用环境变量
入口点脚本知道如何使用环境变量,但您仍然需要告诉 Docker 在运行时导入该变量。您可以使用
-e
标志到docker run
这样做。在这里,Docker 被告知定义一个环境变量
ENVSUBS
,它分配的值是来自当前 shell 环境的$ENVSUBS
的值。入口点脚本如何工作
我将对此进行详细说明,因为在评论中,您似乎对它如何组合在一起有些模糊。
当 Docker 启动一个容器时,它会在容器内执行一个(并且只有一个)命令。该命令变为 PID 1,就像典型 Linux 系统上的
init
或systemd
一样。这个进程负责运行容器需要的任何其他进程。默认情况下,
ENTRYPOINT
是/bin/sh -c
。您可以在 Dockerfile 或 docker-compose.yml 中覆盖它,或者使用 docker 命令。当容器启动时,Docker 运行入口点命令,并将命令(
CMD
)作为参数列表传递给它。之前,我们将自己的ENTRYPOINT
定义为/entrypoint.sh
。这意味着在您的情况下,这是 Docker 启动时将在容器中执行的内容:因为
["npm", "start"]
被定义为命令,所以它作为参数列表传递给入口点脚本。因为我们使用
-e
标志定义了一个环境变量,所以这个入口点脚本(及其子脚本)将可以访问该环境变量。在入口点脚本的最后,我们运行
exec "$@"
。因为$@
扩展为传递给脚本的参数列表,这将运行因为
exec
将其参数作为命令运行,用它自己 替换 当前进程,当你完成时,npm start
在你的容器中变成 PID 1。为什么你不能使用多个 CMD
在评论中,您询问是否可以定义多个
CMD
条目来运行多个事物。您只能定义一个
ENTRYPOINT
和一个CMD
。在构建过程中根本不使用这些。与RUN
和COPY
不同,它们不会在构建期间执行。一旦构建,它们就会作为元数据项添加到图像中。只有稍后,当图像作为容器运行时,这些元数据字段才会被读取,并用于启动容器。
如前所述,入口点是真正运行的,它通过
CMD
作为参数列表传递。它们分开的部分原因是历史原因。在 Docker 的早期版本中,CMD
是唯一可用的选项,而ENTRYPOINT
被固定为/bin/sh -c
。但是由于这种情况,Docker最终允许用户定义ENTRYPOINT
。