Docker 容器退出码 137 (OOMKilled) 排查与修复:内存不足导致 SIGKILL 的完整解决方案

主题: docker-compose-exit-code-137-memory-fix更新于: 2026/7/17作者:AgentFactory 技术团队

快速答案

  • 结论:退出码 137 表示容器被 SIGKILL 信号(信号 9)终止,最常见原因是内存不足(OOM),无论是容器自身超过内存限制,还是宿主机整体内存耗尽。
  • 第一检查:运行 docker inspect <container> 查看 State.OOMKilled 是否为 true;同时执行 dmesg | grep -i "killed process" 检查内核日志。
  • 最小修复:增加容器内存限制(docker run -m 2g ...)或在 Docker Compose 中添加 mem_limit: 512m;对于 Kubernetes 环境,调整 resources.limits.memory
  • 适用边界:本指南适用于 Docker 单机环境和 Kubernetes 环境,覆盖从原因分析到预防措施的完整闭环,但不包含自动化日志分析脚本或告警配置。

它解决什么问题 / 适用场景

Docker 容器退出码 137 是一个常见但容易被误判的问题。它直接表明容器收到了 SIGKILL 信号,无法优雅关闭。最常见的触发场景包括:

  • 容器内存超限:容器使用的内存超过了 --memory 参数设置的限制,被 Docker 的 cgroup OOM killer 杀死。
  • 宿主机内存耗尽:宿主机整体内存不足,内核主动杀死进程以释放内存,即使容器本身未超限。
  • 手动强制终止:有人或自动化脚本执行了 docker killkill -9
  • Kubernetes Pod 被驱逐:Pod 超出节点资源限制,被 kubelet 驱逐。

该指南适用于任何使用 Docker 或 Kubernetes 部署的应用,特别是内存敏感或资源密集型的工作负载,如数据库、数据处理任务、大型 Web 应用等。运维人员、开发者和 SRE 是主要受众。

核心排查步骤与根因分析

第一步:确认 OOM 状态

最直接的证据来自 Docker 的 inspect 输出:

BASH
docker inspect <container_name_or_id> --format '{{.State.OOMKilled}}'

如果返回 true,说明容器确实因内存超限被杀死。

同时检查内核日志:

BASH
dmesg | grep -i "killed process"

输出示例:

[12345.678901] Memory cgroup out of memory: Killed process 12345 (java) total-vm:2048576kB, anon-rss:1048576kB, file-rss:0kB, shmem-rss:0kB

如果日志中包含 Memory cgroup out of memory,说明是容器 cgroup 限制导致的 OOM;如果只有 Out of memory: Killed process 而没有 cgroup 信息,则可能是宿主机整体内存不足。

第二步:检查容器资源使用趋势

使用 docker stats 实时观察容器内存使用情况:

BASH
docker stats <container_name>

重点关注 MEM USAGE / LIMIT 列。如果内存使用持续增长直至接近限制,则很可能存在内存泄漏。

第三步:区分不同原因

现象OOMKilled 状态dmesg 输出根因
容器内存超限true包含 Memory cgroup容器自身超过 --memory 限制
宿主机内存耗尽false无 cgroup 信息宿主机整体内存不足
手动 kill -9false无 OOM 相关人为或自动化操作
应用内部崩溃false无 OOM 相关应用 bug 导致进程异常退出

常见报错与排查

错误 1:docker inspect 显示 OOMKilled: true

解决方案:这是最直接的 OOM 证据。

  1. 增加容器内存限制

    BASH
    docker run -m 2g --memory-reservation 1g your-image
    
  2. 检查应用内存泄漏:使用 docker stats 或应用性能监控工具(如 JDK 的 jvisualvm、Python 的 memory_profiler)观察内存使用趋势。

  3. 在 Kubernetes 中调整 Pod 资源

    YAML
    resources:
      limits:
        memory: "2Gi"
      requests:
        memory: "1Gi"
    

错误 2:容器日志中无明确错误,但 dmesg 显示 Out of memory: Killed process

解决方案:这表明是宿主机内核主动杀死了进程。

  1. 检查宿主机内存使用

    BASH
    free -h
    htop
    
  2. 减少宿主机负载:停止不必要的容器或进程,或考虑增加物理内存。

  3. 调整 Docker 的 OOM 优先级(谨慎使用):

    BASH
    docker run --oom-score-adj -500 your-image
    

    负值降低被 OOM killer 优先杀死的概率,但不应依赖此方法解决根本问题。

错误 3:容器被 docker killkill -9 终止

解决方案:这是人为或自动化操作导致的。

  1. 追溯操作来源:检查命令历史(history)或 CI/CD 流水线日志。

  2. 改用优雅关闭:避免在生产环境中使用 docker killkill -9,改用 docker stop(发送 SIGTERM)或 kill -15

  3. 在 Kubernetes 中调整优雅关闭时间

    YAML
    spec:
      terminationGracePeriodSeconds: 60
    

错误 4:退出码 137,但 OOMKilled: false

解决方案:可能原因包括:

  1. 应用内部崩溃:检查应用日志,可能是进程自身异常退出但被 Docker 以 SIGKILL 方式处理。
  2. 内核内存分配失败:罕见情况,检查 dmesg 输出中是否有其他内核错误。
  3. CPU 限制过严:检查 docker inspect 中的 CPU 配额设置(NanoCpusCpuShares),极低 CPU 限制可能导致 cgroup OOM killer 误判。

生产环境实践与注意事项

1. 为所有容器设置合理的资源限制

BASH
# Docker 运行
docker run -m 512m --memory-reservation 256m --memory-swap 1g your-image

# Docker Compose
services:
  my-service:
    image: my-image
    mem_limit: 512m
    mem_reservation: 256m

注意mem_limit 是硬限制,超过此限制容器将被 OOM kill。mem_reservation 是软限制,当宿主机内存紧张时,Docker 会尝试将容器内存压缩到此值以下。

2. Kubernetes 资源管理

YAML
apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
  containers:
  - name: my-container
    image: my-image
    resources:
      requests:
        memory: "256Mi"
        cpu: "250m"
      limits:
        memory: "512Mi"
        cpu: "500m"

关键点

  • requests 用于调度决策,limits 是硬限制。
  • 建议 requestslimits 设置不同的值,允许突发流量。
  • 使用 requests 保证服务质量(QoS),limits 防止资源滥用。

3. 监控与告警

使用 Prometheus + Grafana 监控容器内存使用率,设置告警阈值(如内存使用率超过 80% 持续 5 分钟)。

YAML
# Prometheus 告警规则示例
groups:
- name: container-alerts
  rules:
  - alert: ContainerMemoryHigh
    expr: container_memory_usage_bytes{container!=""} / container_spec_memory_limit_bytes{container!=""} > 0.8
    for: 5m
    labels:
      severity: warning
    annotations:
      summary: "Container {{ $labels.container }} memory usage > 80%"

4. 重启策略

对于关键服务,配置自动重启:

YAML
# Docker Compose
services:
  my-service:
    restart: unless-stopped

# Kubernetes
spec:
  restartPolicy: Always

5. 定期审查应用代码

内存泄漏是导致 OOM 的常见原因。定期使用工具(如 Valgrind、AddressSanitizer、Python 的 tracemalloc)检查应用内存使用。

常见问题 FAQ

Q: 退出码 137 和退出码 139 有什么区别?

A: 退出码 137 表示容器被 SIGKILL (信号 9) 终止,通常是因为内存不足 (OOM) 或手动强制杀死。退出码 139 表示容器被 SIGSEGV (信号 11) 终止,通常是由于程序访问了无效内存地址(段错误),这往往是应用本身的 bug(如空指针解引用、缓冲区溢出)导致的。

Q: 在 Docker Compose 中如何为服务设置内存限制以防止退出码 137?

A: 在 docker-compose.yml 文件中,为服务添加 deploy 配置(Swarm 模式)或使用 mem_limitmem_reservation 字段。例如:

YAML
services:
  my-service:
    image: my-image
    mem_limit: 512m
    mem_reservation: 256m

注意:mem_limit 是硬限制,超过此限制容器将被 OOM kill。

Q: 如何区分是容器内应用内存泄漏还是宿主机整体内存不足导致的 OOM?

A: 1. 查看 docker inspect <container> 中的 OOMKilled 字段。如果为 true,说明是容器自身超过了其内存限制。2. 查看宿主机 dmesg 输出。如果看到 Memory cgroup out of memory: Killed process 后面跟着容器进程的 PID,说明是容器 cgroup 限制导致的。如果看到 Out of memory: Killed process 但没有 cgroup 信息,则可能是宿主机整体内存不足。3. 使用 docker stats 观察容器内存使用趋势,如果持续增长,则可能是内存泄漏。

Q: 退出码 137 是否一定意味着 OOM?

A: 不一定。虽然 OOM 是最常见的原因,但手动执行 docker killkill -9、Kubernetes Pod 驱逐、甚至某些内核错误也可能导致退出码 137。务必结合 docker inspectdmesg 输出综合判断。

相关深度解决方案

在配置当前服务时,如果您需要实现更复杂的架构或多源数据整合,建议配合参考我们整理的 Node.js 堆内存溢出(OOM)实战排查与修复:从应急到根治

在配置当前服务时,如果您需要实现更复杂的架构或多源数据整合,建议配合参考我们整理的 Docker 容器 DNS 解析失败排查与修复:从临时调试到生产配置