图像是不可变的
Dockerfile定义了映像的构建过程。生成后,图像将是不可变的(无法更改)。运行时变量不会被添加到该不可变映像中。因此,Dockerfile是解决此问题的错误位置。
使用入口点脚本
您可能想做的是
ENTRYPOINT使用自己的脚本覆盖默认值,并让该脚本对环境变量执行某些操作。由于入口点脚本将在运行时(容器启动时)执行,因此这是收集环境变量并对其进行处理的正确时间。
首先,您需要调整Dockerfile以了解入口点脚本。尽管Dockerfile没有直接参与处理环境变量,但它仍需要了解此脚本,因为该脚本将被烘焙到您的映像中。
Dockerfile:
COPY entrypoint.sh /entrypoint.shRUN chmod +x /entrypoint.shENTRYPOINT ["/entrypoint.sh"]CMD ["npm", "start"]
现在,编写一个入口点脚本,该脚本执行命令运行 之前 所需的一切设置,最后运行
exec命令本身。
entrypoint.sh:
#!/bin/sh# Where $ENVSUBS is whatever command you are looking to run$ENVSUBS < fil1 > file2npm install# This will exec the CMD from your Dockerfile, i.e. "npm start"exec "$@"
在这里,我已经将包括在内
npm install,因为您在评论中对此有所询问。我将注意到,这将
npm install在每次运行时运行
。如果合适的话,很好,但是我想指出一下,它每次都会运行,这会增加启动时间的延迟。
现在重建图像,因此入口点脚本是其中的一部分。
在运行时使用环境变量
入口点脚本知道如何使用环境变量,但是您仍然必须告诉Docker在运行时导入该变量。您可以使用该
-e标志
docker run来这样做。
docker run -e "ENVSUBS=$ENVSUBS" <image_name>
在这里,告诉Docker定义一个环境变量
ENVSUBS,分配给它的值是
$ENVSUBS当前shell环境中的值。
入口点脚本如何工作
我将对此进行详细说明,因为在评论中,您似乎对此不太了解。
Docker启动容器时,它将在容器内执行一个(只有一个)命令。该命令变得PID
1,就像
init或
systemd典型的Linux系统上。该过程负责运行容器需要具有的任何其他过程。
默认情况下
ENTRYPOINT为
/bin/sh -c。您可以在Dockerfile或docker-
compose.yml中或使用docker命令覆盖它。
启动容器后,Docker运行entrypoint命令,并将命令(
CMD)作为参数列表传递给它。之前,我们将定义
ENTRYPOINT为
/entrypoint.sh。这意味着在您的情况下,这就是Docker启动时将在容器中执行的操作:
/entrypoint.sh npm start
因为
["npm", "start"]已被定义为命令,所以这就是作为参数列表传递到入口点脚本的内容。
因为我们使用
-e标志定义了环境变量,所以此入口点脚本(及其子项)将有权访问该环境变量。
在入口点脚本的末尾,我们运行
exec "$@"。因为
$@扩展到传递给脚本的参数列表,所以它将运行
exec npm start
并且由于
exec将其参数作为命令运行,因此在完成后用其自身 替换 当前进程
npm start将成为容器中的PID 1。
为什么不能使用多个CMD
在评论中,您询问是否可以定义多个
CMD条目来运行多个事物。
您只能定义一个
ENTRYPOINT和一个
CMD。这些在构建过程中根本不使用。与
RUN和不同
COPY,它们在构建期间不会执行。一旦构建,它们就会作为元数据项添加到图像中。
直到稍后,当图像作为容器运行时,这些元数据字段才被读取并用于启动容器。
如前所述,入口点是实际运行的入口点,并将其
CMD作为参数列表传递。它们分开的原因部分是历史原因。在Docker的早期版本中,它
CMD是唯一可用的选项,
ENTRYPOINT并被固定为
/bin/sh-c。但是由于这种情况,Docker最终
ENTRYPOINT被用户定义。



