Skip to content

Dockerfile 中的 ENTRYPOINT 和 CMD 详解

在 Dockerfile 中,ENTRYPOINTCMD 都用于指定容器启动时运行的命令,但它们的角色和可覆盖性不同。理解两者的区别有助于正确配置容器的默认行为。


1. 基本定义

  • ENTRYPOINT:定义容器启动时必须执行的主命令。它不会被 docker run 后面附加的命令覆盖,除非显式使用 --entrypoint 参数。
  • CMD:提供默认参数默认命令。如果 docker run 后面提供了命令/参数,则会覆盖 CMD

2. 两种语法格式

  • Exec 形式(推荐):
    ["executable", "param1", "param2"]
    直接调用可执行文件,不启动 shell,信号处理正确(如 docker stop 能发送 SIGTERM)。

  • Shell 形式
    command param1 param2
    实际会调用 /bin/sh -c "command param1 param2"进程 PID 1 是 shell,可能无法正确传递信号,且不支持 CMDdocker run 参数中的变量替换。


3. 单独使用时的行为

只有 CMD

dockerfile
CMD ["nginx", "-g", "daemon off;"]

容器启动默认执行 nginx -g "daemon off;"
运行 docker run nginx -h 会执行 nginx -h(覆盖了整个 CMD)。

只有 ENTRYPOINT

dockerfile
ENTRYPOINT ["nginx", "-g", "daemon off;"]

容器启动固定执行 nginx -g "daemon off;"docker run 后面跟的任何参数都会被附加到 ENTRYPOINT 的命令后面(但如果 ENTRYPOINT 未使用 exec 形式,参数可能被忽略)。


4. 组合使用(最佳实践)

ENTRYPOINT 定义固定可执行文件,CMD 提供默认参数。

dockerfile
ENTRYPOINT ["nginx"]
CMD ["-g", "daemon off;"]
  • 默认启动:nginx -g "daemon off;"
  • 运行 docker run nginx -t:执行 nginx -t(替换了 CMD 参数)
  • 运行 docker run --entrypoint /bin/bash nginx:进入 shell,完全覆盖入口点。

这种方式既保留了主程序的固定性,又允许用户方便地修改参数。


5. 覆盖规则总结

情况实际执行命令
只有 CMDCMD 的内容,可被 docker run <image> ... 完全覆盖
只有 ENTRYPOINTENTRYPOINT 的内容,docker run 后的参数作为附加参数(exec 形式下)
两者都有ENTRYPOINT 固定,CMD 作为默认参数,可被 docker run 后的参数覆盖
docker run --entrypoint覆盖 ENTRYPOINT,此时 CMD 变成该新入口点的默认参数

6. 实用示例

示例 1:通用 CLI 工具容器

dockerfile
FROM alpine
ENTRYPOINT ["ping"]
CMD ["localhost"]
  • docker run mypingping localhost
  • docker run myping 8.8.8.8ping 8.8.8.8

示例 2:需要固定脚本的容器

dockerfile
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]

CMD 可用来向该脚本传递默认参数。

示例 3:避免 shell 形式的陷阱

dockerfile
# 不推荐:shell 形式
ENTRYPOINT nginx -g "daemon off;"
# 推荐:exec 形式
ENTRYPOINT ["nginx", "-g", "daemon off;"]

7. 常见误区

  • 一个 Dockerfile 中可以写多个 CMDENTRYPOINT,但只有最后一个生效。
  • 如果既写了 ENTRYPOINT 又写了 CMDCMD 不会“附加”到 ENTRYPOINT 的字符串上,而是作为参数列表传递给 ENTRYPOINT(仅 exec 形式有效)。shell 形式下 CMD 会被忽略。
  • docker run 后面的命令不会覆盖 ENTRYPOINT,除非使用 --entrypoint

总结

  • 需要固定容器的主进程 → 使用 ENTRYPOINT(exec 形式)
  • 需要提供灵活的可替换默认参数 → 使用 CMD
  • 最佳组合:ENTRYPOINT ["fixed-command"] + CMD ["default-arg1", "default-arg2"]
  • 避免使用 shell 形式的 ENTRYPOINTCMD,除非你明确需要 shell 特性(如变量扩展、管道),并愿意承担信号处理风险。