k8s

记一次k8s cgroup内存泄露问题修复

Posted by yaoice on January 4, 2020

环境

arm ubuntu平台

  • Kubernetes v1.14.6
  • Etcd 3.3.12
  • Docker 18.09.9
  • Kernel 4.4.131

现象

现象如这个issue里面所描述的https://github.com/kubernetes/kubernetes/issues/61937 , 还有复现方法

cgroup内存泄露, 现象关键字如下

SLUB: Unable to allocate memory on node -1
No space left on device

解决方法:

  1. 重新编译runc、kubelet禁用kmem accounting
  2. 内核禁用cgroup kmem(Kernel 3.10.0-1062.4.1.el7.x86_64支持)

kernel memory accounting机制简介

为了防止出现“fork bomb”,社区中就有提议通过linux内核限制cgroup中的kmem容量使用从而限制恶意进程的行为, kernel memory accounting机制为cgroup的内存限制增加了stack pages(例如新进程创建)、 slab pages(SLAB/SLUB分配器使用的内存)、sockets memory pressure、tcp memory pressure等

重编译runc、kubelet

安装go环境

下载go

# wget -c https://dl.google.com/go/go1.13.5.linux-arm64.tar.gz

解压至/usr/local目录

# tar xf go1.13.5.linux-arm64.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

验证go环境是否有效

source ~/.bashrc 
# go env
GO111MODULE="off"
GOARCH="arm64"
GOBIN=""
GOCACHE="/root/.cache/go-build"
GOENV="/root/.config/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="arm64"
GOHOSTOS="linux"

下载runc、kubernetes源码

下载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

编译, seccomp选项默认都会带上

apt-get install -y libseccomp-dev  pkg-config
make BUILDTAGS='seccomp nokmem'

runc可执行文件在当前目录, 更多编译选项查看README.md

查看runc版本

# ./runc -v
runc version 1.0.0-rc9
commit: d736ef14f0288d6993a1845745d6756cfc9ddd5a
spec: 1.0.1-dev

网上说升级docker-ce 到 18.09.1以上, runc默认把kmem accounting功能关闭了, 发现18.09.9版本并没有关闭

下载k8s源码

# mkdir -p /data/Documents/src/k8s.io/
# cd /data/Documents/src/k8s.io/
# git clone https://github.com/kubernetes/kubernetes
# cd kubernetes/
# git checkout v1.14.6
# GO111MODULE=off KUBE_GIT_TREE_STATE=clean KUBE_GIT_VERSION=v1.14.6 make kubelet GOFLAGS="-tags=nokmem"

编译v1.14.6版本的kubelet, 产物在当前目录_output/bin/kubelet

跨平台编译

make kubelet KUBE_BUILD_PLATFORMS=linux/arm64

验证

备份原有runc、kubelet

service kubelet stop
service docker stop

mv /usr/bin/kubelet /home/kubelet
mv /usr/bin/runc /home/runc

新版本替换

mv kubelet /usr/bin/kubelet
mv runc /usr/bin/runc

启动服务

service docker starrt
service kubelet start

不重启机器的话,需要删除原来所有的pod

查看新创建pod的cgroup memory kmem

pod uid、container id获取

kubectl get pod `kubectl get pod|grep test-hello| awk '{print $1}'` -o yaml |egrep "uid|container"

查看kmem.slabinfo

cat /sys/fs/cgroup/memory/kubepods/burstable/pod<pod-id>/<containerID>/memory.kmem.slabinfo
slabinfo - version: 2.1
# name            <active_objs> <num_objs> <objsize> <objperslab> <pagesperslab> : tunables <limit> <batchcount> <sharedfactor> : slabdata <active_slabs> <num_slabs> <sharedavail>

没有禁用kmem的情况下, 这下面会有数值的; 禁用kmem的情况下, 在内核3.10.0-862.11.6.el7.x86_64版本下会显示Input/output error, 在内核4.4.131版本下显示的和内核3.10.0-862.11.6.el7.x86_64不一样, 它也没有数值

所以再查看kmem.usage_in_bytes, 以这个为标准

cat /sys/fs/cgroup/memory/kubepods/burstable/pod<pod-id>/<containerID>/memory.kmem.usage_in_bytes
0

显示为0, 说明有效

参考链接