推荐使用 JSON 参数
目录
输出
JSON arguments recommended for ENTRYPOINT/CMD to prevent unintended behavior related to OS signals
描述
ENTRYPOINT
和 CMD
指令都支持两种不同的参数语法
- Shell 形式:
CMD my-cmd start
- Exec 形式:
CMD ["my-cmd", "start"]
使用 Shell 形式时,可执行文件作为 shell 的子进程运行,不会传递信号。这意味着在容器中运行的程序无法检测到 SIGTERM
和 SIGKILL
等操作系统信号并正确响应。
示例
❌ 错误:ENTRYPOINT
命令无法接收操作系统信号。
FROM alpine
ENTRYPOINT my-program start
# entrypoint becomes: /bin/sh -c my-program start
为了确保可执行文件可以接收操作系统信号,请对 CMD
和 ENTRYPOINT
使用 exec 形式,这允许您在容器中将可执行文件作为主进程 (PID 1
) 运行,避免 shell 父进程。
✅ 正确:ENTRYPOINT
接收操作系统信号。
FROM alpine
ENTRYPOINT ["my-program", "start"]
# entrypoint becomes: my-program start
请注意,将程序作为 PID 1 运行意味着程序现在具有与 Linux 中 PID 1 相关的特殊职责和行为,例如收割子进程。
解决方法
您可能仍然有一些情况下希望在 shell 下运行容器。使用 exec 形式时,Shell 功能(例如变量扩展、管道 (|
) 和命令链 (&&
、||
、;
))不可用。要使用这些功能,您需要使用 shell 形式。
以下是一些您可以实现此目的的方法。请注意,这仍然意味着可执行文件作为 shell 的子进程运行。
创建包装脚本
您可以创建一个包装启动命令的入口点脚本,并使用 JSON 格式的 ENTRYPOINT
命令执行该脚本。
✅ 正确:ENTRYPOINT
使用 JSON 格式。
FROM alpine
RUN apk add bash
COPY --chmod=755 <<EOT /entrypoint.sh
#!/usr/bin/env bash
set -e
my-background-process &
my-program start
EOT
ENTRYPOINT ["/entrypoint.sh"]
显式指定 shell
您可以使用 SHELL
Dockerfile 指令显式指定要使用的 shell。这将抑制警告,因为设置 SHELL
指令表示使用 shell 形式是一个有意识的决定。
✅ 正确:显式定义了 shell。
FROM alpine
RUN apk add bash
SHELL ["/bin/bash", "-c"]
ENTRYPOINT echo "hello world"