K8s 资源限制实战:requests/limits 怎么配,OOMKilled 怎么查

📅 2026/7/14 20:54:18
K8s 资源限制实战:requests/limits 怎么配,OOMKilled 怎么查
K8s 资源限制实战:requests/limits 怎么配,OOMKilled 怎么查线上 Pod 半夜被杀,kubectl describe一看是OOMKilled;或者节点明明还有内存,Pod 却调度不上去。这两类问题的根源,几乎都在requests和limits没配对。这篇把这两个参数的真实含义、CPU 和内存的不同处理逻辑、以及 OOMKilled 的完整排查路径讲清楚,配置直接能抄。requests 和 limits 到底管什么一句话区分:requests(请求量):调度器用它决定「把 Pod 放到哪个节点」。它是预定,不是实际用量。节点上所有 Pod 的 requests 之和不能超过节点可分配资源。limits(上限):容器运行时用它限制「最多能用多少」。超了就会被限制(CPU)或杀掉(内存)。看一个典型配置:apiVersion:v1kind:Podmetadata:name:webspec:containers:-name:webimage:nginx:1.25resources:requests:cpu:250m# 预定 0.25 核memory:256Mi# 预定 256 MiBlimits:cpu:500m# 最多用 0.5 核memory:512Mi# 最多用 512 MiB,超了被 OOMKilled250m表示 0.25 个 CPU 核(m milli,1000m 1 核)。内存单位Mi是 2 进制的 MiB(1024×1024 字节),M才是 10 进制的 MB,别写混了。CPU 和内存的处理逻辑完全不同(这是关键)很多人栽在这里:以为超了 limit 都是被杀。其实两种资源的性质天差地别。CPU 是可压缩资源:超过 limit 不会被杀,而是被限流(throttling)——内核 CFS 调度器强行给你降速。表现是请求变慢、延迟飙高,但 Pod 不会挂。内存是不可压缩资源:超过 limit 无法「借一点还回来」,内核直接触发OOM Killer,把容器进程杀掉,Pod 状态变OOMKilled,然后按重启策略重启。所以:内存 limit 配小了 Pod 反复被杀重启;CPU limit 配小了 服务变慢但活着。这也是为什么很多人建议内存 requests 和 limits 设成相等,而 CPU 可以留出弹性空间。QoS 等级:谁先被牺牲当节点整体内存吃紧时,K8s 按 Pod 的QoS(服务质量)等级决定优先杀谁。等级由 requests/limits 的配置方式自动决定:Guaranteed(最高):每个容器的 requests limits(CPU、内存都要相等)。节点压力大时最后才被驱逐。Burstable(中间):设了 requests 但 requests limits,或只设了一部分。BestEffort(最低):啥都没设。节点一紧张第一个被杀。查看某个 Pod 的 QoS:kubectl get pod web-ojsonpath{.status.qosClass}# 输出 Guaranteed / Burstable / BestEffort生产建议:核心服务配成 Guaranteed(内存 requestslimits),让它在节点压力下活到最后;离线任务、批处理可以 Burstable 甚至 BestEffort,资源紧张时优先牺牲它们。OOMKilled 完整排查路径Pod 反复重启,怀疑内存问题时,按这个顺序查。第一步:确认真是 OOMKilledkubectl describe pod web在Last State一段里找:Last State: Terminated Reason: OOMKilled Exit Code: 137Exit Code: 137 128 9(SIGKILL),是内存被杀的典型信号。如果 Reason 是Error或别的,那就不是内存问题,别顺着这条路查下去。第二步:看它到底用了多少内存# 需要装了 metrics-serverkubectltoppod web--containers# NAME CPU(cores) MEMORY(bytes)# web 12m 498Mi ← 已经贴着 512Mi 的 limit 了如果内存长期贴着 limit,要么是 limit 配小了,要么是应用有内存泄漏。区分方法:limit 配小表现为启动后内存快速涨到 limit 就被杀;内存泄漏表现为内存随运行时间缓慢单调上涨,重启后又从低位开始爬。第三步:确认是容器超 limit 还是节点整体 OOM# 看节点事件,有没有系统级 OOMkubectl describenodenode-name|grep-ioom# 或直接上节点看内核日志dmesg-T|grep-ikilled process如果是单容器超自己的 limit,只有那个容器被杀;如果是节点整体内存耗尽,内核会挑 QoS 低的 Pod 杀——这时候要么加节点、要么把 BestEffort 的 Pod 收敛。第四步:给出修复确认是 limit 配小:根据kubectl top的真实峰值,把内存 limit 上调,留出 20%~30% 余量。确认是内存泄漏:limit 只能延缓被杀,治标;得回到应用层查泄漏(堆分析、连接未释放等)。一个能直接抄的生产配置核心 Web 服务,配成 Guaranteed:resources:requests:cpu:500mmemory:512Milimits:cpu:500m# CPU 也相等 → Guaranteed;若要弹性可调大,但会降级为 Burstablememory:512Mi# 内存必须 requestslimits,避免节点压力下被驱逐如果服务有明显的流量高峰、需要 CPU 弹性,那就接受 Burstable,让 CPU limit 大于 requests,但内存仍然保持 requestslimits:resources:requests:cpu:500mmemory:512Milimits:cpu:1000m# 允许突发到 1 核memory:512Mi# 内存不给弹性,守住不被 OOM 的底线另外用LimitRange给命名空间设默认值,防止有人提交没配 resources 的 BestEffort Pod 拖垮节点:apiVersion:v1kind:LimitRangemetadata:name:default-limitsspec:limits:-default:# 没写 limits 时的默认上限cpu:500mmemory:512MidefaultRequest:# 没写 requests 时的默认请求cpu:250mmemory:256Mitype:Container小结requests 管调度(预定)、limits 管运行时上限,两者含义完全不同。CPU 超 limit 只限流不杀,内存超 limit 直接 OOMKilled——内存 limit 宁可配大不要配小。QoS 三级 Guaranteed/Burstable/BestEffort 决定节点压力下谁先死,核心服务做成 Guaranteed(内存 requestslimits)。OOMKilled 排查四步:describe看 Exit Code 137 →top看真实用量 → 区分容器超限还是节点 OOM → 判断是 limit 小了还是内存泄漏。用LimitRange兜底,杜绝无资源配置的 Pod。一句话记忆点:CPU 配小了服务变慢,内存配小了服务被杀;核心服务内存 requests 一定等于 limits。