我可以添加额外的 docker 入口点脚本吗?

我可以添加额外的 docker 入口点脚本吗?

如何入口点Docker 容器具体是如何工作的?我有一个包含 ENTRYPOINT 脚本的基本 docker 镜像,我正在其上构建另一个包含另一个 ENTRYPOINT 脚本的镜像。

当我运行图像时,两个 ENTRYPOINT 脚本都会运行吗?

答案1

如果父图像包含一行:

ENTRYPOINT ["/entrypoint-parent.sh"]

并且您想要将以下内容添加到您的子图像中:

ENTRYPOINT ["/entrypoint-child.sh"]

ENTRYPOINT然后,结果图像中的 值将被替换为/entrypoint-child.sh,换句话说, 只有一个值ENTRYPOINT。Docker 只会调用一个进程来启动您的容器,尽管该进程可以生成子进程。有几种扩展入口点的技术。

选项 A:调用您的入口点,然后在最后运行父入口点,例如/entrypoint-child.sh看起来像:

#!/bin/sh

echo Running child entrypoint initialization steps here
# ...

exec /entrypoint-parent.sh "$@"

exec部分很重要,它用 shell 或进程替换当前 shell /entrypoint-parent.sh,从而消除信号处理问题。结果是您在子入口点中运行初始化的第一部分,然后委托给原始父入口点。这确实要求您跟踪父入口点的名称,这可能会在基础映像的不同版本之间发生变化。

选项 B:在后台运行父入口点。这不是理想的做法,因为除非您采取一些额外步骤,否则您将不再对父进程进行错误处理。最简单的情况下,这在您的 中看起来像以下内容/entrypoint-child.sh

#!/bin/sh

# other initialization steps

/entrypoint-parent.sh "$@" &

# potentially wait for parent to be running by polling

# run something new in the foreground, that may depend on parent processes
exec /child-process.bin

请注意,"$@"我一直使用的符号是将的值CMD作为参数传递到父入口点。

选项 C:切换到类似主管。我不太喜欢这个,因为它意味着在你的容器内运行多个守护进程,而你通常最好将其分成多个容器。当单个子进程不断失败时,你需要决定正确的响应是什么。

选项 D:与选项 A 和 B 类似,我经常创建一个入口点脚本目录,这些脚本可以在镜像构建的不同级别进行扩展。入口点本身没有变化,我只是将新文件添加到一个目录中,该目录会根据文件名按顺序调用。在我的场景中,这些脚本都在前台运行,我CMD在最后执行。你可以在我的基础镜像仓库,特别是包含以下部分的entrypoint.d目录和脚本:bin/entrypointd.sh

# ...

for ep in /etc/entrypoint.d/*; do
  ext="${ep##*.}"
  if [ "${ext}" = "env" -a -f "${ep}" ]; then
    # source files ending in ".env"
    echo "Sourcing: ${ep}"
    set -a && . "${ep}" && set +a
  elif [ "${ext}" = "sh" -a -x "${ep}" ]; then
    # run scripts ending in ".sh"
    echo "Running: ${ep}"
    "${ep}"
  fi
done

# ...

# run command with exec to pass control
echo "Running CMD: $@"
exec "$@"

答案2

不,只有一个入口点,因此您的脚本将替换父映像中定义的入口点。但您的脚本可以调用(*)或借用父映像使用的脚本中的代码。

(*) 当然,只有当这是最后一步时才会发生这种情况。请注意,在设计良好的容器中,您应该将exec脚本的最后一个命令设置为容器的主进程。如果您未能这样做,则发送的用于正常终止容器的信号 (SIGINT/SIGTERM) 将发送到您的脚本(不会对它们执行任何操作),而不是发送到容器“真实”进程。这将 1) 使容器缓慢停止,2) 将阻止正常退出:docker stop将发送 SIGTERM,如果容器未及时终止(10 秒),它将使用 SIGKILL。如果您的exec脚本本身exec就是一个命令,那么该命令将成为主进程,一切都会很好。

答案3

正如前面的答案所提到的:

  1. 您的入口点规范将取代之前的入口点。
  2. 如果您编写了一个入口点并调用原始入口点,则您应该使用“exec”来确保原始入口点作为单独的进程运行,而不是作为入口点内的函数运行。
  3. 将原始参数传递给原始入口点:“#@”

更好的解决方案可能是,如果镜像规范托管在 git 平台上,则分叉 git 存储库,根据需要修改入口点,然后从这个新分叉中使用 docker build 中的从 git 构建功能构建镜像。

相关内容