Dockerfile 中的 ENTRYPOINT 和 CMD 详解
在 Dockerfile 中,ENTRYPOINT 和 CMD 都用于指定容器启动时运行的命令,但它们的角色和可覆盖性不同。理解两者的区别有助于正确配置容器的默认行为。
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,可能无法正确传递信号,且不支持CMD或docker 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. 覆盖规则总结
| 情况 | 实际执行命令 |
|---|---|
只有 CMD | CMD 的内容,可被 docker run <image> ... 完全覆盖 |
只有 ENTRYPOINT | ENTRYPOINT 的内容,docker run 后的参数作为附加参数(exec 形式下) |
| 两者都有 | ENTRYPOINT 固定,CMD 作为默认参数,可被 docker run 后的参数覆盖 |
docker run --entrypoint | 覆盖 ENTRYPOINT,此时 CMD 变成该新入口点的默认参数 |
6. 实用示例
示例 1:通用 CLI 工具容器
dockerfile
FROM alpine
ENTRYPOINT ["ping"]
CMD ["localhost"]docker run myping→ping localhostdocker run myping 8.8.8.8→ping 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 中可以写多个
CMD或ENTRYPOINT,但只有最后一个生效。 - 如果既写了
ENTRYPOINT又写了CMD,CMD不会“附加”到ENTRYPOINT的字符串上,而是作为参数列表传递给ENTRYPOINT(仅 exec 形式有效)。shell 形式下CMD会被忽略。 docker run后面的命令不会覆盖ENTRYPOINT,除非使用--entrypoint。
总结
- 需要固定容器的主进程 → 使用
ENTRYPOINT(exec 形式) - 需要提供灵活的可替换默认参数 → 使用
CMD - 最佳组合:
ENTRYPOINT ["fixed-command"]+CMD ["default-arg1", "default-arg2"] - 避免使用 shell 形式的
ENTRYPOINT或CMD,除非你明确需要 shell 特性(如变量扩展、管道),并愿意承担信号处理风险。
