3. 内存限制与 OOM 机制:--memory 参数详解、OOM Killer 工作原理、oom-score-adj 调整策略
在 Docker 企业级部署中,内存资源限制是防止单容器内存泄漏拖垮宿主机、保障业务连续性的第一道防线。本章深入解析 --memory 参数的实际行为、Linux OOM Killer 的触发与选择逻辑,以及通过 oom-score-adj 实现精细化优先级控制的方法。
3.1 --memory 参数详解:硬限制与软限制
Docker 通过 cgroups 实现内存限制,核心参数为 --memory(或 -m)和 --memory-reservation。两者共同构成内存资源管控的“硬上限”与“软保证”。
| 参数 | 类型 | 默认值 | 行为说明 |
|---|---|---|---|
--memory / -m |
硬限制 | 无限制 | 容器进程及子进程使用的内存总量不得超过此值。超过时触发 OOM Killer 或直接拒绝分配(取决于 --oom-kill-disable)。 |
--memory-reservation |
软限制 | 同 --memory |
当宿主机内存充足时,容器可超过此值;当宿主机内存紧张时,系统会优先回收超过此值的内存。用于设置“期望使用量”。 |
--memory-swap |
交换分区限制 | 两倍 --memory |
控制容器可使用的内存+交换分区总量。设为与 --memory 相同可禁用交换。 |
--memory-swappiness |
页面回收倾向 | 继承宿主机 | 0~100,值越低越倾向于回收匿名页(进程内存),值越高越倾向于回收文件缓存。 |
生产建议:始终同时设置
--memory 和 --memory-reservation。硬限制防止失控,软限制帮助调度器在资源竞争时做出合理决策。例如:--memory=4g --memory-reservation=2g。
3.2 OOM Killer 工作原理:从触发到裁决
当容器内存使用达到 --memory 硬限制时,Docker 不会直接杀死容器,而是触发 Linux 内核的 OOM Killer 机制。理解其完整流程是排查内存异常的基础。
核心公式:
内核通过此公式为每个进程打分,得分最高的进程被优先杀死。已用内存比例越高、
oom_score = 已用内存比例 × 系数 + oom_score_adj内核通过此公式为每个进程打分,得分最高的进程被优先杀死。已用内存比例越高、
oom_score_adj 越大,被杀死的概率越高。
3.3 oom-score-adj 调整策略:精细化优先级控制
oom-score-adj 是 Docker 提供的用户空间接口,允许运维人员直接干预 OOM Killer 的选择结果。其取值范围为 -1000 到 1000,默认值为 0。
| 取值区间 | 效果 | 典型场景 |
|---|---|---|
| -1000 | 完全禁用 OOM Killer 对该进程的杀伤 | 关键基础设施进程(如监控 Agent、SSH 守护进程) |
| -500 ~ -100 | 显著降低被杀死概率 | 数据库主进程、消息队列 Broker |
| 0 | 默认行为,按内存使用比例公平竞争 | 普通无状态应用 |
| 100 ~ 500 | 增加被杀死概率 | 缓存服务、批处理任务(可容忍中断) |
| 1000 | 确保该进程被优先杀死 | 临时计算任务、低优先级日志处理 |
避坑指南:不要对容器内所有进程统一设置
oom-score-adj。Docker 默认只对容器主进程(PID 1)生效。如果容器内运行多进程(如 Java 应用 + Sidecar),需要借助 docker run --oom-score-adj 配合 init 进程或自定义启动脚本,将调整值应用到子进程。
3.4 实战:内存限制与 OOM 调整配置示例
以下示例展示如何为一个 Java 微服务容器配置内存限制和 OOM 优先级:
# 启动容器:硬限制 2G,软限制 1G,禁用交换,降低 OOM 优先级
docker run -d \
--name payment-service \
--memory=2g \
--memory-reservation=1g \
--memory-swap=2g \
--oom-score-adj=-200 \
-e JAVA_OPTS="-Xmx1536m -Xms512m" \
payment-service:1.2.3
# 验证配置
docker inspect payment-service --format='{{.HostConfig.Memory}}'
# 输出:2147483648(2GB)
# 查看容器内进程的 oom_score_adj
docker exec payment-service cat /proc/1/oom_score_adj
# 输出:-200
最佳实践:Java 应用设置
-Xmx 时,建议保留 10%~20% 的余量给 JVM 堆外内存(Metaspace、线程栈、Direct Buffer)。例如 --memory=2g 时,-Xmx 设为 1536m~1600m,避免因堆外内存超限触发 OOM。
3.5 排查 OOM 异常的常用命令
当容器因 OOM 退出时,以下命令可快速定位根因:
- 查看容器退出码:
docker inspect --format='{{.State.ExitCode}}' <container>—— 137 表示 OOM 杀死。 - 查看 OOM 事件:
docker events --since 5m | grep -i oom—— 实时或历史 OOM 事件。 - 查看内核日志:
journalctl -k --since "5 minutes ago" | grep -i "oom\|killed"—— 查看具体被杀死进程。 - 查看 cgroup 内存统计:
docker exec <container> cat /sys/fs/cgroup/memory/memory.stat—— 分析内存使用构成。 - 查看 OOM 调整值:
docker inspect --format='{{.HostConfig.OomScoreAdj}}' <container>—— 确认配置是否生效。
快速诊断口诀:退出码 137 → 查 OOM 事件 → 看内核日志 → 调
oom-score-adj 或增大 --memory。