)
看到這行報(bào)錯(cuò)我估摸著你八成已經(jīng)把安裝腳本重跑了好幾遍甚至開(kāi)始琢磨要不要把節(jié)點(diǎn)格式化掉重來(lái)。先別急著動(dòng)手failed to run Kubelet: misconfiguration: kubelet cgroup driver這個(gè)錯(cuò)誤在 kubeasz 部署 Kubernetes 集群時(shí)其實(shí)非常典型它既不是你的網(wǎng)卡配置有問(wèn)題也不是鏡像拉不下來(lái)更不是某個(gè)組件服務(wù)掛了而是一個(gè)純粹的“兩邊配置不一致”導(dǎo)致 kubelet 拒絕啟動(dòng)的問(wèn)題。我用 kubeasz 部署集群也有幾年了從 All-in-One 到多主多節(jié)點(diǎn)都搭過(guò)中間踩過(guò)不少類(lèi)似的坑。這篇文章專(zhuān)門(mén)把這個(gè)問(wèn)題掰開(kāi)揉碎講清楚報(bào)錯(cuò)背后的機(jī)制是什么、kubeasz 里到底哪些配置會(huì)影響它、正確的修復(fù)路徑怎么走以及我實(shí)操中總結(jié)出來(lái)的排查套路。無(wú)論你是準(zhǔn)備用 kubeasz 裝一套新環(huán)境還是集群里某臺(tái)節(jié)點(diǎn)突然起不來(lái)了這篇都應(yīng)該能幫你省下不少折騰的時(shí)間。1. 拆解這條報(bào)錯(cuò)kubelet 為什么寧可“死掉”也不起來(lái)1.1 kubelet 啟動(dòng)時(shí)的“安檢”邏輯很多人看到misconfiguration這個(gè)詞就有點(diǎn)慌覺(jué)得是不是自己哪里配置寫(xiě)錯(cuò)了。其實(shí)準(zhǔn)確地說(shuō)這是 kubelet 啟動(dòng)時(shí)的一個(gè)強(qiáng)校驗(yàn)邏輯。kubelet 在啟動(dòng)階段大概會(huì)做這幾件事讀取自己的配置文件、初始化容器運(yùn)行時(shí)接口、檢查 cgroup driver 是否匹配、然后才去跟 API Server 注冊(cè)節(jié)點(diǎn)。問(wèn)題就出在第三步。kubelet 會(huì)拿自己配置里的 cgroup driver 去和當(dāng)前節(jié)點(diǎn)上容器運(yùn)行時(shí)常見(jiàn)的就是 Docker 或 containerd實(shí)際使用的 cgroup driver 做對(duì)比。一旦發(fā)現(xiàn)兩邊不一致就不會(huì)繼續(xù)往下走直接拋出misconfiguration然后退出。Kubernetes 在這里選擇的是“寧可讓節(jié)點(diǎn)不加入集群也不能帶病運(yùn)行”的策略。這個(gè)強(qiáng)校驗(yàn)不是憑空設(shè)計(jì)的。因?yàn)?kubelet 和容器運(yùn)行時(shí)都需要通過(guò) cgroup 來(lái)管理容器的 CPU、內(nèi)存、PID 等資源限制。如果兩套組件分別用不同的 cgroup 管理方式它們對(duì)資源層級(jí)、子目錄、清理邏輯的理解就完全不一樣。輕則某個(gè)容器無(wú)法啟動(dòng)重則系統(tǒng)層級(jí)的資源回收、節(jié)點(diǎn)驅(qū)逐、OOM 打分全都會(huì)亂套。你可以理解為兩個(gè)包工頭拿到了同一棟樓的兩張不同圖紙硬要一起施工最后必然出事。所以 kubelet 寧可啟動(dòng)失敗也不愿意在這種狀態(tài)下工作。1.2 cgroupfs 與 systemd兩種驅(qū)動(dòng)到底差在哪要理解這個(gè)報(bào)錯(cuò)就必須知道 cgroup driver 到底是什么。Linux 內(nèi)核提供 cgroup 機(jī)制來(lái)對(duì)進(jìn)程做資源隔離和限制而“driver”就是用戶態(tài)通過(guò)什么接口去操作 cgroup。目前主流的就是兩種cgroupfs driver直接讀寫(xiě)/sys/fs/cgroup下的文件系統(tǒng)創(chuàng)建目錄、寫(xiě)入限制值。這種方式比較底層、直接Docker 的默認(rèn)值就是它。systemd driver把資源管理交給 systemd 自身的 cgroup 管理機(jī)制通過(guò) systemd 的單元scope/slice來(lái)建立 cgroup 層級(jí)。Kubernetes 官方文檔明確建議在使用了 systemd 作為 init 系統(tǒng)的節(jié)點(diǎn)上kubelet 和容器運(yùn)行時(shí)都應(yīng)該把 cgroup driver 配置為systemd。為什么官方推薦 systemd因?yàn)楝F(xiàn)代 Linux 發(fā)行版幾乎都是 systemd 作為 1 號(hào)進(jìn)程。systemd 自己也會(huì)用 cgroup 來(lái)管理服務(wù)。如果 kubelet 和 Docker 繞過(guò) systemd 直接用 cgroupfs就等于在 systemd 的眼皮底下另起爐灶兩套管理邏輯同時(shí)操作同一片資源樹(shù)很容易造成沖突。最典型的問(wèn)題就是 systemd 在停止某個(gè)服務(wù)時(shí)可能會(huì)把 kubelet 創(chuàng)建的 cgroup 目錄一并清理掉或者反過(guò)來(lái)資源統(tǒng)計(jì)出現(xiàn)偏差。統(tǒng)一走systemddriver 之后所有進(jìn)程的資源都由 systemd 統(tǒng)一規(guī)劃和接管整體上要干凈得多。1.3 報(bào)錯(cuò)信息本身就把“答案”告訴你了failed to run Kubelet: misconfiguration: kubelet cgroup driver: xxx is different from docker cgroup driver: yyy注意這行報(bào)錯(cuò)它其實(shí)寫(xiě)得非常清楚xxx是 kubelet 當(dāng)前使用的驅(qū)動(dòng)yyy是 Docker 當(dāng)前使用的驅(qū)動(dòng)。兩邊不一樣所以 kubelet 拒絕啟動(dòng)。換句話說(shuō)問(wèn)題范圍已經(jīng)被縮小到“節(jié)點(diǎn)上的 kubelet 配置”和“節(jié)點(diǎn)上的 Docker 配置”這兩處了。很多時(shí)候你不用去翻日志光看報(bào)錯(cuò)末尾就知道該去改誰(shuí)。不過(guò)有一點(diǎn)要提醒報(bào)錯(cuò)里的字段不一定都是docker。如果你的集群用的容器運(yùn)行時(shí)是 containerd一樣會(huì)報(bào)類(lèi)似的信息只是末尾會(huì)變成containerd cgroup driver。處理方法大同小異后面我會(huì)把兩種運(yùn)行時(shí)的情況都講到。2. 在 kubeasz 里cgroup driver 到底是在哪里被“寫(xiě)死”的2.1 kubeasz 的配置層級(jí)kubeasz 這個(gè)項(xiàng)目本身是基于 Ansible 的一鍵部署方案它把 K8s 集群的安裝拆成很多 role比如 docker、kubelet、kube-master、kube-node 等等。它最大的特點(diǎn)是“二進(jìn)制方式部署”和 kubeadm 的生成方式不同很多配置是直接由模板渲染出來(lái)的。以我常用的 kubeasz 3.x 目錄結(jié)構(gòu)為例你創(chuàng)建集群后主要配置都在/etc/kubeasz/clusters/集群名/config.yml里。這個(gè)文件里有節(jié)點(diǎn)列表、版本號(hào)、網(wǎng)絡(luò)插件、運(yùn)行時(shí)類(lèi)型等一堆參數(shù)。kubelet 的配置由roles/kube-node/templates/kubelet-config.yaml.j2這類(lèi)模板渲染渲染時(shí)就會(huì)讀config.yml里定義的變量。如果你用的老版本 kubeasz 2.x配置位置則往往在/etc/ansible/roles/kube-node/defaults/main.yml這類(lèi) defaults 文件里。在動(dòng)手之前建議先確認(rèn)一下自己用的版本免得按 3.x 的路徑去找 2.x 的文件找了半天找不到。2.2 容器運(yùn)行時(shí)那邊的 cgroup driver 怎么確認(rèn)在 kubeasz 部署過(guò)程中容器運(yùn)行時(shí)的初始化也會(huì)通過(guò) Ansible role 完成。比如 Docker 的配置最終落在/etc/docker/daemon.json這里面就有一個(gè)關(guān)鍵字段{ exec-opts: [native.cgroupdriversystemd] }如果這個(gè)字段缺失Docker 默認(rèn)會(huì)用cgroupfs。而 kubelet 的配置模板里cgroupDriver字段的值則來(lái)自 kubeasz 集群配置里的某個(gè)變量常見(jiàn)叫kubelet_cgroup_driver。如果模板里默認(rèn)值取的是systemd而 Docker 那邊還是cgroupfs那啟動(dòng)時(shí)必然對(duì)不上報(bào)錯(cuò)也就順理成章了。所以排查的一個(gè)核心手段就是先看節(jié)點(diǎn)上兩個(gè)地方的實(shí)際值# 看 kubelet 當(dāng)前配置里的 cgroupDriver cat /var/lib/kubelet/config.yaml | grep -i cgroup # 看 Docker 實(shí)際使用的驅(qū)動(dòng) docker info | grep -i cgroup # 如果是 containerd crictl info | grep -i cgroup注意/var/lib/kubelet/config.yaml是 kubelet 最終生效的配置文件它由 kubeasz 渲染生成。如果你手動(dòng)改過(guò) kubeasz 模板但沒(méi)重新跑 playbook這個(gè)文件里的值很可能還是舊的。2.3 為什么 kubeasz 的環(huán)境特別容易中招我復(fù)盤(pán)了多次踩坑經(jīng)歷發(fā)現(xiàn) kubeasz 部署時(shí)這個(gè)問(wèn)題高發(fā)主要有三個(gè)原因第一kubeasz 支持在多種 Linux 發(fā)行版上部署而不同發(fā)行版預(yù)裝的 Docker 或 containerd 默認(rèn)配置不一致。比如某些系統(tǒng)自帶 Docker它可能已經(jīng)寫(xiě)好了自己的/etc/docker/daemon.json如果里面沒(méi)有cgroupdriver配置默認(rèn)就是cgroupfs。第二kubeasz 在初始化節(jié)點(diǎn)時(shí)可能會(huì)因?yàn)樘綔y(cè)系統(tǒng)類(lèi)型或運(yùn)行時(shí)類(lèi)型的不同導(dǎo)致模板渲染出的 kubelet 配置和運(yùn)行時(shí)配置沒(méi)有對(duì)齊。換句話說(shuō)kubeasz 本身提供了參數(shù)去控制但如果你在創(chuàng)建集群時(shí)沒(méi)有顯式指定或者指定的值和節(jié)點(diǎn)上實(shí)際運(yùn)行時(shí)不匹配就會(huì)出現(xiàn)“部署過(guò)程沒(méi)報(bào)錯(cuò)、kubelet 起不來(lái)”的情況。第三也是最常見(jiàn)的部署人員為了省事先手動(dòng)裝了 Docker之后才用 kubeasz 跑集群。手動(dòng)安裝 Docker 時(shí)很少有人會(huì)特意去寫(xiě)exec-opts于是 Docker 默認(rèn)cgroupfs而 kubeasz 模板里如果默認(rèn)渲染systemd兩邊就打架了。3. 三種修復(fù)姿勢(shì)從“最推薦”到“應(yīng)急”3.1 姿勢(shì)一改 kubeasz 集群配置重跑對(duì)應(yīng) playbook這是我認(rèn)為最正規(guī)的修法適合還沒(méi)上線或者節(jié)點(diǎn)狀態(tài)還能忍受的集群。思路很簡(jiǎn)單通過(guò) kubeasz 自身的配置把 kubelet 的 cgroup driver 統(tǒng)一到和運(yùn)行時(shí)一致然后讓 Ansible 重新生成配置并重啟服務(wù)。假設(shè)你確認(rèn)了 Docker 那邊是cgroupfs但 kubelet 渲染出來(lái)是systemd可以這樣做。第一步進(jìn)入 kubeasz 集群配置目錄找到你的集群配置文件cd /etc/kubeasz grep -R kubelet_cgroup_driver clusters/你的集群名/config.yml第二步把它的值改成和 Docker 一致或者干脆統(tǒng)一改成你期望的 driver。如果你用的是 systemd 系統(tǒng)我建議在配置里直接顯式設(shè)置kubelet_cgroup_driver: systemd同時(shí)把 Docker 也改成systemd一步到位。第三步重新執(zhí)行 kubeasz 的 node 角色 playbookansible-playbook -i clusters/你的集群名/hosts playbooks/02.kube-node.yml這個(gè) playbook 會(huì)重新渲染 kubelet 的配置、啟動(dòng) kubelet并嘗試把節(jié)點(diǎn)加入集群。執(zhí)行完畢之后用kubectl get nodes看節(jié)點(diǎn)狀態(tài)。這里有兩個(gè)細(xì)節(jié)值得說(shuō)。一是改配置前一定先備份原文件別問(wèn)我是怎么知道的改壞了回滾的時(shí)候沒(méi)備份真的想哭。二是如果集群已經(jīng)在跑業(yè)務(wù)重跑 playbook 之前最好先評(píng)估一下影響面因?yàn)?kubelet 的重啟會(huì)短暫中斷該節(jié)點(diǎn)上的 Pod。3.2 姿勢(shì)二修改 Docker daemon 配置來(lái)對(duì)齊如果你不想動(dòng) kubelet 的配置或者你希望保留cgroupfs比如某些極端場(chǎng)景下你確實(shí)不需要 systemd driver那也可以反著來(lái)修改 Docker 的daemon.json讓 Docker 使用和 kubelet 一樣的驅(qū)動(dòng)。先備份現(xiàn)有配置cp /etc/docker/daemon.json /etc/docker/daemon.json.bak然后編輯/etc/docker/daemon.json。如果文件不存在就新建一個(gè)但要注意如果原來(lái)有其他配置項(xiàng)比如鏡像加速、log 配置等一定要保留不要覆蓋掉。以統(tǒng)一到systemd為例{ exec-opts: [native.cgroupdriversystemd] }保存后重啟 Docker 和 kubeletsystemctl restart docker systemctl restart kubelet這里必須提醒一句重啟 Docker 是一個(gè)重操作。如果節(jié)點(diǎn)上已經(jīng)跑了很多容器Docker 重啟會(huì)把容器全部拉起但業(yè)務(wù)中斷的那幾秒是跑不掉的。所以這個(gè)方法更適合單機(jī)測(cè)試環(huán)境或者業(yè)務(wù)允許中斷的場(chǎng)景。線上環(huán)境優(yōu)先用第一種姿勢(shì)。還有個(gè)常見(jiàn)坑你改了daemon.json但 Docker 實(shí)際上沒(méi)有重新加載exec-opts。這是因?yàn)?Docker 對(duì)daemon.json的某些字段修改必須重啟 daemon 才生效不是systemctl reload就行的。如果你用systemctl reload docker很可能看到docker info里還是老樣子。老老實(shí)實(shí)restart。3.3 姿勢(shì)三單節(jié)點(diǎn)手動(dòng)補(bǔ)丁快速恢復(fù)如果你只需要先把一臺(tái)節(jié)點(diǎn)救回來(lái)等之后再統(tǒng)一處理可以直接在節(jié)點(diǎn)上手動(dòng)改配置。這個(gè)方式最靈活也最直接。kubelet 的最終配置文件通常是/var/lib/kubelet/config.yaml里面有一個(gè)頂層字段cgroupDriver: systemd找到它改成和 Docker 一致然后重啟 kubeletsystemctl restart kubelet不過(guò)這里我要潑一盆冷水手動(dòng)改/var/lib/kubelet/config.yaml只對(duì)當(dāng)前節(jié)點(diǎn)生效而且下次重跑 kubeasz 的 playbook、或者有人重新執(zhí)行了初始化腳本這個(gè)文件可能會(huì)被重新渲染覆蓋。所以手動(dòng)補(bǔ)丁只能算“臨時(shí)措施”治標(biāo)不治本。另一個(gè)手動(dòng)修改的入口是 kubelet 的 systemd unit 參數(shù)。你可以查看cat /etc/systemd/system/kubelet.service.d/*.conf如果有類(lèi)似--cgroup-driversystemd這樣的參數(shù)也可以直接改參數(shù)后執(zhí)行systemctl daemon-reload systemctl restart kubelet。但新版本 K8s 更推薦通過(guò)config.yaml來(lái)管理 kubelet 參數(shù)我一般優(yōu)先改配置文件。順便講一個(gè)判斷技巧如果你不確定 kubelet 到底用哪個(gè)配置生效可以執(zhí)行kubelet --print-config這個(gè)命令會(huì)打印出 kubelet 實(shí)際生效的完整配置包括cgroupDriver字段。這樣你就能確認(rèn)它讀的到底是不是/var/lib/kubelet/config.yaml。3.4 修復(fù)完成后必須驗(yàn)證的三件事不管用哪種姿勢(shì)改完之后都不能只看systemctl status kubelet是 active 就完事。我建議按這個(gè)順序驗(yàn)證第一看 kubelet 日志里有沒(méi)有新的報(bào)錯(cuò)journalctl -u kubelet -f如果報(bào)錯(cuò)消失kubelet 會(huì)繼續(xù)往下走開(kāi)始連 API Server 并注冊(cè)節(jié)點(diǎn)。第二核對(duì)兩邊的驅(qū)動(dòng)確實(shí)一致docker info | grep -i cgroup grep -i cgroupDriver /var/lib/kubelet/config.yaml這兩條命令的輸出應(yīng)該是一致的一個(gè)寫(xiě)cgroupfs就都是cgroupfs寫(xiě)systemd就都是systemd。第三到控制平面看節(jié)點(diǎn)狀態(tài)kubectl get nodes如果狀態(tài)變成Ready說(shuō)明節(jié)點(diǎn)已經(jīng)成功加入集群。如果還是NotReady那就得繼續(xù)往下看是不是其他問(wèn)題比如 CNI 網(wǎng)絡(luò)沒(méi)通、容器運(yùn)行時(shí)沒(méi)起來(lái)等。你也可以關(guān)注一下kubectl describe node 節(jié)點(diǎn)名里的 Conditions那里會(huì)告訴你節(jié)點(diǎn)當(dāng)前卡在哪個(gè)環(huán)節(jié)。4. 真實(shí)排障場(chǎng)景與常見(jiàn)問(wèn)題速查4.1 場(chǎng)景A全新集群剛裝完第一個(gè) worker 節(jié)點(diǎn)就報(bào)錯(cuò)有次我搭建測(cè)試環(huán)境主節(jié)點(diǎn)正常但第一個(gè) worker 節(jié)點(diǎn)怎么都加不進(jìn)來(lái)報(bào)錯(cuò)就是開(kāi)頭那段。當(dāng)時(shí)第一反應(yīng)是檢查網(wǎng)絡(luò)結(jié)果發(fā)現(xiàn)主節(jié)點(diǎn)和 worker 節(jié)點(diǎn)之間通信完全正常。后來(lái)仔細(xì)看了報(bào)錯(cuò)信息末尾寫(xiě)的是docker cgroup driver: cgroupfs。我去 worker 節(jié)點(diǎn)上看了一下/var/lib/kubelet/config.yaml里面確實(shí)渲染成了systemd而 Docker 是系統(tǒng)自帶的默認(rèn)cgroupfs。這個(gè)場(chǎng)景就是典型的“兩邊不一致”直接撞上強(qiáng)校驗(yàn)。當(dāng)時(shí)集群還沒(méi)上線我直接用了姿勢(shì)一修改 kubeasz 集群配置里的kubelet_cgroup_driver為cgroupfs重跑 playbook節(jié)點(diǎn)立即加入成功。雖然我個(gè)人不太喜歡cgroupfs但在測(cè)試環(huán)境里沒(méi)必要為了切換驅(qū)動(dòng)折騰一圈能跑就行。如果是生產(chǎn)環(huán)境我還是建議統(tǒng)一到systemd后面再找窗口切換。要注意一個(gè)細(xì)節(jié)這個(gè)場(chǎng)景下 kubeasz 的部署日志里往往沒(méi)有任何異常因?yàn)?Ansible 認(rèn)為配置都執(zhí)行完了只有 kubelet 自己?jiǎn)?dòng)的時(shí)候才知道不對(duì)。這就是為什么很多人會(huì)反復(fù)重跑安裝腳本因?yàn)樗静粫?huì)在安裝階段暴露問(wèn)題。4.2 場(chǎng)景B升級(jí)容器運(yùn)行時(shí)后節(jié)點(diǎn)突然掛了另一種常見(jiàn)情況是節(jié)點(diǎn)原本跑得好好的某次運(yùn)維操作后 kubelet 起不來(lái)了。比如你升級(jí)了 Docker 版本或者 containerd 版本重啟之后發(fā)現(xiàn)節(jié)點(diǎn) NotReady。原因通常是容器運(yùn)行時(shí)升級(jí)后默認(rèn)的 cgroup driver 發(fā)生了變化或者升級(jí)后的運(yùn)行時(shí)把config.toml/daemon.json里的某個(gè)參數(shù)重置了。我之前遇到過(guò)一個(gè)情況某臺(tái)節(jié)點(diǎn)上 containerd 的配置里SystemdCgroup原本是true但升級(jí)后配置文件被重置成了默認(rèn)false于是 containerd 實(shí)際使用的是cgroupfs而 kubelet 配置還是systemd兩邊對(duì)不上kubelet 啟動(dòng)時(shí)直接報(bào)錯(cuò)。這種場(chǎng)景下處理思路就兩句話先確認(rèn)運(yùn)行時(shí)現(xiàn)在的實(shí)際 driver再?zèng)Q定改 kubelet 還是改運(yùn)行時(shí)。千萬(wàn)不要憑記憶覺(jué)得“之前是 systemd 就不用查了”一定要以當(dāng)前實(shí)際狀態(tài)為準(zhǔn)。如果是 containerd配置文件通常在/etc/containerd/config.toml關(guān)鍵字段是[plugins.io.containerd.grpc.v1.cri] systemd_cgroup true這里有個(gè)非常誤導(dǎo)人的地方systemd_cgroup false時(shí) containerd 用的是cgroupfs很多人看到字段名帶systemd就以為設(shè)置成 false 才是“不用 systemd”其實(shí)反過(guò)來(lái)。一定要記住要使用systemddriver必須設(shè)置systemd_cgroup true。4.3 常見(jiàn)問(wèn)題速查表我把幾種典型情況整理成了一張表方便你在排查時(shí)直接對(duì)照?qǐng)?bào)錯(cuò)特征實(shí)際原因處理方式kubelet 為 cgroupfsdocker 為 systemd有人改過(guò) Docker 的 exec-opts或發(fā)行版 Docker 預(yù)設(shè)了 systemd將 kubelet 配置改為 systemd或改 daemon.json 回 cgroupfskubelet 為 systemddocker 為 cgroupfsDocker 默認(rèn)配置kubeasz 模板渲染成了 systemd推薦改 Docker 為 systemd或改 kubeasz 配置為 cgroupfs錯(cuò)誤提示包含 containerd cgroup drivercontainerd 配置里 systemd_cgroup 與實(shí)際不符修改/etc/containerd/config.toml中 systemd_cgroup 參數(shù)重跑 playbook 后配置又被覆蓋改的是節(jié)點(diǎn)本地文件kubeasz 模板值沒(méi)同步改 kubeasz 集群配置重跑對(duì)應(yīng) playbook部分節(jié)點(diǎn) Ready、部分 NotReady節(jié)點(diǎn)間 Docker 或 kubelet 的配置存在差異逐臺(tái)檢查 daemon.json、kubelet config.yaml統(tǒng)一配置這張表看起來(lái)簡(jiǎn)單但實(shí)際排查時(shí) 80% 的情況都能直接套用。核心就是“先看報(bào)錯(cuò)末尾的 driver 是什么、再看本地各自的配置是什么”。5. 一些我踩過(guò)的坑和額外提醒5.1 別把 cgroupfs 和 systemd 混著用這個(gè)問(wèn)題值得單獨(dú)拿出來(lái)說(shuō)。雖然 Kubernetes 只強(qiáng)制要求“同一節(jié)點(diǎn)上 kubelet 和容器運(yùn)行時(shí)一致”但并沒(méi)有要求“全集群所有節(jié)點(diǎn)一致”。理論上一個(gè)節(jié)點(diǎn)用cgroupfs、另一個(gè)節(jié)點(diǎn)用systemd也能組成集群但我不建議這么干。原因很簡(jiǎn)單不同節(jié)點(diǎn)上 kubelet 的行為會(huì)因?yàn)轵?qū)動(dòng)不同而產(chǎn)生細(xì)微差異尤其是在資源統(tǒng)計(jì)、驅(qū)逐閾值、Pod QoS 等級(jí)的判定上。混用狀態(tài)下的集群排查問(wèn)題時(shí)你會(huì)多出一個(gè)“節(jié)點(diǎn)之間行為不一致”的干擾項(xiàng)。有次我圖省事只改了一臺(tái) worker 的 kubelet 配置結(jié)果這臺(tái)節(jié)點(diǎn)后來(lái)出現(xiàn)了 cgroup 目錄殘留的問(wèn)題和集群里其他節(jié)點(diǎn)的表現(xiàn)完全不同排查了大半天才發(fā)現(xiàn)是驅(qū)動(dòng)混用導(dǎo)致的。所以我的建議是要么全集群cgroupfs要么全集群systemd推薦后者。5.2 版本差異3.x 和 2.x 的配置位置不一樣kubeasz 不同版本之間的配置路徑變動(dòng)比較大。如果你在網(wǎng)上搜解決方案搜出來(lái)的路徑未必適用你的版本。以我之前用過(guò)的版本為例kubeasz 2.x 時(shí)代配置經(jīng)常直接寫(xiě)在/etc/ansible/roles/kube-node/defaults/main.yml這類(lèi)文件里改起來(lái)相對(duì)粗暴。kubeasz 3.x 之后引入了集群概念配置集中在/etc/kubeasz/clusters/xxx/config.yml并且推薦用ezctl命令來(lái)管理和創(chuàng)建集群。所以當(dāng)你準(zhǔn)備修改配置時(shí)先確認(rèn)自己的版本。你可以在項(xiàng)目目錄下看 README 或者執(zhí)行ansible-playbook --version # 或者看 kubeasz 項(xiàng)目目錄里的版本文件 cat /etc/kubeasz/VERSION避免照著網(wǎng)上的舊教程改了半天的文件結(jié)果根本不是渲染模板的數(shù)據(jù)源那就白費(fèi)勁了。5.3 主機(jī)本身的環(huán)境因素不要忽略有時(shí)候問(wèn)題不在 kubeasz 配置里而在主機(jī)環(huán)境本身。比如某些云廠商或虛擬化平臺(tái)提供的鏡像里Docker 是預(yù)裝的而且daemon.json里已經(jīng)寫(xiě)了exec-opts。有些鏡像甚至已經(jīng)把 Docker 改成了systemddriver而 kubeasz 模板還是按默認(rèn)的cgroupfs渲染了 kubelet這種情況下報(bào)錯(cuò)方向是反的。所以我的建議是不要一上來(lái)就去翻 kubeasz 集群配置先去節(jié)點(diǎn)上跑一遍 3.2 節(jié)里的兩條檢查命令拿到兩邊的真實(shí) driver 再?zèng)Q定改哪邊。先收集事實(shí)再動(dòng)手修改這個(gè)順序能幫你少走很多彎路。另外還有一種少見(jiàn)但確實(shí)存在的情況主機(jī)上之前安裝過(guò)其他容器編排工具或者手動(dòng)掛載過(guò) cgroup 文件系統(tǒng)導(dǎo)致 systemd 和 cgroupfs 的視圖不一致。這種環(huán)境問(wèn)題不是改一行配置就能解決的可能需要重啟主機(jī)或者重新掛載 cgroup 子系統(tǒng)才能恢復(fù)干凈狀態(tài)。如果你排查到這一步建議先把主機(jī)上的歷史容器環(huán)境清理干凈再重新部署。最后分享一個(gè)小經(jīng)驗(yàn)。我后來(lái)部署集群時(shí)會(huì)在 kubeasz 的集群配置文件里顯式寫(xiě)出kubelet_cgroup_driver: systemd同時(shí)確認(rèn)/etc/docker/daemon.json里存在native.cgroupdriversystemd再讓 ansible 開(kāi)始干活。一次鎖定之后后面不管怎么擴(kuò)容、怎么重裝節(jié)點(diǎn)都不會(huì)再被這個(gè)報(bào)錯(cuò)騷擾。這個(gè)檢查步驟我建議你加到自己的部署前 checklist 里真的能省很多半夜排障的精力。