Kubernetes上生產(chǎn)環(huán)境后,99%都會遇到這2個(gè)故障
隨著微服務(wù)的不斷推進(jìn),使用 k8s 集群越來越多,越來越深入,隨之而來會遇到一系列的問題,本文向大家介紹實(shí)際使用 k8s 遇到的一些問題以及解決方法。
1問題一:修復(fù) K8S 內(nèi)存泄露問題
問題描述
當(dāng) k8s 集群運(yùn)行日久以后,有的 node 無法再新建 pod,并且出現(xiàn)如下錯(cuò)誤,當(dāng)重啟服務(wù)器之后,才可以恢復(fù)正常使用。查看 pod 狀態(tài)的時(shí)候會出現(xiàn)以下報(bào)錯(cuò)。
applying?cgroup?…?caused:?mkdir?…no?space?left?on?device
或者在 describe pod 的時(shí)候出現(xiàn) cannot allocate memory。
這時(shí)候你的 k8s 集群可能就存在內(nèi)存泄露的問題了,當(dāng)創(chuàng)建的 pod 越多的時(shí)候內(nèi)存會泄露的越多,越快。
具體查看是否存在內(nèi)存泄露
$?cat?/sys/fs/cgroup/memory/kubepods/memory.kmem.slabinfo
當(dāng)出現(xiàn) cat: /sys/fs/cgroup/memory/kubepods/memory.kmem.slabinfo: Input/output error 則說明不存在內(nèi)存泄露的情況 如果存在內(nèi)存泄露會出現(xiàn)
slabinfo?-?version:?2.1
#?name?????????????????:?tunables????:?slabdata???
解決方案
解決方法思路:關(guān)閉 runc 和 kubelet 的 kmem,因?yàn)樯墐?nèi)核的方案改動(dòng)較大,此處不采用。
kmem 導(dǎo)致內(nèi)存泄露的原因:
內(nèi)核對于每個(gè) cgroup 子系統(tǒng)的的條目數(shù)是有限制的,限制的大小定義在 kernel/cgroup.c #L139,當(dāng)正常在 cgroup 創(chuàng)建一個(gè) group 的目錄時(shí),條目數(shù)就加 1。我們遇到的情況就是因?yàn)殚_啟了 kmem accounting 功能,雖然 cgroup 的目錄刪除了,但是條目沒有回收。這樣后面就無法創(chuàng)建 65535 個(gè) cgroup 了。也就是說,在當(dāng)前內(nèi)核版本下,開啟了 kmem accounting 功能,會導(dǎo)致 memory cgroup 的條目泄漏無法回收。
2.1 編譯 runc
配置 go 語言環(huán)境
$?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
#?驗(yàn)證
$?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'
#?編譯完成之后會在當(dāng)前目錄下看到一個(gè)runc的可執(zhí)行文件,等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
制作編譯環(huán)境的鏡像(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 環(huán)境鏡像中來進(jìn)行編譯 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 進(jìn)行替換
$?cp?kubelet?/usr/bin/kubelet
$?cp?kubelet?/usr/local/bin/kubelet
$?cp?runc?/usr/bin/docker-runc
檢查 kmem 是否關(guān)閉前需要將此節(jié)點(diǎn)的 pod 殺掉重啟或者重啟服務(wù)器,當(dāng)結(jié)果為 0 時(shí)成功
$?cat?/sys/fs/cgroup/memory/kubepods/burstable/memory.kmem.usage_in_bytes
檢查是否還存在內(nèi)存泄露的情況
$?cat?/sys/fs/cgroup/memory/kubepods/memory.kmem.slabinfo2問題二:k8s 證書過期問題的兩種處理方法
前情提要
公司測試環(huán)境的 k8s 集群使用已經(jīng)很長時(shí)間了,突然有一天開發(fā)聯(lián)系我說 k8s 集群無法訪問,開始以為是測試環(huán)境的機(jī)器磁盤空間不夠了,導(dǎo)致組件異常或者把開發(fā)使用的鏡像自動(dòng)清理掉了,但是當(dāng)?shù)巧蠙C(jī)器去查驗(yàn)的時(shí)候發(fā)現(xiàn)不是這個(gè)原因。當(dāng)時(shí)覺得也很疑惑。因?yàn)殚_發(fā)環(huán)境使用人數(shù)較少,不應(yīng)該會出問題,所以就去查驗(yàn) log 的相關(guān)報(bào)錯(cuò)信息。
問題現(xiàn)象
出現(xiàn) k8s api 無法調(diào)取的現(xiàn)象,使用 kubectl 命令獲取資源均返回如下報(bào)錯(cuò):
$?Unable?to?connect?to?the?server:?x509:?certificate?has?expired?or?is?not?yet?valid
經(jīng)網(wǎng)上搜索之后發(fā)現(xiàn)應(yīng)該是 k8s 集群的證書過期了,使用命令排查證書的過期時(shí)間
$?kubeadm?alpha?certs?check-expiration
發(fā)現(xiàn)確實(shí)是證書過期了
相關(guān)介紹以及問題解決
因?yàn)槲覀兪鞘褂?kubeadm 部署的 k8s 集群,所以更新起證書也是比較方便的,默認(rèn)的證書時(shí)間有效期是一年,我們集群的 k8s 版本是 1.15.3 版本是可以使用以下命令來更新證書的,但是一年之后還是會到期,這樣就很麻煩,所以我們需要了解一下 k8s 的證書,然后我們來生成一個(gè)時(shí)間很長的證書,這樣我們就可以不用去總更新證書了。
$?kubeadm?alpha?certs?renew?all?--config=kubeadm.yaml
$?systemctl?restart?kubelet
$?kubeadm?init?phase?kubeconfig?all?--config?kubeadm.yaml
#?然后將生成的配置文件替換,重啟?kube-apiserver、kube-controller、kube-scheduler、etcd?這4個(gè)容器即可
另外 kubeadm 會在控制面板升級的時(shí)候自動(dòng)更新所有證書,所以使用 kubeadm 搭建的集群最佳的做法是經(jīng)常升級集群,這樣可以確保你的集群保持最新狀態(tài)并保持合理的安全性。但是對于實(shí)際的生產(chǎn)環(huán)境我們可能并不會去頻繁的升級集群,所以這個(gè)時(shí)候我們就需要去手動(dòng)更新證書。
下面我們通過調(diào)用 k8s 的 api 來實(shí)現(xiàn)更新一個(gè) 10 年的證書
首先在 /etc/kubernetes/manifests/kube-controller-manager.yaml 文件加入配置
spec:
??containers:
??-?command:
????-?kube-controller-manager
????#?設(shè)置證書有效期為?10年
????-?--experimental-cluster-signing-duration=87600h
????-?--client-ca-file=/etc/kubernetes/pki/ca.crt
修改完成后 kube-controller-manager 會自動(dòng)重啟生效。然后我們需要使用下面的命令為 Kubernetes 證書 API 創(chuàng)建一個(gè)證書簽名請求。如果您設(shè)置例如 cert-manager 等外部簽名者,則會自動(dòng)批準(zhǔn)證書簽名請求(CSRs)。否者,您必須使用 kubectl certificate 命令手動(dòng)批準(zhǔn)證書。以下 kubeadm 命令輸出要批準(zhǔn)的證書名稱,然后等待批準(zhǔn)發(fā)生:
#?需要將全部?pending?的證書全部批準(zhǔn)
$?kubeadm?alpha?certs?renew?all?--use-api?--config?kubeadm.yaml?&
我們還不能直接重啟控制面板的幾個(gè)組件,這是因?yàn)槭褂?kubeadm 安裝的集群對應(yīng)的 etcd 默認(rèn)是使用的 /etc/kubernetes/pki/etcd/ca.crt 這個(gè)證書進(jìn)行前面的,而上面我們用命令 kubectl certificate approve 批準(zhǔn)過后的證書是使用的默認(rèn)的 /etc/kubernetes/pki/ca.crt 證書進(jìn)行簽發(fā)的,所以我們需要替換 etcd 中的 ca 機(jī)構(gòu)證書:
#?先拷貝靜態(tài)?Pod?資源清單
$?cp?-r?/etc/kubernetes/manifests/?/etc/kubernetes/manifests.bak
$?vi?/etc/kubernetes/manifests/etcd.yaml
......
spec:
??containers:
??-?command:
????-?etcd
????#?修改為?CA?文件
????-?--peer-trusted-ca-file=/etc/kubernetes/pki/ca.crt
????-?--trusted-ca-file=/etc/kubernetes/pki/ca.crt
......
????volumeMounts:
????-?mountPath:?/var/lib/etcd
??????name:?etcd-data
????-?mountPath:?/etc/kubernetes/pki??#?更改證書目錄
??????name:?etcd-certs
??volumes:
??-?hostPath:
??????path:?/etc/kubernetes/pki??#?將?pki?目錄掛載到?etcd?中去
??????type:?DirectoryOrCreate
????name:?etcd-certs
??-?hostPath:
??????path:?/var/lib/etcd
??????type:?DirectoryOrCreate
????name:?etcd-data
......
由于 kube-apiserver 要連接 etcd 集群,所以也需要重新修改對應(yīng)的 etcd ca 文件:
$?vi?/etc/kubernetes/manifests/kube-apiserver.yaml
......
spec:
??containers:
??-?command:
????-?kube-apiserver
????#?將etcd?ca文件修改為默認(rèn)的ca.crt文件
????-?--etcd-cafile=/etc/kubernetes/pki/ca.crt
......
除此之外還需要替換 requestheader-client-ca-file 文件,默認(rèn)是 /etc/kubernetes/pki/front-proxy-ca.crt 文件,現(xiàn)在也需要替換成默認(rèn)的 CA 文件,否則使用聚合 API,比如安裝了 metrics-server 后執(zhí)行 kubectl top 命令就會報(bào)錯(cuò):
$?cp?/etc/kubernetes/pki/ca.crt?/etc/kubernetes/pki/front-proxy-ca.crt
$?cp?/etc/kubernetes/pki/ca.key?/etc/kubernetes/pki/front-proxy-ca.key
這樣我們就得到了一個(gè) 10 年證書的 k8s 集群,還可以通過重新編譯 kubeadm 來實(shí)現(xiàn)一個(gè) 10 年證書的,這個(gè)我沒有嘗試,不過在初始化集群的時(shí)候也是一個(gè)方法。
- END -
?推薦閱讀? Kubernetes 企業(yè)運(yùn)維進(jìn)階實(shí)戰(zhàn)? 如何用 Kubernetes 實(shí)現(xiàn) CI/CD 發(fā)布流程?| 漫畫 使用 Jenkins 構(gòu)建 CI/CD 之多分支流水線 K8s kubectl 常用命令總結(jié)(建議收藏) 終于明白了 DevOps 與 SRE 的區(qū)別! 我在創(chuàng)業(yè)公司的 “云原生” 之旅 基于Nginx實(shí)現(xiàn)灰度發(fā)布與AB測試 編寫 Dockerfile 最佳實(shí)踐 Kubernetes 網(wǎng)絡(luò)方案之炫酷的 Cilium 運(yùn)維工程師不得不看的經(jīng)驗(yàn)教訓(xùn)和注意事項(xiàng) Kubernetes 的這些核心資源原理,你一定要了解
點(diǎn)亮,服務(wù)器三年不宕機(jī)


