ENTRYPOINT定义容器主程序且不可覆盖,CMD提供默认参数;二者需同用exec形式才能正确传递参数,推荐用入口脚本+exec "$@"实现灵活启动与调试。

Dockerfile 中的 CMD 和 ENTRYPOINT 配合使用,能让容器启动行为更灵活、可复用性更强——关键在于理解它们的协作逻辑:ENTRYPOINT 定义容器的“主程序”,CMD 提供默认参数;运行容器时传入的命令会覆盖 CMD,但始终作为 ENTRYPOINT 的参数执行。
ENTRYPOINT 与 CMD 的执行关系
当同时存在 ENTRYPOINT(shell 或 exec 形式)和 CMD 时:
- ENTRYPOINT 必须是容器启动时**固定执行的命令或脚本**,不可被 docker run 覆盖
- CMD 是 ENTRYPOINT 的**默认参数列表**,若 docker run 后跟了新命令,则 CMD 被完全替换,新命令成为 ENTRYPOINT 的参数
- 两者都必须使用 exec 形式(数组写法)才能正确传递参数,例如
ENTRYPOINT ["nginx", "-g"]+CMD ["daemon off;"]→ 最终执行nginx -g "daemon off;" - 若 ENTRYPOINT 用 shell 形式(如
ENTRYPOINT nginx -g "daemon off;"),CMD 将被忽略(除非手动在 shell 命令里引用$@)
推荐写法:用 exec 形式 + 可扩展的入口脚本
生产环境建议将复杂逻辑封装进一个入口脚本(如 entrypoint.sh),再通过 ENTRYPOINT 调用它,CMD 传入业务参数:
- 在镜像中加入
entrypoint.sh,开头加#!/bin/sh,并chmod +x - Dockerfile 中写:
ENTRYPOINT ["/entrypoint.sh"],CMD ["app:start"] - 脚本内可用
exec "$@"安全执行 CMD 或用户传入的命令,既保留调试能力(如docker run myimg sh),又支持正常启动(docker run myimg)
常见误用与排查要点
容易出问题的地方集中在格式和覆盖逻辑上:
- 混用 shell 和 exec 形式:比如 ENTRYPOINT 是 shell 形式,CMD 是数组,CMD 会被静默丢弃
- 忘记给 entrypoint 脚本加执行权限,导致 “Permission denied” 错误
- 在 CMD 中写完整命令(如
CMD ["npm", "start"]),却没在 ENTRYPOINT 中预留接收位置,结果 npm 启动后容器立即退出(因为没前台进程) - 调试时想临时进容器,却写了
docker run -it myimg bash,发现进不去——检查是否 ENTRYPOINT 把 bash 当作参数传给了某个不接受交互命令的程序
典型实用场景示例
以 Python Web 应用为例,支持开发/生产双模式:
- Dockerfile:
ENTRYPOINT ["/app/entrypoint.sh"]<br>CMD ["gunicorn", "--bind", "0.0.0.0:8000", "app:app"]
- entrypoint.sh 内容节选:
#!/bin/sh<br># 可在此做环境准备、配置生成等<br>exec "$@"
- 运行方式:
• 默认启动:docker run mypyapp→ 执行 gunicorn
• 进入 shell 调试:docker run -it mypyapp sh→ 执行 sh
• 指定其他命令:docker run mypyapp python manage.py migrate










