修復k8s內存泄露問題


 

問題描述

  1. 當 k8s 集群運行日久以后,有的 node 無法再新建 pod,並且出現如下錯誤,當重啟服務器之后,才可以恢復正常使用。查看 pod 狀態的時候會出現以下報錯。
    applying cgroup … caused: mkdir …no space left on device

或者在 describe pod 的時候出現 cannot allocate memory。

這時候你的 k8s 集群可能就存在內存泄露的問題了,當創建的 pod 越多的時候內存會泄露的越多,越快。

  1. 具體查看是否存在內存泄露
    $ 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 的條目泄漏無法回收。

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"
  1. 替換原有的 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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM