CMD 指令指定容器启动默认命令,受运行参数和镜像继承影响;支持 shell、exec、参数三种格式,其中 exec 格式推荐生产使用;常因显式命令覆盖或误用导致失效,宜与 ENTRYPOINT 配合实现可控启动。

CMD 指令在 Dockerfile 中用于指定容器启动时默认执行的命令,但它不是一写就生效的“开机即跑”,而是受启动方式、镜像继承和运行时参数影响的关键配置。用对了省心,用错了容器秒退或命令被覆盖。
理解 CMD 的三种写法及其行为差异
CMD 支持 shell 格式、exec 格式和参数格式,实际效果差别很大:
-
Shell 格式(CMD command param1 param2):如
CMD nginx -g "daemon off;",实际被包装成/bin/sh -c "nginx -g \"daemon off;\""执行。优点是支持 shell 特性(如变量、管道),但 PID 1 不是 nginx 而是 sh,信号(如 SIGTERM)可能无法正确传递给主进程。 -
Exec 格式(CMD ["executable","param1","param2"]):如
CMD ["nginx", "-g", "daemon off;"],直接执行二进制,PID 1 就是 nginx,能正常接收系统信号,推荐在生产环境使用。 - 参数格式(CMD ["param1","param2"]):仅当配合 ENTRYPOINT 使用时有效,作为 ENTRYPOINT 命令的默认参数,单独写会报错或被忽略。
避免 CMD 被覆盖的常见陷阱
很多人发现写了 CMD 却没执行,往往是因为以下原因:
- 运行容器时显式传入命令(如
docker run myimg bash),会完全覆盖 CMD,此时 CMD 被忽略; - 基础镜像(如官方
node:alpine)已定义 CMD,而你的 Dockerfile 又写了新的 CMD,后者会覆盖前者——这是预期行为,但容易误以为“没生效”; - 误把 CMD 当作初始化脚本:CMD 不适合做环境准备(如创建目录、改权限),它只负责启动主进程;这类操作应放在 RUN 或 Entrypoint 脚本中。
结合 ENTRYPOINT 实现更可控的启动逻辑
想让容器始终运行某个程序,又允许用户灵活传参,推荐组合使用:
- 用 ENTRYPOINT 固定主程序(如
["/usr/local/bin/start.sh"]),保证不被覆盖; - 用 CMD 提供默认参数(如
["--port", "8080"]),用户可通过docker run myimg --port 9000覆盖; - start.sh 内部可做权限检查、配置生成等前置动作,再 exec 执行最终服务(
exec "$@"),确保 PID 1 正确。
验证 CMD 是否生效的小技巧
别只靠 docker run 看日志,多用这几个命令确认:
-
docker inspect myimage | grep Cmd查看镜像元数据中的 Cmd 字段,确认写入值; -
docker run --rm myimage ps aux快速查看实际启动的进程树; - 临时覆盖 CMD 测试:
docker run --rm myimage echo hello,若输出 hello,说明原 CMD 已被替换,证明它确实可被覆盖。










