Docker容器在终止时,容器内主进程返回的整数值会被Docker daemon捕获并记录为退出码(Exit Code)。这个数值不是Docker随意定义的,而是遵循Linux进程退出状态规范,同时叠加了Docker自身对特殊信号的处理逻辑。理解退出码,是排查容器异常退出最直接、成本最低的切入点。当容器频繁重启或任务中断,与其盲目翻日志,不如先执行docker inspect看一眼状态码。

常见Docker退出码及其底层含义
最基础的退出码是0,它表示容器内的主进程正常结束,没有发生任何错误。在CI/CD流水线中,0代表构建或测试通过;如果是长期运行的服务主动退出并返回0,往往说明程序被设计成一次性任务或者收到了优雅终止信号后自行收尾。与之相对,退出码1通常表示应用程序发生了一般性错误,例如未捕获的异常、配置文件缺失或数据库连接失败。这类问题需要结合容器日志中的堆栈信息来定位具体代码位置。
退出码137是生产环境最常遇到的状态码之一。它对应的二进制高位表示进程被信号9(SIGKILL)终止,计算方式为128+9=137。SIGKILL不能被进程捕获或忽略,一般由操作系统OOM Killer在内存耗尽时发出,或是用户手动执行docker kill。如果容器设置了内存限制而应用真实占用超标,就会触发该码。另一个高频码是143,即128+15,代表SIGTERM信号,说明进程被要求终止但可能没来得及完全关闭,常见于docker stop默认行为。
此外,退出码126表示容器内的命令无法执行,比如权限不足或解释器不存在;127代表命令未找到,往往是ENTRYPOINT或CMD写错路径。还有特殊码139(SIGSEGV段错误)、134(SIGABRT断言失败)等,多指向程序底层bug或依赖库不兼容。下表列出了部分核心退出码对照关系,方便日常速查。
| 退出码 | 信号/含义 | 典型场景 |
|---|---|---|
| 0 | 正常退出 | 任务完成、服务优雅关停 |
| 1 | 应用错误 | 代码异常、配置错误 |
| 137 | SIGKILL | OOM、强制杀进程 |
| 143 | SIGTERM | docker stop默认信号 |
| 126 | 权限/不可执行 | 脚本无执行位 |
| 127 | 命令未找到 | CMD路径错误 |
如何通过Docker命令定位退出码根源
拿到退出码后,第一步应使用docker ps -a查看容器状态列中的Exited (137)字样,确认基础信息。接着执行docker inspect 容器名,在输出的JSON中搜索State节点,里面包含ExitCode、Error以及OOMKilled布尔字段。若OOMKilled为true,则可断定是内存问题,需要调大--memory限制或优化应用内存占用。该命令还能看到挂载卷和网卡信息,排除因存储驱动故障导致的异常。
日志是退出码的重要补充。运行docker logs --tail 200 容器名可以拉取退出前的标准输出与错误流。对于退出码1,日志中通常会有明显的异常堆栈;对于126或127,则可能显示permission denied或exec format error。如果容器重启过快来不及看日志,可加上--timestamps参数保留时间线,或利用docker events实时监听守护进程层面的销毁事件,交叉验证退出时刻的系统压力。
在Kubernetes等编排系统中,退出码会被包装为Reason字段,如OOMKilled、Error、CrashLoopBackOff。此时除了看Pod描述,还可进入节点用journalctl -u docker查守护进程日志,确认是否为节点级资源争抢。通过组合inspect、logs与宿主监控,基本能在几分钟内锁定退出码背后的真实诱因,而不必盲目回滚镜像。
编写健壮Dockerfile降低异常退出概率
许多非预期退出源于镜像构建阶段埋下的隐患。建议在Dockerfile中用exec形式写ENTRYPOINT,例如ENTRYPOINT ["java","-jar","app.jar"],避免shell形式带来的信号转发失效,使得SIGTERM能正确送达JVM。同时,给基础镜像打固定版本标签,防止某次docker build拉取到含bug的新版系统库,从而导致运行时随机出现134或139。
针对内存类退出,应在docker run或compose中显式声明限制并预留 headroom。例如设置mem_limit: 512m且应用堆内存控制在300m以内,既防OOM也便于监控。对于可能退出的批处理容器,在外部用restart策略设为on-failure并配合最大重试次数,避免无限重启掩盖问题。下面是一段docker-compose片段示例,展示了限制与重启策略的写法。
version: '3'
services:
worker:
image: myapp:1.0
# 使用exec形式在镜像内已定义
restart: on-failure:3
deploy:
resources:
limits:
memory: 512M
# 通过环境变量控制应用内存
environment:
- JAVA_OPTS=-Xmx300m
最后,在CI环节加入退出码断言。测试脚本中若容器返回非0,则直接失败并输出docker inspect结果。这样能把排查窗口左移,开发者在本地就能感知到126、127这类低级错误,而不是等到生产环境凌晨告警才面对137。把退出码当成契约的一部分,团队排障效率会有明显提升。