目录

kubernetes排错-cgroup 内存泄漏导致 pod 无法创建

创建 pod 时提示 cannot allocate memory: unknown 错误.

起因

kubernetes 无法创建 pod ,describe pod 事件提示如下:

Error: failed to create containerd task: OCI runtime create failed: container_linux.go:370: starting container process caused: process_linux.go:326: applying cgroup configuration for process caused: mkdir /sys/fs/cgroup/memory/kubepods/besteffort/podc8bfb77f-ac98-4c15-9161-f5c3eb4501dc/bill-daily: cannot allocate memory: unknown

到对应的节点,查看 kubelet 日志,也发现类似的日志:

E0307 02:07:02.444577 51168 pod_workers.go:191] Error syncing pod 18dcebc5-6c0d-4d7a-9a92-33186cd52baf ("order-auto-renew-1709748420-vkwtf_volc(18dcebc5-6c0d-4d7a-9a92-33186cd52baf)"), skipping: failed to ensure that the pod: 18dcebc5-6c0d-4d7a-9a92-33186cd52baf cgroups exist and are correctly applied: failed to create container for [kubepods besteffort pod18dcebc5-6c0d-4d7a-9a92-33186cd52baf] : mkdir /sys/fs/cgroup/memory/kubepods/besteffort/pod18dcebc5-6c0d-4d7a-9a92-33186cd52baf: cannot allocate memory

查阅网上相关资料,得知是 cgroup 内存泄露所致。

查看 cgroup 数量:

1
2
cat /proc/cgroups|grep memory
memory  8       65932    1

cgroup 的默认限制是 65536 , 通过这一系列检查可以发现, 当前占用的 cgroups 数量(第一个命令结果)大于65536, 新建容器会需要创建一个新的 cgroup 资源组, 系统认为已经没有 cgroup 资源可以分配了, 所以容器无法启动。

解决这个问题的办法:

  1. 重启机器(几乎所有资料都是重启节点解决)

  2. 升级内核版本(几乎所有资料都是升级到 4.x 以上,实际上 4.x 版本内核在特定的某些场景也会触发),之前我用 centos7 用的 5.12 以上版本没遇到过这个问题

  3. 使用脚本(重点介绍脚本,不用重启节点即可解决问题,需要做成 crontab 任务)

脚本

保存脚本名为 clean_slab_leak.sh (请自行命名),免责声明:生产环境慎用,我们的环境可以生效,不代表你们的环境可以,出了任何问题,概不负责

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/bin/bash

# opr_obj shoule be one of the cpu, memory
opr_obj=$1

ls /sys/fs/cgroup/${opr_obj}/user.slice/user-0.slice/|grep scope > /tmp/scope_user0_${opr_obj}
while read -r a ; do
   f=/sys/fs/cgroup/${opr_obj}/user.slice/user-0.slice/$a/cgroup.procs
   out=`cat $f`
   if [ -z "$out" ] ; then
         rmdir /sys/fs/cgroup/${opr_obj}/user.slice/user-0.slice/$a
         echo "delete empty cgroup success:" $f
   fi
done </tmp/scope_user0_${opr_obj}

ls /sys/fs/cgroup/${opr_obj}/system.slice > /tmp/scope_system_${opr_obj}
while read -r a ; do
   f=/sys/fs/cgroup/${opr_obj}/system.slice/$a/cgroup.procs
   out=`cat $f`
   if [ -z "$out" ] ; then
         rmdir /sys/fs/cgroup/${opr_obj}/system.slice/$a
         echo "delete empty cgroup success:" $f
   fi
done </tmp/scope_system_${opr_obj}

执行脚本:

1
2
bash clean_slab_leak.sh  memory
bash clean_slab_leak.sh  cpu

再次查看 cgroup 数量:

1
2
cat /proc/cgroups|grep memory
memory  8       1599    1

pod 也可以 run 起来了。然后再把上面的脚本做成 crontab 定时任务,定时执行即可。

总结

网上大部分教程都是提到重启节点,没有其他的处理办法,这里我们也是找了熟悉内核的同事,他们给的脚本,才无需重启节点解决问题。