当前位置: 首页 > 面试题库 >

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

卞经业
2023-03-14
问题内容

我的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 ccommand?


问题答案:

图像是不可变的

Dockerfile定义了映像的构建过程。生成后,图像将是不可变的(无法更改)。运行时变量不会被添加到该不可变映像中。因此,Dockerfile是解决此问题的错误位置。

使用入口点脚本

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

首先,您需要调整Dockerfile以了解入口点脚本。尽管Dockerfile没有直接参与处理环境变量,但它仍需要了解此脚本,因为该脚本将被烘焙到您的映像中。

Dockerfile:

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

ENTRYPOINT ["/entrypoint.sh"]
CMD ["npm", "start"]

现在,编写一个入口点脚本,该脚本执行命令运行 之前 所需的一切设置,最后运行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 "$@"

在这里,我已经将包括在内npm install,因为您在评论中对此有所询问。我将注意到,这将npm install 在每次运行时运行
。如果合适的话,很好,但是我想指出一下,它每次都会运行,这会增加启动时间的延迟。

现在重建图像,因此入口点脚本是其中的一部分。

在运行时使用环境变量

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

docker run -e "ENVSUBS=$ENVSUBS" <image_name>

在这里,告诉Docker定义一个环境变量ENVSUBS,分配给它的值是$ENVSUBS当前shell环境中的值。

入口点脚本如何工作

我将对此进行详细说明,因为在评论中,您似乎对此不太了解。

Docker启动容器时,它将在容器内执行一个(只有一个)命令。该命令变得PID
1,就像initsystemd典型的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被用户定义。



 类似资料:
  • 我试图在mongo shell中使用脚本js。我正在读一本关于这个主题的书。我尝试了几个选项,例如在我的计算机的特定位置打开cmd。我在这里发现了一个问题“如何通过shell脚本执行mongo命令?”,但对我来说似乎什么都不管用。我的mongo工作正常,我可以输入mongo,只需在cmd中键入mongo。但是,它找不到js文件。我正在使用Windows;有人知道引擎盖下会发生什么吗? 例如:

  • 2. Shell如何执行命令 2.1. 执行交互式命令 用户在命令行输入命令后,一般情况下Shell会fork并exec该命令,但是Shell的内建命令例外,执行内建命令相当于调用Shell进程中的一个函数,并不创建新的进程。以前学过的cd、alias、umask、exit等命令即是内建命令,凡是用which命令查不到程序文件所在位置的命令都是内建命令,内建命令没有单独的man手册,要在man手册

  • 问题内容: 我正在尝试通过Java执行命令行参数。例如: 上面的命令打开命令行,但是不执行或。有任何想法吗?我正在运行Windows XP,JRE6。 (我已经对问题进行了更详细的修订。以下答案很有用,但不能回答我的问题。) 问题答案: 你发布的代码使用自己的命令启动三个不同的过程。要打开命令提示符然后运行命令,请尝试以下操作(请勿自己尝试):

  • 问题内容: 我试图找出Linux上是否存在程序,并且找到了本文。我尝试从go程序中执行此操作,但它始终提示我无法在$ PATH中找到“命令”,这是预料之中的,因为它是Linux中的内置命令,而不是二进制文件。所以我的问题是如何从go程序中执行linux的内置命令? 错误:执行:“命令”:在$ PATH中找不到可执行文件 问题答案: 就像那篇文章说的那样,“命令”是内置的shell。您可以通过go本

  • 问题内容: 问题在标题中。以感叹号开头的shell命令(shell脚本的一部分)的目的是什么?具体示例: 在foo.sh中: 我知道没有空格,感叹号用于替换历史记录,并且根据手册页可用于评估“ expr为false时为True ”。但是在示例上下文中,这对我来说没有意义。 问题答案: TL; DR:这只是绕过您使用它的特定行中的标志。 在hek2mgl的正确和有用的答案中添加add 。 你有: B

  • 我有一个案例,我想问我可以解决与Spring壳。我有一个Main.jar应用程序,它有几个部署在Wildly服务器上的Spring任务。在我的例子中,我不能停止或重新部署main.jar,因为必须不停地提供服务。