面向初级运维,使用 Linux 节点和稳定 Kubernetes API。命令按 Bash 编写。本文为教学故障场景,日志及结果为示意,不冒充生产实测;执行前确认 context、命名空间和权限。
一、遭遇情况:应用突然重启,日志没有异常堆栈
服务偶尔中断,Pod 名字没有变化,但 RESTARTS 增加。上一轮容器显示 OOMKilled、退出码 137。应用日志可能在正常输出时突然结束,因为进程被内核终止,未必有机会打印完整异常。
本篇案例是容器内存 limit 为 64 MiB,而程序持续保留新分配内存。节点整体还有余量,也可能因为这个容器自己的限制而发生 OOM。
二、原因分析:三类现象不能混为一谈
容器达到内存限制后被终止;节点整体内存压力触发 OOM;kubelet 因节点压力驱逐 Pod。这些情况都与内存有关,但对象状态、恢复方式和修复重点不同。

requests 主要参与资源预留与调度,limits 提供运行时边界。把 requests 写小不会让程序实际少用内存,还可能让节点放入更多无法稳定共存的工作负载。
137 是常见 SIGKILL 退出线索,不独属于 OOM。主动强杀或停止超时也可能产生同类数字,应与 terminated.reason 和事件结合判断。
三、排查思路:当前数值不能代替过去峰值
kubectl describe pod 目标Pod -n 目标命名空间
kubectl get pod 目标Pod -n 目标命名空间 -o jsonpath='{.status.containerStatuses}{"\n"}'
kubectl top pod 目标Pod -n 目标命名空间 --containers
kubectl describe node 目标节点
top 依赖 Metrics API,集群没部署相关组件时可能不可用,不代表 kubelet 一定坏了。重启后当前内存很低,也不能否定上一轮达到限制;需要历史监控、日志时间与节点记录。
有授权的节点管理员可进一步查看内核 OOM 记录。托管集群可能需要通过平台日志获取,不应为了取证临时开放所有节点权限。
应用内存还包含非业务堆部分,例如线程栈、本地分配和缓存。Java 的 Xmx 不能简单等于容器 limit;具体余量应按运行时与负载测量。
四、实践:仅在测试集群运行受限内存实验
本实验只让一个新容器在 64 MiB 限制内分配内存,不对真实应用注入压力。需要测试集群有余量且允许这些资源设置,保存为 kt07-memory.yaml:
apiVersion: v1
kind: Namespace
metadata:
name: kt07
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: memory-demo
namespace: kt07
spec:
replicas: 1
selector:
matchLabels:
app: memory-demo
template:
metadata:
labels:
app: memory-demo
spec:
containers:
- name: app
image: python:3.12-alpine
command: ["python", "-u", "-c"]
args:
- |
import time
blocks = []
while True:
blocks.append(bytearray(4 * 1024 * 1024))
print("allocated blocks:", len(blocks), flush=True)
time.sleep(0.3)
resources:
requests:
cpu: 25m
memory: 32Mi
limits:
cpu: 250m
memory: 64Mi
kubectl apply -f kt07-memory.yaml
kubectl get pods -n kt07
kubectl describe pod -n kt07 -l app=memory-demo
预期可观察到容器持续分配后被终止并重启,常见 reason 为 OOMKilled。若发生 Python MemoryError、准入拒绝或节点其他异常,应记录实际结果,不能把预期写成既定事实。
不要长时间保留这个故意失败的实验。看清证据后及时修正或删除自己创建的 Deployment。
五、修复实践:本例修程序行为,不只增加内存
实验程序无限保留内存,没有任何固定 limit 能让它永久稳定。把 args 改成有界分配:
kubectl patch deployment memory-demo -n kt07 --type=strategic -p '{"spec":{"template":{"spec":{"containers":[{"name":"app","args":["import time; blocks=[bytearray(4*1024*1024)]; print(\"bounded-memory\", flush=True); time.sleep(3600)"]}]}}}}'
kubectl rollout status deployment/memory-demo -n kt07 --timeout=120s
kubectl get pods -n kt07
这样在原有内存限制下也有机会保持稳定,重点是分配不再无限增长。对真实应用,应检查泄漏、缓存上限、批处理大小和并发;若合理业务需求本来就超过限制,再评估节点容量并调整 requests 与 limits。
如果根因是节点压力或驱逐,还要检查其他工作负载和容量规划,不能只改这一个容器。Pod 因 Evicted 失败的详细路径见第 15 篇。
六、验证与总结
用接近故障时的真实负载检查内存峰值、重启次数、延迟与节点条件。仅新 Pod 在空载时运行一分钟,没有证明高峰故障已消失。
内存故障的证据链是:哪个容器 → 哪次退出 → 哪条资源边界 → 哪种分配趋势 → 修复后同类负载是否稳定。不要把“多给一点内存”当成所有 OOM 的最终答案。