Exited (0) 表示前台进程(PID 1)正常结束,并非容器启动成功;Docker 容器依赖持续运行的前台进程维持生命周期,若 ENTRYPOINT/CMD 中命令执行完即退出(如 sh -c、npm start 包装脚本),或服务默认后台运行(如 nginx、redis),容器便会立即退出。

为什么 Exited (0) 其实是“前台进程跑完了”
退出码 0 不代表容器“启动成功”,只说明 PID 1 进程调用了 exit(0) —— 它干完活就走了。Docker 容器没有“后台服务”概念,它必须靠一个持续运行的前台进程撑住生命周期。比如你写 CMD ["sh", "-c", "echo hello"],echo 执行完、sh 退出,容器立刻变成 Exited (0)。这不是 bug,是设计使然。
检查 ENTRYPOINT 和 CMD 是否意外结束
最容易被忽略的是命令写法导致进程提前退出:
-
ENTRYPOINT ["python", "app.py"]是 OK 的,只要app.py启动后不主动退出(如 Flask 默认 dev server 就是常驻) -
ENTRYPOINT ["sh", "-c", "python app.py"]是危险的:如果app.py没有阻塞逻辑,sh会等它结束然后自己退出 → 容器退出 -
CMD ["npm", "start"]在某些 Node 镜像里实际执行的是npm start包装脚本,而该脚本可能 fork 出子进程后直接 exit,导致 PID 1 变成 shell 而非 node 进程
验证方法:docker run -it --rm your-image ps aux,看 PID 1 是不是你预期的服务进程(如 node、nginx、java),而不是 sh 或 bash。
常见“伪前台”陷阱与绕过方案
有些程序默认以 daemon 模式启动(即 fork + exit),这会让它在容器里瞬间“消失”:
-
nginx默认后台运行 → 必须加-g "daemon off;" -
redis-server默认后台运行 → 改用redis-server /etc/redis.conf --daemonize no -
supervisord启动后默认不前台 → 加-n参数
临时调试可用 tail -f /dev/null 占位,但这是掩耳盗铃;真正要解决,得让主进程真正在前台跑起来。
别信日志,要看进程树和信号链
docker logs 可能完全空白,因为进程退出太快,连日志都来不及刷到 stdout。这时必须用更底层的方式确认:
- 容器还在时,进容器看进程:
docker exec -it <container-id> ps auxf</container-id>,观察父子关系是否断裂 - 查 cgroup 状态:
docker inspect <container-id> | grep -A5 "State"</container-id>,重点看"Status": "exited"和"OOMKilled": false(排除 OOM 干扰) - 用
docker events --filter event=die --since 1h看退出触发时间点,比对应用日志时间戳
最隐蔽的情况是:进程收到 SIGTERM 后优雅退出,返回 0,但你根本没发过 docker stop —— 很可能是健康检查失败或 Kubernetes liveness probe 触发了重启策略,而 Docker Desktop 本身不会干这事,得回头查编排配置。


















