cmd提供默认可覆盖的启动命令,entrypoint设定不可轻易更改的执行入口;二者组合时,entrypoint作为主程序,cmd或docker run参数作为其附加参数。
在 dockerfile 中正确编写容器启动命令,关键在于区分 cmd、entrypoint 和 docker run 时传入的参数三者的关系。多数问题源于混淆它们的执行逻辑,导致容器启动失败或命令未按预期运行。
CMD:提供默认可被覆盖的运行时指令
CMD 定义容器启动时默认执行的命令,但它可以被 docker run 后面的参数完全替换。它有三种写法,推荐使用exec 形式(数组格式):
-
✅ 推荐:
CMD ["node", "app.js"]—— 直接执行,不经过 shell,信号能正常传递(如 SIGTERM),适合生产环境 -
❌ 避免:
CMD node app.js—— shell 形式,实际执行的是/bin/sh -c "node app.js",主进程不是 node,而是 sh,导致 stop/kill 失效 - ⚠️ 注意:
CMD只能有一个生效,后写的会覆盖前面的
ENTRYPOINT:设定不可轻易更改的执行入口
ENTRYPOINT 让容器像一个可执行程序一样运行,它的值不会被 docker run 的命令参数覆盖,而会被当作“主程序”,CMD 或 run 后的参数则作为它的参数传入。
-
✅ 常见用法:
ENTRYPOINT ["./entrypoint.sh"]+CMD ["--port=3000"]→ 启动时执行./entrypoint.sh --port=3000 - ✅ 适合封装初始化逻辑:比如检查环境变量、生成配置、等待依赖服务就绪等
-
❌ 不要混用 shell 和 exec 形式:例如
ENTRYPOINT /bin/sh -c "./start.sh"会丢失信号,且无法接收 CMD 参数
如何组合使用 CMD 和 ENTRYPOINT(推荐生产模式)
典型可靠结构是:ENTRYPOINT 固定为启动脚本或主二进制,CMD 提供默认参数,既保证灵活性又不失可控性。
-
示例(Node.js 应用):
FROM node:18-alpine<br> COPY . /app<br> WORKDIR /app<br> RUN npm ci --only=production<br> ENTRYPOINT ["npm", "start"]<br> CMD []
此时docker run myapp执行npm start;docker run myapp -- --port=4000会传参给 npm(即npm start -- --port=4000) -
示例(带初始化脚本):
ENTRYPOINT ["./docker-entrypoint.sh"]<br> CMD ["mysqld"]
脚本内可判断$1是mysqld还是mysql,再决定启动服务还是进入客户端
验证与调试技巧
写完 Dockerfile 后,别急着部署,先本地验证行为是否符合预期:
- 构建后查看配置:
docker inspect myimage | grep -A 5 'Entrypoint\|Cmd' - 临时绕过 ENTRYPOINT 测试 CMD:
docker run --entrypoint="" myimage sh -c 'ps aux' - 交互式调试启动过程:
docker run -it --rm myimage sh,手动执行 ENTRYPOINT 中的命令看是否报错 - 确认主进程 PID 是否为 1:
docker exec -it <container> ps -p 1</container>,如果不是你期望的进程,说明 shell 封装过深











