如何通过dockerfile在ENTRYPOINT之前执行shell命令

use*_*mda 13 javascript environment-variables node.js docker

我的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" ]
Run Code Online (Sandbox Code Playgroud)

我使用-e标志运行docker容器,提供环境变量

但我没有看到替代品.当env变量可用时,Run ccommand是否会被执行?

Dan*_*owe 40

图像是不可变的

Dockerfile定义图像的构建过程.构建后,图像是不可变的(无法更改).运行时变量不会被烘焙到这个不可变图像中.所以Dockerfile是解决这个问题的错误地方.

使用入口点脚本

您可能想要做的是ENTRYPOINT使用您自己的脚本覆盖默认值,并让该脚本对环境变量执行某些操作.由于入口点脚本将在运行时执行(当容器启动时),这是收集环境变量并对它们执行某些操作的正确时间.

首先,您需要调整Dockerfile以了解入口点脚本.虽然Dockerfile没有直接参与处理环境变量,但它仍然需要知道这个脚本,因为脚本将被烘焙到您的图像中.

Dockerfile:

COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh

ENTRYPOINT ["/entrypoint.sh"]
CMD ["npm", "start"]
Run Code Online (Sandbox Code Playgroud)

现在,编写一个入口点脚本,运行命令之前执行所需的任何设置,最后编写exec命令本身.

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 "$@"
Run Code Online (Sandbox Code Playgroud)

在这里,我已经包括在内npm install,因为您在评论中询问了这一点.我会注意到这将npm install 在每次运行时运行.如果这是合适的,那很好,但我想指出它每次都会运行,这会给你的启动时间增加一些延迟.

现在重建您的图像,因此入口点脚本是它的一部分.

在运行时使用环境变量

入口点脚本知道如何使用环境变量,但您仍然必须告诉Docker在运行时导入变量.您可以使用该-e标志docker run来执行此操作.

docker run -e "ENVSUBS=$ENVSUBS" <image_name>
Run Code Online (Sandbox Code Playgroud)

这里,Docker被告知定义一个环境变量ENVSUBS,它被赋值的值是$ENVSUBS当前shell环境的值.

入口点脚本如何工作

我会详细说明一下,因为在评论中,看起来你对它如何组合起来有点模糊.

当Docker启动容器时,它会在容器内执行一个(也是唯一一个)命令.该命令变得PID 1,就像initsystemd典型的Linux系统上.此过程负责运行容器需要的任何其他进程.

默认情况下,ENTRYPOINT/bin/sh -c.您可以在Dockerfile或docker-compose.yml中或使用docker命令覆盖它.

启动容器时,Docker会运行entrypoint命令,并将command(CMD)作为参数列表传递给它.早些时候,我们将自己定义ENTRYPOINT/entrypoint.sh.这意味着在您的情况下,这是Docker在启动时将在容器中执行的内容:

/entrypoint.sh npm start
Run Code Online (Sandbox Code Playgroud)

因为["npm", "start"]被定义为命令,所以它作为参数列表传递给入口点脚本.

因为我们使用-e标志定义了一个环境变量,所以此入口点脚本(及其子项)将可以访问该环境变量.

在入口点脚本的末尾,我们运行exec "$@".因为$@扩展到传递给脚本的参数列表,所以这将运行

exec npm start
Run Code Online (Sandbox Code Playgroud)

并且因为exec将其参数作为命令运行,自己替换当前进程,完成后,npm start在容器中变为PID 1.

为什么你不能使用多个CMD

在评论中,您询问是否可以定义多个CMD条目来运行多个内容.

您只能定义一个ENTRYPOINT和一个CMD.在构建过程中根本不使用它们.与RUN和不同COPY,它们不会在构建期间执行.它们在构建后作为元数据项添加到图像中.

只有在稍后,当图像作为容器运行时,才会读取这些元数据字段,并用于启动容器.

如前所述,入口点是真正运行的,它CMD作为参数列表传递.他们分开的原因部分是历史性的.在早期版本的Docker中,它CMD是唯一可用的选项,并且ENTRYPOINT已被修复为/bin/sh -c.但是由于像这样的情况,Docker最终允许ENTRYPOINT由用户定义.