PID 1 必须是主进程以正确处理信号和回收僵尸进程;应使用 ENTRYPOINT exec 形式(如 ["./myapp"])而非 shell 形式,避免 /bin/sh 占据 PID 1 导致优雅终止失败。

在 Docker 中,让容器的 PID 1 进程正确承担信号转发和僵尸进程回收职责,是稳定运行的关键。使用 ENTRYPOINT(尤其是 exec 形式)而非仅靠 CMD,是确保应用进程成为 PID 1 的最可靠方式。
为什么 PID 1 必须是你的主进程?
Docker 容器生命周期由 PID 1 进程决定:它接收 SIGTERM 等信号、负责转发给子进程,并必须主动调用 wait() 回收僵尸进程。若 PID 1 是 shell(如 /bin/sh -c),它通常不处理信号也不回收僵尸,导致容器无法优雅停止、子进程残留、资源泄漏。
ENTRYPOINT exec 形式才是安全选择
务必使用 exec 格式(JSON 数组),避免 shell 格式:
-
✅ 正确(exec 形式):
ENTRYPOINT ["./myapp", "--port=8080"]→./myapp直接成为 PID 1 -
❌ 危险(shell 形式):
ENTRYPOINT ./myapp --port=8080→ 实际执行/bin/sh -c './myapp --port=8080',/bin/sh成为 PID 1
配合 CMD 实现灵活参数传递
推荐组合写法:ENTRYPOINT 固定主程序,CMD 提供默认参数,便于运行时覆盖:
ENTRYPOINT ["./server"] CMD ["--config=config.yaml", "--log-level=info"]
这样既保证 ./server 总是 PID 1,又支持外部传参:docker run myimg --config=prod.yaml 会替换整个 CMD,最终执行 ./server --config=prod.yaml。
需要 Shell 功能?请显式启用并自行管理
若必须用 shell(如环境变量展开、管道、条件判断),不要依赖隐式 shell 启动。应显式使用 sh -c 并确保它能正确处理信号和僵尸:
- 用
exec替换自身:例如ENTRYPOINT ["sh", "-c", "exec \"$@\"", "_", "./myapp", "$@"] - 或使用轻量级 init 工具(如
tini)作为真正 PID 1,再由它启动你的应用
直接写 ENTRYPOINT /bin/sh -c './myapp $@' 仍会让 shell 占据 PID 1,且不处理信号——这是常见陷阱。

















