zoukankan      html  css  js  c++  java
  • K8S内存泄漏问题处理

    K8S内存泄漏问题处理

    问题描述

    我使用kubeadm 安装的K8S集群,随着pod增多,运行的时间久了,就会出现不能创建pod的情况。当kubectl describe pod,发现有 cannot allocate memory的错误信息。只有重启对应的服务器,才可以增加pod,异常提示才会消失。但继续随着时间的推移,pod的增多,该问题会继续出现。

    问题分析

    根据pod的异常信息,初步判断K8S可能造成了内存泄漏。
    使用 cat /sys/fs/cgroup/memory/kubepods/memory.kmem.slabinfo 在出现问题的node查看时,如果显示如下图,则说明没有存在内存泄漏:



    如果显示如下图,则说明存在内存泄漏:


    具体原因

    原因一句话:kmem导致内存泄露:

    ​ 内核对于每个 cgroup 子系统的的条目数是有限制的,限制的大小定义在 kernel/cgroup.c #L139,当正常在 cgroup 创建一个 group 的目录时,条目数就加1。我们遇到的情况就是因为开启了 kmem accounting 功能,虽然 cgroup 的目录删除了,但是条目没有回收。这样后面就无法创建65535个 cgroup 了。也就是说,在当前内核版本下,开启了 kmem accounting 功能,会导致 memory cgroup 的条目泄漏无法回收。

    Kmem在3.X内核的机器上存在内存泄漏

    cgroup 的 kmem account 特性在 3.x 内核上有内存泄露问题,如果开启了 kmem account 特性 会导致可分配内存越来越少,直到无法创建新 pod 或节点异常。

    几点解释:

    1. kmem account 是cgroup 的一个扩展,全称CONFIG_MEMCG_KMEM,属于机器默认配置,本身没啥问题,只是该特性在 3.10 的内核上存在漏洞有内存泄露问题,4.x的内核修复了这个问题。
    2. 因为 kmem account 是 cgroup 的扩展能力,因此runc、docker、k8s 层面也进行了该功能的支持,即默认都打开了kmem 属性
    3. 因为3.10 的内核已经明确提示 kmem 是实验性质,我们仍然使用该特性,所以这其实不算内核的问题,是 k8s 兼容问题。

    k8s在 1.9版本开启了对 kmem 的支持,因此 1.9 以后的所有版本都有该问题,但必须搭配 3.x内核的机器才会出问题。一旦出现会导致新 pod 无法创建,已有 pod不受影响,但pod 漂移到有问题的节点就会失败,直接影响业务稳定性。因为是内存泄露,直接重启机器可以暂时解决,但还会再次出现

    了解更多理论原因,可参考 https://blog.kelu.org/tech/2020/09/29/cgroup-kmem.html。

    总而言之:K8S 1.9版本及以后的版本,在内核是3.X的服务器上,都会出现内存泄漏的问题。在4.X的内核上,则修复了这个问题


    问题处理

    处理这个问题,可以升级服务器的内核。不过推荐使用下载kubelet和runc的源码,编译、再替换原来的。

    一、配置go语言环境

    我的机器系统是centos7.6。配置我参考了 http://docs.studygolang.com/doc/installhttps://www.cnblogs.com/biaopei/p/11883104.htmlhttps://studygolang.com/articles/7202。

    1.下载go源码包,版本要>1.16

    下载地址:https://golang.google.cn/dl/ 或者 https://studygolang.com/dl 官网 https://golang.org/dl/ 很可能访问通。我下载的是 go1.17.3.linux-amd64.tar.gz。

    2.解压源码包

    移除之前的源码包(如果有),并解压源码包到/usr/local:

    rm -rf /usr/local/go && tar -C /usr/local -xzf go1.17.3.linux-amd64.tar.gz
    

    3.配置golang的系统环境变量(选择一种配置方式即可)

    临时配置:下面这个配置,是临时的,服务器重启后,要重新执行。

    export PATH=$PATH:/usr/local/go/bin
    

    永久配置方式一:

    echo 'export PATH=$PATH:/usr/local/go/bin'>>/etc/profile  #配置系统变量  
    source /etc/profile 
    

    永久配置方案二:

    vi /etc/profile
    在文件中,追加如下环境变量
    export GOROOT=/usr/local/go  #设置为go安装的路径
    export GOPATH=$HOME/gocode   #默认安装包的路径
    export PATH=$PATH:$GOROOT/bin:$GOPATH/bin
    
    追加后,再执行如下命令,才能让环境变量生效。
    source /etc/profile
    

    环境变量生效后,验证了。

    go version

    二、编译runc

    参考 https://github.com/opencontainers/runchttps://www.cnblogs.com/zhangmingcheng/p/14309962.html

    1.源码下载:

    下载源码时,通过git下载,要有git工具,yum install git 安装。

     mkdir -p /data/Documents/src/github.com/opencontainers/
     cd /data/Documents/src/github.com/opencontainers/
     git clone https://github.com/opencontainers/runc  (或者 git clone git://github.com/opencontainers/runc)
    

    也可以手动从 https://github.com/opencontainers/runc 下载,再放入/data/Documents/src/github.com/opencontainers/ 中 )

    2.安装编译工具

    安装编译runc的工具libseccomp。其中,centos安装 libseccomp-devel,ubuntu安装 libseccomp-dev。

    yum install libseccomp-devel
    

    安装 gcc编译器:编译runc,还需要gcc编译器。

    yum -y install gcc gcc-c++ kernel-devel
    

    3.执行编译

    cd runc
    make BUILDTAGS='seccomp nokmem'
    

    执行make指令的时候,一定要加上 BUILDTAGS='seccomp nokmem'。这样,重新编译的runc才不会开启kmem属性,也就不会造成内存泄漏

    编译完成之后会在当前目录下看到一个runc的可执行文件

    三、编译kubelet

    1.源码下载:

    mkdir -p /root/k8s/
    cd /root/k8s/
    
    git clone https://github.com/kubernetes/kubernetes  或者  git clone git://github.com/kubernetes/kubernetes
    (这一步,建议从国内的码云下载,github会非常慢: git clone https://gitee.com/mirrors/Kubernetes.git)
    

    版本还原:根据自己安装的K8S版本,将源码还原到对应的版本:

     cd Kubernetes/
     git checkout v1.20.0
    

    2.编译

    GO111MODULE=on KUBE_GIT_TREE_STATE=clean KUBE_GIT_VERSION=v1.20.0 make kubelet GOFLAGS="-tags=nokmem"
    

    make编译的时候,必须要加上参数 GOFLAGS="-tags=nokmem"。这样编译的kubelet才不会开启kmem属性,也就不会导致内存泄漏。

    生成的kubelet二进制文件在生成的_output路径下的bin当中。

    四、替换runc和kubelet

    1.备份原有的kubelet和runc

    cp /usr/bin/kubelet /home/kubelet
    cp /usr/bin/runc /home/runc
    

    2.停止kubelet和docker,然后替换runc和kubelet

    systemctl stop docker
    systemctl stop kubelet
    
    cp kubelet /usr/bin/kubelet
    cp kubelet /usr/local/bin/kubelet
    cp runc /usr/bin/runc
    

    3.重启服务器,检查内存泄漏

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

    执行命令后,显示如下,则说明内存泄漏已修复。

  • 相关阅读:
    POJ 最小球覆盖 模拟退火
    POJ 1379 模拟退火
    PythonTip(2)
    PythonTip(1)
    LA 3353 最优巴士线路设计
    LA 4254 贪心
    判断分析
    因子分析——因子得分
    因子分析——应用
    因子分析——因子旋转
  • 原文地址:https://www.cnblogs.com/Fengyinyong/p/15672978.html
Copyright © 2011-2022 走看看