记一次线上Docker容器OOM排查
Raj Kumar | 2026-08-26T06:27:00 | DevOps, Cloud
K8s线上容器频繁OOM重启的排查过程,从kubectl describe到Java堆转储分析的全流程
# 记一次线上Docker容器OOM排查 ## 背景 周一早上刚到公司,运维群里就炸了:“线上Java服务频繁重启!”。打开监控一看,Pod在过去12小时内重启了8次,全是OOMKilled。 ## 排查过程 ### 第一步:确认OOM ```bash # 查看Pod状态 kubectl describe pod app-service-xxx -n production # 输出关键信息: # Last State: Terminated # Reason: OOMKilled # Exit Code: 137 ``` ### 第二步:检查资源配置 ```yaml # deployment.yaml 资源配置 resources: requests: memory: "512Mi" # 请求内存 limits: memory: "1Gi" # 限制内存 ``` ### 踩坑:JVM堆 vs 容器内存 > 这里是最大的坑:容器内存限制1Gi,但JVM不只有堆内存! ```bash # 有问题的启动参数 java -Xmx1g -jar app.jar # JVM总内存 = 堆(1g) + 元空间 + 线程栈 + 直接内存 > 1Gi # 所以容器一定会OOM! # 正确配置 - 堆内存设为容器限制的60-70% java -Xmx614m -Xms614m \ -XX:MaxMetaspaceSize=128m \ -XX:+UseContainerSupport \ -jar app.jar ``` ### 第三步:堆转储分析 ```bash # 在容器中生成堆转储 kubectl exec -it app-service-xxx -- \ jmap -dump:format=b,file=/tmp/heap.hprof 1 # 拷贝到本地 kubectl cp app-service-xxx:/tmp/heap.hprof ./heap.hprof # 用MAT或VisualVM分析 # 发现:一个缓存Map没有设置大小限制,不断膨胀 ``` ## 问题根因 代码中有一个本地缓存,用的是普通HashMap,没有做大小限制和过期策略: ```java // 问题代码 private static Map cache = new HashMap(); // 只有put,没有remove,内存持续增长 // 修复:改用Caffeine缓存 private static Cache cache = Caffeine.newBuilder() .maximumSize(10000) .expireAfterWrite(Duration.ofMinutes(30)) .build(); ``` ## 总结 - 容器内存限制 != JVM堆内存,要田30-40%给非堆 - 加上 `-XX:+UseContainerSupport` 让JVM感知容器 - 本地缓存一定要设大小和过期策略