服務器環境
系統版本:CentOS Linux release 7.4.1708 (Core)
系統內核:3.10.0-1127.el7.x86_64
kubernetes版本:v1.10.7
docker版本: 17.09.1-ce
問題記錄
k8s創建pod在某台節點上無法創建並報錯:
unable to ensure pod container exists: faild to create container for /kubepods/burstable/podf1242523-2135abf-200cfcce08 : mkdir /sys/fs/cgroup/memory/kubepods/burstable/podf1242523-2135abf-200cfcce08: no space left on device
在節點上創建容器失敗
docker: Error response from daemon: OCI runtime create failed: container_linux.go:348: starting container process caused "process_linux.go:279: applying cgroup configuration for process caused \"mkdir /sys/fs/cgroup/memory/docker/f88b5f802a5ddb0fc0b072f01e481911c3fd736092565f6b9047d3c1d0d08e26: cannot allocate memory\"": unknown.
在網上查詢發現是cgroup泄露導致cagroup memory在容器被刪除后沒有釋放,導致memory被占滿
騰訊容器雲解決方案地址:https://tencentcloudcontainerteam.github.io/2018/12/29/cgroup-leaking/
網上復現文章地址:http://www.linuxfly.org/kubernetes-19-conflict-with-centos7/?from=groupmessage
docker社區:https://github.com/moby/moby/issues/29638
kubernetes社區:https://github.com/kubernetes/kubernetes/issues/70324
解決方案
因為這個K8S集群本身是作為PASS平台的底層集群,如果升級集群可能會導致和PASS平台兼容出現問題,所以采取清理出現問題的節點把pod飄移出去盡量小的影響業務,然后重啟服務器來釋放。
規避方案
如果你用的低版本內核(比如 CentOS 7 v3.10 的內核)並且不方便升級內核,可以通過不開啟 kmem accounting 來實現規避,但會比較麻煩。
kubelet 和 runc 都會給 memory cgroup 開啟 kmem accounting,所以要規避這個問題,就要保證kubelet 和 runc 都別開啟 kmem accounting,下面分別進行說明:
runc
runc 在合並 這個PR 之后創建的容器都默認開啟了 kmem accounting,后來社區也注意到這個問題,並做了比較靈活的修復, PR 1921 給 runc 增加了 “nokmem” 編譯選項,缺省的 release 版本沒有使用這個選項, 自己使用 nokmem 選項編譯 runc 的方法:
1 cd $GO_PATH/src/github.com/opencontainers/runc/ 2 make BUILDTAGS="seccomp nokmem"
docker-ce v18.09.1 之后的 runc 默認關閉了 kmem accounting,所以也可以直接升級 docker 到這個版本之后。
kubelet
如果是 1.14 版本及其以上,可以在編譯的時候通過 build tag 來關閉 kmem accounting:
KUBE_GIT_VERSION=v1.14.1 ./build/run.sh make kubelet GOFLAGS="-tags=nokmem"
如果是低版本需要修改代碼重新編譯。kubelet 在創建 pod 對應的 cgroup 目錄時,也會調用 libcontianer 中的代碼對 cgroup 做設置,在 pkg/kubelet/cm/cgroup_manager_linux.go
的 Create
方法中,會調用 Manager.Apply
方法,最終調用 vendor/github.com/opencontainers/runc/libcontainer/cgroups/fs/memory.go
中的 MemoryGroup.Apply
方法,開啟 kmem accounting。這里也需要進行處理,可以將這部分代碼注釋掉然后重新編譯 kubelet。