现象

测试环境中一台机器上的pod转移到其他节点,涉及大量的容器创建和销毁。偶尔会遇到 memory cgroup 无法创建的问题。体现在日志上如下:

Dec 22 08:00:00  kubelet: E1222 08:00:00.498519   15276 pod_workers.go:190] Error syncing pod 568af631-f663-4475-86f2-fa6bf1612659 ("crawler-api-7948c5bc95-5kgp8_brown(568af631-f663-4475-86f2-fa6bf1612659)"), skipping: failed to ensure that the pod: 568af631-f663-4475-86f2-fa6bf1612659 cgroups exist and are correctly applied: failed to create container for [kubepods besteffort pod568af631-f663-4475-86f2-fa6bf1612659] : mkdir /sys/fs/cgroup/memory/kubepods/besteffort/pod568af631-f663-4475-86f2-fa6bf1612659: cannot allocate memory
Dec 22 08:00:01  dockerd: time="2020-12-22T08:00:01.237276548+08:00" level=error msg="Handler for POST /containers/394d2d6299fa46be7056e82ac9504fd1488b0af87fdadbe9b907d882eb7b3b8f/start returned error: OCI runtime create failed: container_linux.go:349: starting container process caused \"process_linux.go:297: applying cgroup configuration for process caused \\\"mkdir /sys/fs/cgroup/memory/kubepods/besteffort/pod2d78b9c8-6696-46ac-8326-aa9477f1b1d0/394d2d6299fa46be7056e82ac9504fd1488b0af87fdadbe9b907d882eb7b3b8f: cannot allocate memory\\\"\": unknown"

手动创建时,错误如下:

mkdir: cannot create directory '/sys/fs/cgroup/memory/test': Cannot allocate memory
原因

3.10.x 内核的 memory cgroup 实现中 kmem accounting 相关是 alpha 特性,实现上由许多 BUG 。具体我们的场景,开启 kmem accouting 后有两个问题:

SLAB 泄露,具体可以参见 Try to Fix Two Linux Kernel Bugs While Testing TiDB Operator in K8s | TiDB
memory cgroup 泄露,删除后不会被完全回收。又因为内核对 memory cgroup 总数有 65535 总数限制,频繁创建删除开启了 kmem 的 cgroup ,会让内核无法再创建新的 memory cgroup ,出现上面的错误。
问题复现与修复验证脚本
“SLAB” 泄露
可以观察 k8s pods 创建过程中,系统 slab 情况。若稳定增长,则存在泄露。

watch -n 1 'ls /sys/kernel/slab  | wc -l'

当然也可以手动创建 cgroup 并开启 kmem 后删除来模拟。

此问题,在 CentOS 7.6 的当前最新内核 (3.10.0-1062.12.1.el7.x86_64) 中已修复。

Memory Cgroup 泄露
先将所有的 memory cgroup 耗尽:

mkdir /sys/fs/cgroup/memory/test
for i in `seq 1 65535`;do mkdir /sys/fs/cgroup/memory/test/test-${i}; done

在其中一个 cgroup 中打开 kmem ,然后删除:

for n in 1 -1; do
echo $n > /sys/fs/cgroup/memory/test/test-1/memory.kmem.limit_in_bytes
done
echo $$ > /sys/fs/cgroup/memory/test/test-1/cgroup.procs
echo $$ > /sys/fs/cgroup/memory/cgroup.procs
rmdir /sys/fs/cgroup/memory/test/test-1

尝试重新创建:

mkdir /sys/fs/cgroup/memory/test/test-1

此问题,在 CentOS 7.6 的当前最新内核 (3.10.0-1062.12.1.el7.x86_64) 中还未修复,需要开使用 “cgroup.memory=nokmem” 内核参数全局禁用 kmem 来避免。

总结

若系统发行版本无法升级,可以通过以下方案解决:

方法一
业务层不使用 kmem 特性,比如 kubelet 需要使用 nokmem BUILDTAG 来编译,具体可以参见 https://pingcap.com/blog/try-to-fix-two-linux-kernel-bugs-while-testing-tidb-operator-in 。

方法二
但不是所有的场景都方便关闭 kmem 使用。比如容器中创建 k8s 集群测试。此时,可以在内核层全局使用 “cgroup.memory=nokmem” 参数关闭。

注意,内核需要升级到 CentOS 7.6 的最新版(当前为: 3.10.0-1062.12.1.el7.x86_64) ,以修复 SLAB 泄露问题。

若系统发行版可以升级,建议直接升级到 CentOS 8 。内核为 4.x 版本,cgroup 相关实现各方面都健壮许多。

从 RedHat 的 Issue 中,据说问题在 7.8 的 kernel-3.10.0-1075.el7 最终修复了。等 CentOS 7.8 发布后,也是一个选择。

参考文章

https://zhuanlan.zhihu.com/p/106757502
https://help.mulesoft.com/s/article/RTF-How-to-Resolve-the-Cgroup-Memory-Leaking-Issue-in-Runtime-Fabric

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐