节点内存吃紧时 kubelet 会驱逐 Pod,但谁先走?这篇把工作节点的内存阈值调高,让四个不同 QoS、不同 requests 的 Pod 同时占内存,看 kubelet 真正驱逐了谁、message 里怎么解释,以及节点的 MemoryPressure 条件和污点什么时候出现、什么时候消失。
把工作节点 kubelet 的 evictionHard.memory.available 设成 6851Mi(比当时的可用量少 900 MiB)、evictionPressureTransitionPeriod 设成 20s,重启 kubelet;重启后 MemoryPressure=False a-besteffort BestEffort,将占 400 MiB,没有 requests b-burst-under Burstable,将占 400 MiB,requests 500 MiB(用量低于 requests) c-burst-over Burstable,将占 300 MiB,requests 50 MiB(用量超出 requests 250 MiB) d-guaranteed Guaranteed,将占 300 MiB,requests = limits = 400 MiB 四个 Pod 都在 Running、还没有分配内存时 memory.available = 7695 MiB(阈值 6851);同时对四个 Pod 执行 touch /tmp/go,它们各自分配 400 / 400 / 300 / 300 MiB(合计 1400 MiB,会越过阈值)
kind 写进 kubelet 配置的 evictionHard 只有三个磁盘类阈值(都是 0%),没有 memory.available,而 Docker 虚拟机有 8GB 内存,一般的实验压不到。这里把工作节点 kubelet 的 evictionHard.memory.available 设成 6851Mi(比当时的可用量 7752 MiB 少 900 MiB)并重启 kubelet,再让四个已经就绪的 Pod 同时分配内存:a BestEffort 400 MiB,b Burstable 400 MiB(requests 500 MiB),c Burstable 300 MiB(requests 50 MiB),d Guaranteed 300 MiB(requests = limits = 400 MiB)。memory.available 是节点容量减去节点 cgroup 的工作集。
+ 10.3s a-besteffort phase=Failed reason=Evicted + 12.6s c-burst-over phase=Failed reason=Evicted 节点 memory.available 每隔约 5 秒的采样(MiB):7695, 7695, 6694, 7008, 7008, 7008, 7008, 7008(阈值 6851)
可用内存从 7695 MiB 掉到 6694 MiB(低于阈值)。+10.3 秒 a 被驱逐,+12.6 秒 c 被驱逐,之后可用内存回到 7008 MiB,压力解除。b(实际用 400 MiB,低于 requests 500 MiB)和 d(用 300 MiB,低于 requests 400 MiB)全程没被动。规律:用量超过 requests 的 Pod 最先被驱逐,其中超出得多的更靠前;没超出的、尤其是 Guaranteed,留到最后。kubelet 一次只驱逐一个,再看有没有回到阈值之上。
a-besteffort:phase=Failed reason=Evicted;STATUS 列=Error
message=The node was low on resource: memory. Threshold quantity: 6851Mi, available: 6676700Ki. Container c was using 413908Ki, request is 0, has larger consumption of memory.
c-burst-over:phase=Failed reason=Evicted;STATUS 列=Error
message=The node was low on resource: memory. Threshold quantity: 6851Mi, available: 6854264Ki. Container c was using 311288Ki, request is 50Mi, has larger consumption of memory.
没有被驱逐的(仍在 Running):['b-burst-under', 'd-guaranteed']被驱逐的 Pod phase=Failed、reason=Evicted(STATUS 列显示 Error),对象还在,要靠控制器另建新 Pod 或者自己清理。message 写得很具体:The node was low on resource: memory. Threshold quantity: 6851Mi, available: 6676700Ki. Container c was using 413908Ki, request is 0, has larger consumption of memory.——阈值、当时的可用量、容器用量、requests 都在里面,这是排查“Pod 为什么被驱逐”的第一手资料。
第 8 秒 MemoryPressure 变成 True,同时节点出现污点 ['node.kubernetes.io/memory-pressure:NoSchedule'] 最后一次越过阈值大约在第 13 秒(最后一次驱逐);MemoryPressure 在第 40 秒回到 False(evictionPressureTransitionPeriod=20s,压力消失后条件还要保持 20 秒);此时 taints=[]
第 8 秒节点的 MemoryPressure 变成 True,同时出现污点 node.kubernetes.io/memory-pressure:NoSchedule;最后一次驱逐在第 13 秒,但条件到第 40 秒才回到 False——evictionPressureTransitionPeriod 设成 20 秒,压力消失后条件还要保持 20 秒,避免在阈值边缘来回抖动。
这个脚本更早的几次不干净的运行里还见到过三种现象,没有放进上面的结论:同时创建的 Pod 因为还没有统计数据被优先驱逐(一个 Guaranteed Pod 最先被驱逐,message 里没有容器用量);统计每 10 秒才刷新,两个都超出 requests 的 Pod 的先后在不同运行里互换过;重启 kubelet 时 BestEffort Pod 因节点有 MemoryPressure 条件被准入拒绝(Pod was rejected: The node had condition: [MemoryPressure])。