线上服务最怕"没有明显报错,却自己重启"。监控里只看到请求突然失败,容器列表里服务又重新起来了,应用日志可能只停在某一行,没有异常堆栈。这类问题里,Docker 容器被内存限制触发 OOMKilled,是很常见也很容易误判的一种。
这篇文章给一套生产可用的排查路径:先确认是不是 OOMKilled,再看容器限制、宿主机内存、内核日志和应用指标,最后给出临时止血、长期治理和发布前验证清单。适合部署 Web/API、爬虫任务、AI 推理服务、Java/Node/Python 后端的小团队使用。
一、先确认容器是不是被 OOM 杀掉
不要第一时间重启服务。先保留现场,执行:
bash
docker ps -a --format "table {{.Names}}\t{{.Status}}\t{{.Image}}"
docker inspect <container_name> --format='OOMKilled={{.State.OOMKilled}} ExitCode={{.State.ExitCode}} FinishedAt={{.State.FinishedAt}}'
docker logs --tail=120 <container_name>
如果 OOMKilled=true,或者退出码经常是 137,通常说明进程收到了 SIGKILL。它可能来自容器内存限制,也可能来自宿主机整体内存紧张。Docker 官方文档说明,容器运行时可以通过内存相关参数限制容器可用资源;Linux 内核在内存压力下也可能触发 OOM killer。
二、看容器限制:是不是给得太小
先看容器是否设置了内存上限:
bash
docker inspect <container_name> --format='Memory={{.HostConfig.Memory}} MemorySwap={{.HostConfig.MemorySwap}}'
docker stats --no-stream
如果 Memory 输出为 0,表示没有给容器设置明确内存限制;如果有具体数值,需要换算成 MB/GB。常见误区是:服务实际峰值需要 900MB,却只给了 512MB;应用一遇到导出报表、图片处理、模型加载、批量任务,就被系统杀掉。
临时止血可以先提高限制,但不要只靠扩容:
bash
docker update --memory 1g --memory-swap 1g <container_name>
docker restart <container_name>
如果是 docker compose,建议显式写清资源限制,避免不同环境表现不一致:
yaml
services:
api:
image: your-api:latest
mem_limit: 1g
restart: unless-stopped
environment:
- NODE_OPTIONS=--max-old-space-size=768
- ```
## 三、看宿主机:不是容器的问题也会被杀
容器限制正常时,继续看宿主机有没有整体内存压力:
```bash
free -h
vmstat 1 5
top -o %MEM
journalctl -k --since "2 hours ago" | grep -i -E "out of memory|oom|killed process"
dmesg -T | grep -i -E "out of memory|oom|killed process" | tail -30
如果内核日志里出现 Out of memory、Killed process,说明宿主机层面已经发生 OOM。此时要重点关注同机部署了多少服务、是否有定时任务、备份压缩、日志处理、爬虫或 AI 推理进程在抢内存。
四、定位应用内存增长:是峰值太高,还是持续泄漏
排查时要区分两类问题:
- 瞬时峰值:导入导出、图片压缩、PDF 生成、模型加载、批量查询时内存突然冲高;
-
- 持续泄漏:服务运行时间越久内存越高,即使流量回落也不释放。
Node.js 可以先看进程内存:
- 持续泄漏:服务运行时间越久内存越高,即使流量回落也不释放。
bash
node -e "setInterval(()=>console.log(process.memoryUsage()),5000)"
Java 服务要关注 JVM 堆配置与容器限制是否匹配:
bash
jcmd <pid> VM.flags
jcmd <pid> GC.heap_info
Python 服务重点检查大列表、缓存、图片对象、模型对象是否长期持有引用。AI 推理服务还要单独看 GPU 显存和 CPU 内存,很多模型加载失败并不是显卡不够,而是 tokenizer、向量索引或中间批数据占满了系统内存。
五、止血方案:先恢复,再治理
线上故障时建议按这个顺序处理:
- 保留现场截图和日志,不要直接清空容器;
-
- 临时提高容器内存限制或减少并发;
-
- 暂停大任务、定时导出、离线计算;
-
- 给核心服务单独机器或单独实例,避免和重任务混跑;
-
- 增加内存监控和 OOM 告警;
-
- 回头做代码层面的泄漏排查。
如果业务允许,给批处理任务加队列和限流,比单纯加大服务器更稳。很多小团队的问题不是服务器太小,而是所有任务都挤在一台机器里,Web 请求、定时任务、日志压缩、备份、爬虫一起抢资源。
- 回头做代码层面的泄漏排查。
六、发布前验证清单
上线前至少做一次压测和资源观测:
bash
docker stats
curl -s http://127.0.0.1:8080/health
journalctl -k --since "30 minutes ago" | grep -i oom
如果是 API 服务,建议压测 10 到 20 分钟,观察内存是否稳定在一个区间;如果是任务型服务,至少跑一遍最大数据量样本。不要只看"功能能跑通",还要看"资源是否会持续上涨"。
七、建议的最终状态
生产环境建议做到:
- 核心服务设置明确内存限制;
-
- 容器重启策略可控,不让服务无限重启掩盖问题;
-
- 宿主机保留 20% 以上内存余量;
-
- 大任务与在线 API 分开部署;
-
- OOM、重启次数、内存使用率进入监控;
-
- 每次扩容都记录原因,避免长期靠加钱掩盖代码或架构问题。
参考资料:Docker 官方资源限制文档、Docker inspect 命令文档、Linux kernel OOM killer 相关文档、systemd journalctl 手册。
- 每次扩容都记录原因,避免长期靠加钱掩盖代码或架构问题。
如果你正在做网站/API/AI 应用上云,遇到容器频繁重启、内存上涨、502/504、日志爆满等问题,可以把现有架构、实例配置和错误日志整理出来,通过 CSDN 私信交流排查思路。



