K8s 报错cannot allocate memory或者no space left on device,修复Kubernetes内存泄露问题

问题描述


Kubernetes集群运行日久以后,有的Node无法再新建Pod,并且出现如下错误,当重启服务器之后,才可以恢复正常使用。查看Pod状态的时候会出现以下报错。

applying cgroup … caused: mkdir …no space left on device
或者在describe pod的时候出现cannot allocate memory

这时候你的Kubernetes集群可能就存在内存泄露的问题了,当创建的Pod越多的时候内存会泄露的越多,越快。

具体查看是否存在内存泄露


cat /sys/fs/cgroup/memory/kubepods/memory.kmem.slabinfo
当出现cat: /sys/fs/cgroup/memory/kubepods/memory.kmem.slabinfo: Input/output error则说明不存在内存泄露的情况
如果存在内存泄露会出现
slabinfo - version: 2.1
# name            <active_objs> <num_objs> <objsize> <objperslab> <pagesperslab> : tunables <limit> <batchcount> <sharedfactor> : slabdata <active_slabs> <num_slabs> <sharedavail>

解决方案


  1. 解决方法思路:关闭runc和kubelet的kmem,因为升级内核的方案改动较大,此处不采用。
  2. kmem导致内存泄露的原因:
    内核对于每个cgroup子系统的的条目数是有限制的,限制的大小定义在kernel/cgroup.c #L139,当正常在cgroup创建一个group的目录时,条目数就加1。我们遇到的情况就是因为开启了kmem accounting功能,虽然cgroup的目录删除了,但是条目没有回收。这样后面就无法创建65535个cgroup了。也就是说,在当前内核版本下,开启了kmem accounting功能,会导致memory cgroup的条目泄漏无法回收。

具体实现


需要重新编译runc


1、配置go语言环境

wget https://dl.google.com/go/go1.12.9.linux-amd64.tar.gz
tar xf go1.12.9.linux-amd64.tar.gz -C /usr/local/

写入bashrc
vim ~/.bashrc 
export GOPATH="/data/Documents"
export GOROOT="/usr/local/go"
export PATH="$GOROOT/bin:$GOPATH/bin:$PATH"
export GO111MODULE=off

验证
source ~/.bashrc 
go env

2、下载runc源码码

mkdir -p /data/Documents/src/github.com/opencontainers/
cd /data/Documents/src/github.com/opencontainers/
git clone https://github.com/opencontainers/runc
cd runc/
git checkout v1.0.0-rc9  # 切到v1.0.0-rc9 tag

3、编译

安装编译组件

sudo yum install libseccomp-devel
make BUILDTAGS='seccomp nokmem'

编译完成之后会在当前目录下看到一个runc的可执行文件,等kubelet编译完成之后会将其替换

编译kubelet


1、下载Kubernetes源码

mkdir -p /root/k8s/
cd /root/k8s/
git clone https://github.com/kubernetes/kubernetes
cd kubernetes/
git checkout v1.15.3

2、制作编译环境的镜像(Dockerfile如下)

FROM centos:centos7.3.1611

ENV GOROOT /usr/local/go
ENV GOPATH /usr/local/gopath
ENV PATH /usr/local/go/bin:$PATH

RUN yum install rpm-build which where rsync gcc gcc-c++ automake autoconf libtool make -y \
    && curl -L https://studygolang.com/dl/golang/go1.12.9.linux-amd64.tar.gz | tar zxvf - -C /usr/local

3、在制作好的go环境镜像中来进行编译kubelet

docker run  -it --rm   -v /root/k8s/kubernetes:/usr/local/gopath/src/k8s.io/kubernetes   build-k8s:centos-7.3-go-1.12.9-k8s-1.15.3   bash
cd /usr/local/gopath/src/k8s.io/kubernetes
#编译
GO111MODULE=off KUBE_GIT_TREE_STATE=clean KUBE_GIT_VERSION=v1.15.3 make kubelet GOFLAGS="-tags=nokmem"

替换原有的runc和kubelet


1、将原有runc和kubelet备份

mv /usr/bin/kubelet /home/kubelet
mv /usr/bin/docker-runc /home/docker-runc

2、停止Docker和kubelet

systemctl stop docker
systemctl stop kubelet

3、将编译好的runc和kubelet进行替换

cp kubelet /usr/bin/kubelet
cp kubelet /usr/local/bin/kubelet
cp runc /usr/bin/docker-runc

4、检查kmem是否关闭前需要将此节点的Pod杀掉重启或者重启服务器,当结果为0时成功

cat /sys/fs/cgroup/memory/kubepods/burstable/memory.kmem.usage_in_bytes

5、是否还存在内存泄露的情况

cat /sys/fs/cgroup/memory/kubepods/memory.kmem.slabinfo

版权声明:本文为作者原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原创文章,作者:老C,如若转载,请注明出处:https://www.code404.icu/1239.html

发表评论

登录后才能评论