limit 和容器里的进程到底是什么关系?只杀超出的那个进程,还是整个容器?哪些东西算内存?这篇读 cgroup 的 memory.max、memory.oom.group 与 memory.events,用 python、sh 子进程、内存型 emptyDir 和磁盘文件分别试一遍,最后验证不重启在线调大 limit。
容器启动 1 秒后读 cgroup:memory.max=67108864(64 MiB = limit 64Mi);memory.oom.group=1(1 表示 OOM 时整个 cgroup 里的进程一起杀);memory.high=max python 分配 100 MiB 后 5.5 秒内:lastState.terminated reason=OOMKilled exitCode=137(= 128 + 9,SIGKILL);restartCount=1;Pod 级 memory.events:low 0 high 0 max 38 oom 1 oom_kill 2 oom_group_kill 1 sock_throttled 0 又过 25 秒:restartCount=2;kubectl get STATUS=Running;Pod 级 memory.events:low 0 high 0 max 142 oom 3 oom_kill 6 oom_group_kill 3 sock_throttled 0
limit 64Mi 的容器,cgroup 里 memory.max=67108864(64 MiB),memory.high 是 max(没有软限制),还有 memory.oom.group=1。python 作 1 号进程一次性分配 100 MiB,容器的 lastState.terminated 是 reason: OOMKilled、exitCode: 137(128 + 9,被 SIGKILL)。内核的计数在 Pod 级 cgroup 的 memory.events 里:oom_kill、oom_group_kill 随着每次重启增加(25 秒后 oom_group_kill 3)。
OOM 之前容器里的进程:1 sh -c sleep 3600 & sleep 6; python -c 'imp | 12 sleep 3600 | 13 sleep 6 python 子进程分配 100 MiB:5.9 秒内容器 lastState.terminated reason=OOMKilled exitCode=137,restartCount=1——PID 1(sh)并没有自己超出内存,整个容器仍被 OOMKilled(memory.oom.group=1) 容器日志里没有“python 退出码”那行(sh 也被一起杀了):(空);Pod 级 memory.events:low 0 high 0 max 25 oom 1 oom_kill 4 oom_group_kill 1 sock_throttled 0
容器里 sh 是 1 号进程,后台还有两个 sleep,真正分配内存的是子进程 python。因为 memory.oom.group=1,一次 OOM 把整个容器的进程一起杀掉:memory.events 里 oom_kill 4(4 个进程)而 oom_group_kill 1;容器被当作 OOMKilled 重启,sh 里本该在 python 退出后打印的那一行日志没有出现。也就是说“让 1 号进程别超内存就行”是不够的,容器里所有进程的内存加在一起不能超过 limit。
写 100 MiB 到内存型 emptyDir(tmpfs),limit 64Mi:lastState.terminated 的变化:
+ 7.4s restartCount=1 reason=StartError exitCode=128 message=failed to create containerd task: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: container init was OOM-killed (memory limit too low?)
+21.5s restartCount=2 reason=StartError exitCode=128 message=failed to create containerd task: failed to create shim task: OCI runtime create failed: runc create failed: unable to start container process: container init was OOM-killed (memory limit too low?)
写 100 MiB 到磁盘型 emptyDir:restartCount=0;日志:dd 结束 0;memory.current=62 MiB(其中 file=60 MiB shmem=0 MiB anon=0 MiB);写了 100 MiB 却只占了 limit 以内,因为页缓存可以被回收写进内存型 emptyDir(medium: Memory,tmpfs)的文件算作容器的内存,而且重启不清除:往里写 100 MiB 的容器反复失败,StartError,message 是 container init was OOM-killed (memory limit too low?)——tmpfs 里的数据还占着内存,新容器刚 init 就被杀了。往磁盘型 emptyDir 写同样 100 MiB,memory.current 只有 62 MiB(file=60 MiB、shmem=0、anon=0),页缓存虽然也算内存,但可以被回收,没有触发 OOM。
容器每秒多分配 10 MiB,limit 64Mi;分配刚开始就执行 kubectl patch --subresource=resize 把 limit 调到 256Mi:pod/grow patched 20 秒后:restartCount=0;state=running;memory.max=268435456;日志最后一行:已分配 150 MiB spec 里 limit=256Mi;status.containerStatuses[0].resources.limits.memory=256Mi
容器每秒多分配 10 MiB,limit 64Mi。分配刚开始就用 kubectl patch --subresource=resize 把 limit 调到 256Mi:20 秒后 restartCount=0、memory.max=268435456,日志停在 已分配 150 MiB——超过了原来的 64Mi 也没被杀;spec 与 status.containerStatuses[0].resources 里的 limit 都是 256Mi。调大是安全的;调小而用量已经超过新值时会发生什么,这里没有测。