docker run 下去容器没起来,docker ps -a 看到 STATUS 一片 Exited (137) / Exited (1)。这种问题在本地和 CI 里几乎天天发生。Docker 容器的启动失败原因集中在十几个固定场景,按频次从高到低查,5-10 分钟能落到具体位置。
第一步:先看容器报什么错
不要上来就 rebuild,先看 docker 自己说什么:
docker ps -a # 看 STATUS 列
docker logs container_id --tail 200
docker logs container_id --tail 200 2>&1 | grep -i 'error\|exception\|fail'
没有日志(容器压根没启动到输出那一步)就上 inspect:
docker inspect container_id | grep -A 3 'Error\|State'
第二步:常见错误码速查
| 退出码 | 含义 | 最常见原因 |
|---|---|---|
| 0 | 正常退出(少见) | 前台任务完成、配置写了 restart=always 错 |
| 1 | 应用启动失败 | 配置错 / 依赖缺失 / 端口占用 |
| 125 / 126 | docker daemon 问题 | Docker 服务没正常运行 |
| 127 | 命令找不到 | CMD/ENTRYPOINT 拼错或文件不存在 |
| 137 | OOM Killed(最常见) | 内存不足被宿主机杀掉 |
| 139 | 段错误(segfault) | 应用本身崩溃、版本不兼容 |
| 143 | 收到 SIGTERM | 正常被 docker stop / orchestrator 终止 |
第三步:频次最高的几个场景
场景 1:Exited (137) — OOM Killed
最常见。容器内存超出限制被宿主机 OOM Killer 杀掉。
docker inspect container_id | grep -i oom
docker events --since='2026-08-11T00:00:00' --filter type=oom
修复:要么加 --memory 1g,要么 JVM / Node 这种运行时调小堆内存(比如 Java -Xmx512m,Node --max-old-space-size=512)。
场景 2:Exited (1) — 应用启动失败
查 docker logs 里的具体报错。常见:
- 配置路径错(YAML / conf 文件找不到或语法错)
- 环境变量缺失(DB_HOST / API_KEY 没传)
- 端口被占(
bind: address already in use) - 数据库连接不上(等不到 DB ready 就起)
修法:
- 加 HEALTHCHECK 起一段时间再 ready
- depends_on 加 condition: service_healthy(Compose 里)
- 改环境变量或 mount 路径
场景 3:端口冲突
宿主机端口已经被占:
lsof -i :8080 # 看谁占了
docker run -p 9090:8080 image # 改宿主机端口
场景 4:CMD 形式错误
Shell 形式 CMD npm start vs Exec 形式 CMD ["npm","start"]。Exec 形式才是标准做法,Shell 形式容易收不到 SIGTERM,docker stop 要等 10 秒超时。
场景 5:卷权限错
挂载 -v 后容器内 uid/gid 跟宿主机不一致,文件 owner 是 root,应用写日志报 Permission denied。修法:
- 镜像里 USER 1000
- 宿主机
chown -R 1000:1000 /data - 用命名卷而非 bind mount(命名卷权限由 Docker 管)
场景 6:HEALTHCHECK 没配
容器其实起来了,但应用没监听端口,orchestrator 以为它挂了。Dockerfile 加:
HEALTHCHECK --interval=30s --timeout=3s \ CMD curl -f http://localhost:8080/healthz || exit 1
场景 7:Secrets 进了 ENV
密码直接写在 ENV 里,docker inspect 能看到全部明文。改用 --env-file 或 Docker secrets / Vault。
场景 8:.dockerignore 没配
node_modules / .git / .env / *.pem 全被打进镜像,体积爆炸还泄密。建 .dockerignore:
node_modules .git .env *.pem *.key
第四步:进入容器内部排查
容器至少跑到了一瞬间,可以用 docker exec -it container_id /bin/sh 进去。如果进不去,就在 docker run 时加 --entrypoint /bin/sh 临时覆盖启动命令。
或者 docker run --rm -it image:tag /bin/sh 直接跳过 CMD 进去看文件系统。
第五步:K8s 场景下的容器启动失败
在 K8s 里更复杂一点:
| Pod 状态 | 原因 | 排障命令 |
|---|---|---|
| ImagePullBackOff | 镜像拉不下来 | kubectl describe pod |
| CrashLoopBackOff | 应用反复崩溃 | kubectl logs –previous |
| OOMKilled | 超内存限制 | kubectl describe pod 看 status |
| Pending | 调度不到节点 | kubectl describe pod 看 events |
| CreateContainerConfigError | Secret/ConfigMap 缺失 | kubectl get cm,secret |
Deployment 防崩 checklist:
- 别用 :latest,固定 tag
- resources.requests + limits 都设
- livenessProbe + readinessProbe 都配
- runAsNonRoot: true,readOnlyRootFilesystem: true(tmp 用 emptyDir)
常见问题
Q:容器起来又立刻挂掉,根本来不及看日志怎么办?
A:加 --restart=no 防止自动重启覆盖掉日志;或者 docker run --entrypoint /bin/sh -it image 跳过 CMD 进去手动跑启动命令,能看到每一步输出。
Q:本地能跑,部署到服务器就起不来?
A:八成是镜像架构不匹配(Mac M1 是 arm64,服务器是 amd64)。用 docker buildx build --platform linux/amd64 多架构构建,或者服务器上用 docker run --platform linux/amd64 指定。
Q:Compose 起服务有顺序依赖怎么办?
A:用 depends_on 加 condition: service_healthy,配合每个服务的 HEALTHCHECK;旧版的 condition: service_started 只等启动不等 ready。
容器启动失败的原因 90% 都在上面这些场景里。先看日志、再看退出码、再对号入座,绝大多数 5 分钟能修好。如果有具体报错看不懂,贴到 fenij.com 留言里,我帮你接着分析。