为什么要升级内核
本文中所使用到的OS为Ubuntu 16.04,用户均为root用户。升级内核为必须条件。
在低版本的内核中会出现一下很让人恼火的Bug,时不时来一下,发作时候会导致整个OS Hang住无法执行任何命令。
现象如下:
kernel:unregister_netdevice: waiting for lo to become free. Usage count = 1
关于这个Bug,你可以从以下地方追踪到:
还有老哥放出了重现这个Bug的代码:https://github.com/fho/docker-samba-loop。
而根据我实际的实验(采坑)下来,这个问题我花费了差不多1个多月的时间先后尝试了内核版本3.10、4.4、4.9、4.12、4.14、4.15版本,均会不同程度的复现上述Bug,而一旦触发并无他法,只能重启(然后祈祷不要再次触发)。
实在是让人寝食难安,睡不踏实,直到我遇到了内核4.17,升级完毕之后,从6月到现在。重来没有复现过。似乎可以认为该Bug已经修复了。故而强烈建议升级内核到4.17+。
这篇文章的全称应该叫:[在某些内核版本上,cgroup 的 kmem account 特性有内存泄露问题],如果你遇到过 pod 的 "cannot allocated memory"报错,node 内核日志的“SLUB: Unable to allocate memory on node -1”报错,那么恭喜你中招了。
这个问题在 pingcap 的文章和腾讯云的官方修复都发过,原因也讲的很清楚,不过因为版本差异,文章里的方法有所变动,这里做下总结
3.10的kernel 问题太多了。也是k8s 支持的最低版本。
为什么要升级内核?
由于Docker 在CentOS系统中需要安装在 CentOS 7 64 位的平台,并且内核版本不低于 3.10;CentOS 7.× 满足要求的最低内核版本要求,但由于 CentOS 7默认内核版本比较低,部分功能(如 overlay2 存储层驱动)无法使用,并且部分功能可能不太稳定。所以建议大家升级到最新的稳定内核版本。
今天k8s集群服务器突然出现这个警告错误,网上查了一下,建议升级内核版本
kernel:unregister_netdevice: waiting for lo to become free. Usage count = 1
原因
一句话总结:
cgroup 的 kmem account 特性在 3.x 内核上有内存泄露问题,如果开启了 kmem account 特性会导致可分配内存越来越少,直到无法创建新 pod 或节点异常。
几点解释:
其他细节原因下面会解释
既然是 3.x 的问题,直接升级内核到 4.x 及以上即可,内核问题解释:
这种方式的缺点是:
因为 2 和 3 的原因,我们没有选择升级内核,决定使用其他方案
[root@jenkins-master ~]# cat /etc/redhat-release
CentOS Linux release 7.4.1708 (Core)
[root@jenkins-master ~]# uname -r
3.10.0-693.el7.x86_64
7.4版本操作系统升级 rpms.tar
# 升级Master节点和Node节点内核至 4.19.20-1.el7.x86_64 版本,步骤:
# 上传rpm安装包,执行rpm安装
yum localinstall -y kernel-ml-* --skip-broken
# 修改内核启动参数
sed -i 's/saved/0/g' /etc/default/grub
# 重建grub
grub2-mkconfig -o /boot/grub2/grub.cfg
# 检查grub启动参数
grep "^menuentry" /boot/grub2/grub.cfg
menuentry 'CentOS Linux (4.19.20-1.el7.x86_64) 7 (Core)' --class centos --class gnu-linux --class gnu --class os --unrestricted $menuentry_id_option 'gnulinux-4.19.20-1.el7.x86_64-advanced-918bc4e0-3f40-4ee7-89f3-0acbd5e60266'
# awk -F\' '$1=="menuentry " {print i++ " : " $2}' /etc/grub2.cfg
0 : CentOS Linux (4.19.20-1.el7.x86_64) 7 (Core)
1 : CentOS Linux (3.10.0-1127.el7.x86_64) 7 (Core)
2 : CentOS Linux (0-rescue-ce96d80aad324672909914d327a2d91c) 7 (Core)
# 重启服务器
# sudo reboot
Kernel requirement for Containerd
https://github.com/containerd/containerd/blob/master/README.md
Kernel requirement for Kubernetes
Kernel 3.10 is just the minimum requirement, you can see 4+ or even 5+ is also supported by Kubernetes. The latest CentOS7.6 releases on kernel 3.10. No any plan for kernel 4. While some of the kernel feature like ‘kernel memory accounting (kmem)’ that Kubernetes enables by default is only experimental in kernel 3, but stable on kernel 4. Docker provides the kernel version check and disables kmem if kernel is 3.x. But I can’t find any flags to disable kmem from Kubernetes. One possible way to fix it is to rebuild Kubernetes from source code with build flag to disable the kmem accounting from runc. https://github.com/kubernetes/kubernetes/blob/master/cmd/kubeadm/app/util/system/types_unix.go
Kernel 3.10只是最低要求,你可以看到Kubernetes也支持4+甚至5+。内核 3.10 上最新的 CentOS7.6 版本。没有针对内核 4 的任何计划。虽然 Kubernetes 默认启用的一些内核功能,例如“内核内存记帐 (kmem)”,仅在内核 3 中处于试验阶段,但在内核 4 上是稳定的。
Docker 提供内核版本检查并在以下情况下禁用 kmem内核是 3.x。但是我找不到从 Kubernetes 禁用 kmem 的任何标志。修复它的一种可能方法是使用构建标志从源代码重建 Kubernetes,以禁用来自 runc 的 kmem 记帐。
Other situations
其他情况
The Analysis of Kernel & OS for running Kubernetes - Gary's Understandings
当 k8s 集群运行日久以后,有的 node 无法再新建 pod,并且出现如下错误,当重启服务器之后,才可以恢复正常使用。查看 pod 状态的时候会出现以下报错。
applying cgroup … caused: mkdir …no space left on device
或者在 describe pod 的时候出现 cannot allocate memory。
这时候你的 k8s 集群可能就存在内存泄露的问题了,当创建的 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 : tunables : slabdata
解决方法思路:关闭 runc 和 kubelet 的 kmem,因为升级内核的方案改动较大,此处不采用。
kmem 导致内存泄露的原因:
内核对于每个 cgroup 子系统的的条目数是有限制的,限制的大小定义在 kernel/cgroup.c #L139,当正常在 cgroup 创建一个 group 的目录时,条目数就加 1。我们遇到的情况就是因为开启了 kmem accounting 功能,虽然 cgroup 的目录删除了,但是条目没有回收。这样后面就无法创建 65535 个 cgroup 了。也就是说,在当前内核版本下,开启了 kmem accounting 功能,会导致 memory cgroup 的条目泄漏无法回收。
2.1 编译 runc
配置 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
下载 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
编译
# 安装编译组件
$ sudo yum install libseccomp-devel
$ make BUILDTAGS='seccomp nokmem'
# 编译完成之后会在当前目录下看到一个runc的可执行文件,等kubelet编译完成之后会将其替换
2.2 编译 kubelet
下载 kubernetes 源码
$ mkdir -p /root/k8s/
$ cd /root/k8s/
$ git clone https://github.com/kubernetes/kubernetes
$ cd kubernetes/
$ git checkout v1.15.3
制作编译环境的镜像(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
在制作好的 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
将原有 runc 和 kubelet 备份
$ mv /usr/bin/kubelet /home/kubelet
$ mv /usr/bin/docker-runc /home/docker-runc
停止 docker 和 kubelet
$ systemctl stop docker
$ systemctl stop kubelet
将编译好的 runc 和 kubelet 进行替换
$ cp kubelet /usr/bin/kubelet
$ cp kubelet /usr/local/bin/kubelet
$ cp runc /usr/bin/docker-runc
检查 kmem 是否关闭前需要将此节点的 pod 杀掉重启或者重启服务器,当结果为 0 时成功
$ cat /sys/fs/cgroup/memory/kubepods/burstable/memory.kmem.usage_in_bytes
检查是否还存在内存泄露的情况
$ cat /sys/fs/cgroup/memory/kubepods/memory.kmem.slabinfo