记录一次kubernetes集群cgroup泄露问题

avatar 2022年8月26日18:12:59 评论 649 次浏览

服务器环境

  系统版本:CentOS Linux release 7.4.1708 (Core)

  系统内核:3.10.0-1127.el7.x86_64

  kubernetes版本:v1.10.7

  docker版本: 17.09.1-ce

问题记录

k8s创建pod在某台节点上无法创建并报错:

unable to ensure pod container exists: faild to create container for /kubepods/burstable/podf1242523-2135abf-200cfcce08 : mkdir /sys/fs/cgroup/memory/kubepods/burstable/podf1242523-2135abf-200cfcce08: no space left on device

在节点上创建容器失败

docker: Error response from daemon: OCI runtime create failed: container_linux.go:348: starting container process caused "process_linux.go:279: applying cgroup configuration for process caused \"mkdir /sys/fs/cgroup/memory/docker/f88b5f802a5ddb0fc0b072f01e481911c3fd736092565f6b9047d3c1d0d08e26: cannot allocate memory\"": unknown.

在网上查询发现是cgroup泄露导致cagroup memory在容器被删除后没有释放,导致memory被占满

腾讯容器云解决方案地址:https://tencentcloudcontainerteam.github.io/2018/12/29/cgroup-leaking/

网上复现文章地址:http://www.linuxfly.org/kubernetes-19-conflict-with-centos7/?from=groupmessage

docker社区:https://github.com/moby/moby/issues/29638

kubernetes社区:https://github.com/kubernetes/kubernetes/issues/70324

解决方案

因为这个K8S集群本身是作为PASS平台的底层集群,如果升级集群可能会导致和PASS平台兼容出现问题,所以采取清理出现问题的节点把pod飘移出去尽量小的影响业务,然后重启服务器来释放。

规避方案

如果你用的低版本内核(比如 CentOS 7 v3.10 的内核)并且不方便升级内核,可以通过不开启 kmem accounting 来实现规避,但会比较麻烦。

kubelet 和 runc 都会给 memory cgroup 开启 kmem accounting,所以要规避这个问题,就要保证kubelet 和 runc 都别开启 kmem accounting,下面分别进行说明:

runc

runc 在合并 这个PR 之后创建的容器都默认开启了 kmem accounting,后来社区也注意到这个问题,并做了比较灵活的修复, PR 1921 给 runc 增加了 “nokmem” 编译选项,缺省的 release 版本没有使用这个选项, 自己使用 nokmem 选项编译 runc 的方法:

cd $GO_PATH/src/github.com/opencontainers/runc/
make BUILDTAGS="seccomp nokmem"

docker-ce v18.09.1 之后的 runc 默认关闭了 kmem accounting,所以也可以直接升级 docker 到这个版本之后。

kubelet

如果是 1.14 版本及其以上,可以在编译的时候通过 build tag 来关闭 kmem accounting:

 KUBE_GIT_VERSION=v1.14.1 ./build/run.sh make kubelet GOFLAGS="-tags=nokmem"

如果是低版本需要修改代码重新编译。kubelet 在创建 pod 对应的 cgroup 目录时,也会调用 libcontianer 中的代码对 cgroup 做设置,在 pkg/kubelet/cm/cgroup_manager_linux.goCreate 方法中,会调用 Manager.Apply 方法,最终调用 vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/memory.go 中的 MemoryGroup.Apply 方法,开启 kmem accounting。这里也需要进行处理,可以将这部分代码注释掉然后重新编译 kubelet。

avatar

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: