未回收引發(fā)RDMA資源消失與CSI掛載故障排查)
那天下午我是被告警拉起來(lái)的一批訓(xùn)練任務(wù)和數(shù)據(jù)庫(kù)實(shí)例幾乎同時(shí)卡在ContainerCreating和Pending消息記錄里到處是 RDMA 連接失敗和 CSI 掛載超時(shí)的字樣。群里第一反應(yīng)是“存儲(chǔ)集群掛了”有人已經(jīng)開(kāi)始聯(lián)系機(jī)房值班查鏈路。說(shuō)實(shí)話(huà)看到這類(lèi)組合報(bào)錯(cuò)正常的直覺(jué)確實(shí)是先往存儲(chǔ)端想。但那次不一樣——我登錄節(jié)點(diǎn)用ibstat檢查 RDMA 網(wǎng)卡物理狀態(tài)鏈路明明是正常的存儲(chǔ)服務(wù)端的日志也干干凈凈。真正的問(wèn)題藏在一個(gè)完全想不到的地方節(jié)點(diǎn)上的 RDMA 擴(kuò)展資源在小半天前突然變成了 0而 CSI 掛載失敗只是它引發(fā)的一連串“次生災(zāi)害”。這次排障最終指向了平臺(tái)組件的污點(diǎn)Taint與容忍Toleration配置一句話(huà)總結(jié)就是維護(hù)節(jié)點(diǎn)時(shí)留下的一個(gè)污點(diǎn)沒(méi)有回收導(dǎo)致承載 RDMA 資源上報(bào)和 CSI 存儲(chǔ)插件的 DaemonSet 被驅(qū)逐后一直回不來(lái)。1. 故障現(xiàn)象復(fù)盤(pán)掛載報(bào)錯(cuò)先于告警排查卻差點(diǎn)被帶偏到存儲(chǔ)端1.1 兩類(lèi)報(bào)錯(cuò)同時(shí)出現(xiàn)Pending 與 FailedMount 的現(xiàn)場(chǎng)當(dāng)時(shí)集群里同時(shí)出現(xiàn)了兩類(lèi)完全不同的 Pod 狀態(tài)這也是最初讓所有人困惑的地方。第一類(lèi)是依賴(lài) RDMA 設(shè)備的數(shù)據(jù)處理任務(wù)。kubectl describe pod里看到的事件是典型的資源不足Events: Type Reason Age From Message ---- ------ ---- ---- ------- Warning FailedScheduling 3h default-scheduler 0/120 nodes are available: 20 node(s) insufficient rdma/rdma_shared_device.調(diào)度器明確告訴你有 20 個(gè)節(jié)點(diǎn)缺 RDMA 擴(kuò)展資源??墒沁@批節(jié)點(diǎn)的網(wǎng)卡從硬件上看都活著這個(gè)“資源不足”從哪兒來(lái)的第二類(lèi)是需要掛載并行存儲(chǔ)卷的普通業(yè)務(wù) Pod它們的報(bào)錯(cuò)更直接MountVolume.SetUp failed for volume pvc-f3a9c...: rpc error: code Unknown desc mount failed: failed to connect to storage server via rdma: no such device這個(gè)no such device很關(guān)鍵它是從存儲(chǔ)掛載插件里拋出來(lái)的意思是插件在節(jié)點(diǎn)上找不到可用的 RDMA 設(shè)備。注意這里說(shuō)的“設(shè)備”不一定是物理網(wǎng)卡消失而是某個(gè)上層可見(jiàn)的設(shè)備對(duì)象沒(méi)了。兩種現(xiàn)象有一個(gè)共同指向節(jié)點(diǎn)上的 RDMA 能力在某個(gè)層面上“消失”了。但一個(gè)是調(diào)度器層面感知不到一個(gè)是存儲(chǔ)插件執(zhí)行掛載時(shí)感知不到兩條線(xiàn)索看起來(lái)相關(guān)又沒(méi)有一個(gè)統(tǒng)一的解釋。1.2 第一輪誤判存儲(chǔ)服務(wù)端查了個(gè)底朝天因?yàn)?CSI 掛載失敗的報(bào)錯(cuò)太顯眼大家第一輪排查基本都圍著存儲(chǔ)轉(zhuǎn)。先看存儲(chǔ)服務(wù)端進(jìn)程正??创鎯?chǔ)集群的 RDMA 監(jiān)聽(tīng)端口正常用節(jié)點(diǎn)去 ping 存儲(chǔ)的服務(wù) IP通檢查存儲(chǔ)端的認(rèn)證和導(dǎo)出配置沒(méi)問(wèn)題。甚至懷疑是不是存儲(chǔ) CSI Controller 的版本出了兼容性問(wèn)題把 controller 重啟過(guò)一輪沒(méi)用。后來(lái)有人提議“既然是掛載時(shí)連不上 RDMA那去節(jié)點(diǎn)上看看網(wǎng)卡”于是值班同事登錄故障節(jié)點(diǎn)跑了一遍ibstat返回的端口狀態(tài)是Active鏈路速率、帶寬都正常。物理層完全健康存儲(chǔ)服務(wù)端也沒(méi)毛病那這個(gè)no such device到底哪來(lái)的這里就得承認(rèn)我們一開(kāi)始把“CSI 掛載失敗”當(dāng)成了獨(dú)立故障在查完全忽略了另一個(gè)正在發(fā)生的現(xiàn)象一批需要 RDMA 資源的任務(wù)已經(jīng) Pending 三個(gè)小時(shí)了。把這兩個(gè)現(xiàn)象放一起看思路才打開(kāi)。1.3 轉(zhuǎn)折點(diǎn)節(jié)點(diǎn)眼里 RDMA 資源憑空消失了真正讓排查轉(zhuǎn)向的是一條很簡(jiǎn)單的命令。某位同事對(duì)故障節(jié)點(diǎn)執(zhí)行了kubectl describe node cn-storage-07 | grep -A 12 Capacity歷史截圖里這個(gè)節(jié)點(diǎn)原本應(yīng)該帶著類(lèi)似rdma/rdma_shared_device: 32這樣的擴(kuò)展資源但當(dāng)時(shí) Capacity 列表里壓根沒(méi)有這項(xiàng)。也就是說(shuō)這個(gè)節(jié)點(diǎn)向 Kubernetes 集群上報(bào)的 RDMA 能力已經(jīng)從調(diào)度器的“庫(kù)存賬本”里刪掉了。硬件健康、上報(bào)消失中間只隔著一個(gè)東西節(jié)點(diǎn)上的 RDMA Device Plugin。它不運(yùn)行kubelet 就拿不到設(shè)備的數(shù)量和健康狀態(tài)自然會(huì)把資源從 Capacity 里移除。順著這個(gè)思路去查platform命名空間里的 DaemonSet我當(dāng)場(chǎng)愣住了——故障節(jié)點(diǎn)上根本沒(méi)有對(duì)應(yīng)的 Device Plugin Pod它的狀態(tài)是0/1 nodes are available: 1 node(s) had untolerated taint {node.maint/rdma-check: true}到這里問(wèn)題的性質(zhì)就完全變了不是存儲(chǔ)壞了也不是網(wǎng)卡壞了而是平臺(tái)組件因?yàn)槲埸c(diǎn)容忍配置不夠被驅(qū)逐之后沒(méi)能回到節(jié)點(diǎn)上。而這個(gè)組件一消失RDMA 資源上報(bào)消失連帶 CSI 存儲(chǔ)插件也癱瘓。2. RDMA 資源是如何“消失”的Device Plugin 上報(bào)鏈路拆解2.1 節(jié)點(diǎn)上的 RDMA 能力靠什么進(jìn)入集群視野先說(shuō)一個(gè)底層事實(shí)Kubernetes 本身不認(rèn)識(shí)“RDMA 網(wǎng)卡”這種設(shè)備。它只認(rèn)識(shí) CPU、內(nèi)存、GPU 這類(lèi)內(nèi)置資源像 RDMA 網(wǎng)卡、FPGA、SR-IOV VF 這類(lèi)硬件必須借助擴(kuò)展資源Extended Resource機(jī)制才能進(jìn)入集群的資源池。整個(gè)鏈路是這樣的節(jié)點(diǎn)上的 Device Plugin 以 DaemonSet 或裸 Pod 形式運(yùn)行啟動(dòng)后通過(guò) Unix Socket 跟 kubelet 通信告訴 kubelet“我這臺(tái)節(jié)點(diǎn)上有多少塊 RDMA 網(wǎng)卡、每塊有多少可用隊(duì)列”。kubelet 收到上報(bào)后把這個(gè)數(shù)字寫(xiě)進(jìn)節(jié)點(diǎn)對(duì)象的status.capacity和status.allocatable里調(diào)度器再去讀取這些字段做 Pod 分配。拿日常場(chǎng)景類(lèi)比Device Plugin 是倉(cāng)庫(kù)的報(bào)關(guān)員kubelet 是倉(cāng)庫(kù)管理系統(tǒng)調(diào)度器是接單系統(tǒng)。報(bào)關(guān)員不上班倉(cāng)庫(kù)系統(tǒng)里當(dāng)然查不到這批貨。所以一個(gè)很容易被忽略的事實(shí)是RDMA 資源在 Kubernetes 里的可見(jiàn)性完全依賴(lài) Device Plugin 這個(gè)中間代理的存活狀態(tài)。它活著資源就在它死了哪怕網(wǎng)卡插在機(jī)器上發(fā)光發(fā)熱調(diào)度器也只會(huì)認(rèn)為這臺(tái)節(jié)點(diǎn)沒(méi)有 RDMA。2.2 上報(bào)者消失后資源被刪除硬件明明健康賬本卻清零那 Device Plugin 掛掉之后kubelet 是立刻清除資源還是會(huì)有延遲這取決于 kubelet 的 Device Manager 的監(jiān)聽(tīng)機(jī)制。Device Plugin 與 kubelet 之間通過(guò) gRPC 維持心跳連接插件同時(shí)會(huì)通過(guò)ListAndWatch接口持續(xù)上報(bào)設(shè)備狀態(tài)。一旦插件進(jìn)程退出、Unix Socket 斷開(kāi)kubelet 的 Device Manager 會(huì)在一段很短的時(shí)間內(nèi)通常是幾秒到十幾秒把它注冊(cè)的設(shè)備全部標(biāo)記為不可用并在下一輪節(jié)點(diǎn)狀態(tài)上報(bào)時(shí)把對(duì)應(yīng)的 Extended Resource 從capacity和allocatable中移除。這也是為什么describe node里 Capacity 會(huì)“憑空消失”。不是 kubelet 出錯(cuò)而是它的工作邏輯決定了它只相信自己能連通獲取信息的那些設(shè)備。2.3 一個(gè)命令還原全過(guò)程如果只是想快速確認(rèn)“資源消失是否因?yàn)?Device Plugin 不在”其實(shí)不用繞太多# 1. 確認(rèn)節(jié)點(diǎn)當(dāng)前容量 kubectl get node cn-storage-07 -o jsonpath{.status.capacity.rdma\.io/rdma_shared_device} # 輸出為空或 0 # 2. 查看 Device Plugin 的 Pod 分布 kubectl -n platform get pod -o wide -l apprdma-device-plugin # 發(fā)現(xiàn)目標(biāo)節(jié)點(diǎn)沒(méi)有 Running 的 Pod # 3. 查看 DaemonSet 的調(diào)度事件 kubectl -n platform describe pod pending-pod-name | tail -10 # 關(guān)鍵信息untolerated taint我自己復(fù)盤(pán)的時(shí)候覺(jué)得這套邏輯鏈并不復(fù)雜難的是在紛亂的掛載錯(cuò)誤信息里想到“該去看 Device Plugin”。因?yàn)閺墓收媳憩F(xiàn)看存儲(chǔ)掛載失敗才是“主訴”誰(shuí)會(huì)一開(kāi)始想到去查一個(gè)默默無(wú)聞的 DaemonSet 呢3. 根因定位維護(hù)污點(diǎn)沒(méi)回收DaemonSet 容忍范圍成了盲區(qū)3.1 維護(hù)操作留下的 Taint定位到 Device Plugin 缺失后下一步是搞清它為什么不在節(jié)點(diǎn)上。我們知道 DaemonSet 本來(lái)就應(yīng)該在每個(gè)匹配標(biāo)簽的節(jié)點(diǎn)上保持一個(gè) Pod出現(xiàn)“節(jié)點(diǎn)有標(biāo)簽但 Pod 不在”的情況只有兩類(lèi)原因節(jié)點(diǎn)被打了污點(diǎn)Taint而 DaemonSet 的容忍Toleration列表里沒(méi)有對(duì)應(yīng)的項(xiàng)節(jié)點(diǎn)標(biāo)簽被修改導(dǎo)致節(jié)點(diǎn)不再匹配 DaemonSet 的 nodeSelector。先看節(jié)點(diǎn)標(biāo)簽沒(méi)問(wèn)題。再看污點(diǎn)kubectl describe node cn-storage-07 | grep -A 4 Taints輸出里赫然寫(xiě)著Taints: node.maint/rdma-checktrue:NoExecute這是一個(gè)平臺(tái)自定義的維護(hù)污點(diǎn)作用是排空節(jié)點(diǎn)上所有不容忍它的 Pod然后強(qiáng)制驅(qū)逐。從污點(diǎn) key 的名字看八成是之前做 RDMA 鏈路檢修時(shí)加的當(dāng)時(shí)為了不讓業(yè)務(wù) Pod 繼續(xù)往這些節(jié)點(diǎn)調(diào)度運(yùn)維用自動(dòng)化腳本給整批存儲(chǔ)節(jié)點(diǎn)打了這個(gè)污點(diǎn)并觸發(fā)驅(qū)逐。問(wèn)題在于檢修完成后腳本只做了“確認(rèn)鏈路恢復(fù)正?!睕](méi)有回收污點(diǎn)。于是這批節(jié)點(diǎn)一直帶著NoExecute污點(diǎn)運(yùn)行所有默認(rèn)容忍列表里沒(méi)有它的 DaemonSet Pod 都被驅(qū)逐干凈并且無(wú)法再被調(diào)度回來(lái)。3.2 平臺(tái)組件為什么回不來(lái)容忍配置的盲區(qū)這里要說(shuō)一下 Taint/Toleration 的工作機(jī)制方便沒(méi)基礎(chǔ)的朋友理解。節(jié)點(diǎn)打了NoExecute污點(diǎn)后kubelet 會(huì)立即驅(qū)逐節(jié)點(diǎn)上所有沒(méi)有對(duì)應(yīng)容忍的 Pod而且以后調(diào)度器也不會(huì)再把新 Pod 放到這個(gè)節(jié)點(diǎn)上。想要讓某個(gè) Pod 留在節(jié)點(diǎn)上只能在 Pod 或 DaemonSet 的tolerations里顯式聲明“我不怕這個(gè)污點(diǎn)”。我們平臺(tái)的 RDMA Device Plugin 和 CSI Node 插件都是 DaemonSet它們的容忍配置寫(xiě)的是這樣tolerations: - key: node-role.kubernetes.io/control-plane operator: Exists effect: NoSchedule - key: node.kubernetes.io/not-ready operator: Exists effect: NoExecute覆蓋了控制面污點(diǎn)和通用的節(jié)點(diǎn)不可用污點(diǎn)但誰(shuí)也沒(méi)想到去容忍node.maint/rdma-check這種自定義維護(hù)污點(diǎn)。于是發(fā)生了一連串連鎖反應(yīng)維護(hù)腳本給節(jié)點(diǎn)加上node.maint/rdma-checktrue:NoExecutekubelet 立刻驅(qū)逐節(jié)點(diǎn)上所有沒(méi)有容忍的 Pod包括 RDMA Device Plugin 和 CSI Node 插件的 PodDaemonSet 控制器嘗試重建 Pod但調(diào)度器發(fā)現(xiàn)節(jié)點(diǎn)仍有不容忍的污點(diǎn)Pod 一直P(pán)endingkubelet 檢測(cè)到 Device Plugin Socket 斷開(kāi)把rdma/rdma_shared_device資源從 Capacity 中移除CSI Node 插件不在節(jié)點(diǎn)kubelet 掛載存儲(chǔ)卷時(shí)調(diào)用不到插件報(bào)no such device。3.3 完整因果鏈從一條 YAML 到整個(gè)集群故障把因果鏈寫(xiě)出來(lái)之后整個(gè)故障其實(shí)非?!肮こ袒睕](méi)有任何一個(gè)環(huán)節(jié)是玄學(xué)環(huán)節(jié)狀態(tài)節(jié)點(diǎn) RDMA 物理網(wǎng)卡健康ibstat正常節(jié)點(diǎn) Taintnode.maint/rdma-checktrue:NoExecute未回收RDMA Device Plugin DaemonSet容忍列表缺項(xiàng)Pod 被驅(qū)逐后無(wú)法回歸kubelet Device Manager斷開(kāi)插件連接后自動(dòng)移除擴(kuò)展資源調(diào)度器看到的節(jié)點(diǎn) Capacityrdma/rdma_shared_device消失CSI Node 插件 DaemonSet同樣容忍缺項(xiàng)Pod 不在故障節(jié)點(diǎn)CSI Controller 或存儲(chǔ)服務(wù)端健康無(wú)辜背鍋污點(diǎn)機(jī)制本身是個(gè)好東西它提供了一種優(yōu)雅的“節(jié)點(diǎn)排空”手段。但這次事故也暴露了一個(gè)很現(xiàn)實(shí)的問(wèn)題污點(diǎn)是運(yùn)行時(shí)被外部運(yùn)維腳本動(dòng)態(tài)加上的而容忍是靜態(tài)寫(xiě)在 DaemonSet 模板里的。兩者一旦不同步平臺(tái)基礎(chǔ)組件就成了最先遭殃的受害者。4. 修復(fù)實(shí)操補(bǔ)齊容忍、恢復(fù)組件、驗(yàn)證掛載4.1 給 DaemonSet 補(bǔ)上容忍的正確寫(xiě)法定位到根因之后修復(fù)其實(shí)不復(fù)雜給兩個(gè)受影響的 DaemonSet 都補(bǔ)上對(duì)node.maint/rdma-check的容忍。要注意這里不能只改一個(gè)Device Plugin 和 CSI Node 插件都必須覆蓋否則要么資源恢復(fù)了但掛載還是失敗要么反過(guò)來(lái)。我在實(shí)際修改時(shí)用的是kubectl -n platform edit ds/rdma-device-plugin在spec.template.spec.tolerations里追加tolerations: - key: node.maint/rdma-check operator: Exists effect: NoExecute細(xì)心的朋友可能會(huì)問(wèn)為什么用operator: Exists而不是指定value: true兩個(gè)都行但Exists更穩(wěn)妥。它表示“只要這個(gè) key 存在不管 value 是什么我都容忍”適合這類(lèi)維護(hù)污點(diǎn)——因?yàn)槟悴恢老麓尉S護(hù)腳本會(huì)不會(huì)把 value 改成false或者maintenance。如果寫(xiě)成具體的value: true以后污點(diǎn) value 一換又要改一輪 YAML沒(méi)必要。同樣的操作給storage-csi-node這個(gè) DaemonSet 也來(lái)一遍然后觸發(fā)滾動(dòng)更新kubectl -n platform rollout restart ds/rdma-device-plugin ds/storage-csi-node4.2 恢復(fù)順序很重要先組件后業(yè)務(wù)很多同行遇到這類(lèi)故障手一快就去刪 Pending 的業(yè)務(wù) Pod想讓它重新調(diào)度。但在資源還沒(méi)恢復(fù)的時(shí)候刪調(diào)度器依然找不到帶 RDMA 的節(jié)點(diǎn)Pod 會(huì)繼續(xù) Pending沒(méi)有任何意義。正確的順序是“先讓平臺(tái)組件重新站起來(lái)再恢復(fù)業(yè)務(wù)”。我用kubectl -n platform get pod -o wide -l apprdma-device-plugin觀察新 Pod 的調(diào)度情況。因?yàn)槿萑桃呀?jīng)補(bǔ)齊Pod 很快就安排到了原本被污點(diǎn)擋在外面的存儲(chǔ)節(jié)點(diǎn)上狀態(tài)轉(zhuǎn)成Running。注意一個(gè)小細(xì)節(jié)Device Plugin Pod 起來(lái)之后kubelet 重新通過(guò) Socket 完成設(shè)備注冊(cè)和上報(bào)這個(gè)過(guò)程中節(jié)點(diǎn) Capacity 不會(huì)立刻變化。我大概等了十幾秒再檢查kubectl get node cn-storage-07 -o jsonpath{.status.capacity.rdma\.io/rdma_shared_device}輸出從空變成了32說(shuō)明資源重新回到了調(diào)度器的賬本里。CSI Node 插件也處于Running問(wèn)題節(jié)點(diǎn)的CSINode對(duì)象重新被注冊(cè)kubelet 有辦法調(diào)用掛載操作了。4.3 驗(yàn)證清單Capacity 恢復(fù)與 CSI 掛載成功率業(yè)務(wù)恢復(fù)不是清一波 Pending 就完事我習(xí)慣按下面這套清單逐項(xiàng)驗(yàn)證驗(yàn)證項(xiàng)命令預(yù)期結(jié)果Device Plugin Pod 就緒kubectl -n platform get pod -l apprdma-device-plugin -o wide故障節(jié)點(diǎn)上有 Running 且 Ready 的 PodCSI Node 插件就緒kubectl -n platform get pod -l appstorage-csi-node -o wide故障節(jié)點(diǎn)上有 Running 且 Ready 的 Pod節(jié)點(diǎn)擴(kuò)展資源恢復(fù)kubectl get node cn-storage-07 -o jsonpath{.status.capacity.rdma\.io/rdma_shared_device}輸出等于物理網(wǎng)卡總量如32CSINode 對(duì)象恢復(fù)kubectl get csinode cn-storage-07 -o jsonpath{.spec.drivers}能看到存儲(chǔ)驅(qū)動(dòng)名業(yè)務(wù) Pod 掛載成功kubectl get pod business-podRunning 狀態(tài)事件里無(wú) FailedMount對(duì)于已經(jīng) Pending 的 Pod等資源恢復(fù)后我會(huì)刪幾個(gè)代表性地觀察確認(rèn)能調(diào)度成功后再批量處理。實(shí)際場(chǎng)景里調(diào)度器會(huì)周期性地做調(diào)度重試一部分 Pending Pod 在資源恢復(fù)后會(huì)自動(dòng)起來(lái)如果等了很久還沒(méi)動(dòng)手動(dòng)刪掉觸發(fā)重建是最直接的。5. 排障方法論沉淀別再被“存儲(chǔ)端錯(cuò)誤”牽著走5.1 這類(lèi)故障的共同套路上層錯(cuò)誤信息最不可信這次事故給我最大的啟發(fā)是多層依賴(lài)系統(tǒng)里的故障最外層的錯(cuò)誤提示往往最具誤導(dǎo)性。CSI 掛載失敗明明只是一個(gè)“結(jié)果”卻因?yàn)閳?bào)錯(cuò)內(nèi)容指向存儲(chǔ)服務(wù)端把大量人力拖進(jìn)了存儲(chǔ)排查的泥潭。類(lèi)似的事件在 GPU 資源消失、FPGA 資源消失、SR-IOV VF 異常的場(chǎng)景里也會(huì)出現(xiàn)。共同套路是上層應(yīng)用報(bào)“設(shè)備不存在”或“資源不足”物理硬件實(shí)際是健康的問(wèn)題出在中間層的“資源上報(bào)代理”身上。遇到這種“硬件沒(méi)壞但上層看不見(jiàn)”的矛盾第一時(shí)間應(yīng)該把目光放到資源上報(bào)鏈路上檢查對(duì)應(yīng) Device Plugin 的存活狀態(tài)而不是先懷疑硬件和存儲(chǔ)。5.2 資源消失類(lèi)問(wèn)題的十分鐘排查手冊(cè)這幾步驟是我踩過(guò)坑之后總結(jié)出來(lái)的分享給同樣維護(hù)平臺(tái)的朋友看業(yè)務(wù) Pod 事件是FailedScheduling調(diào)度器看不到資源還是FailedMount掛載執(zhí)行層出問(wèn)題先分清楚看節(jié)點(diǎn) Capacity直接搜擴(kuò)展資源名確認(rèn)資源到底有沒(méi)有從節(jié)點(diǎn)上報(bào)中消失看 Device Plugin 分布kubectl -n platform get pod -o wide | grep device一眼就能發(fā)現(xiàn)故障節(jié)點(diǎn)上有沒(méi)有對(duì)應(yīng)的 Pod看 DaemonSet 的調(diào)度事件如果有untolerated taint問(wèn)題基本就是污點(diǎn)容忍對(duì)比節(jié)點(diǎn) Taint 與 DaemonSet Toleration# 快速列出所有節(jié)點(diǎn)的污點(diǎn) kubectl get nodes -o jsonpath{range .items[*]}{.metadata.name}{\t}{.spec.taints}{\n}{end} # 快速查看某個(gè) DaemonSet 的容忍 kubectl -n platform get ds ds-name -o jsonpath{range .spec.template.spec.tolerations[*]}{.key}{:}{.effect}{\n}{end}前后一對(duì)照缺哪條補(bǔ)哪條。5.3 平臺(tái)組件污點(diǎn)容忍治理的三個(gè)建議這次事故本質(zhì)上是變更管理疏漏而不是技術(shù)原理問(wèn)題。維護(hù)腳本加了污點(diǎn)卻忘了回收這種操作上的坑單純靠人盯是盯不住的。我后來(lái)在團(tuán)隊(duì)里推了三件事第一統(tǒng)一維護(hù)污點(diǎn)的前綴和生命周期。所有運(yùn)維腳本的臨時(shí)污點(diǎn)統(tǒng)一使用某個(gè)前綴比如node.maint/并且強(qiáng)制腳本在結(jié)束前調(diào)用回收邏輯。回收動(dòng)作要做到冪等哪怕重復(fù)執(zhí)行也不會(huì)出錯(cuò)。第二給關(guān)鍵 DaemonSet 建立“容忍覆蓋清單”。每一個(gè)平臺(tái)基礎(chǔ)組件都要在發(fā)布的 YAML 里顯式聲明支持哪些污點(diǎn)。定期把節(jié)點(diǎn)上的實(shí)際污點(diǎn)集合與所有 DaemonSet 容忍集合做一次求差集任何新出現(xiàn)的污點(diǎn)如果沒(méi)有任何容忍項(xiàng)立刻報(bào)警。第三變更前做一次“污點(diǎn)常見(jiàn)面測(cè)試”。模擬在目標(biāo)節(jié)點(diǎn)上打一個(gè)代表未來(lái)維護(hù)場(chǎng)景的新污點(diǎn)觀察核心組件是否還能保持存活。這個(gè)操作成本很低但能提前暴露很多配置盲區(qū)。這次事故之后我養(yǎng)成了一個(gè)有點(diǎn)“反直覺(jué)”的習(xí)慣每次看到 CSI 掛載失敗第一件事不是查存儲(chǔ)而是先看節(jié)點(diǎn)上的平臺(tái)組件健不健康、節(jié)點(diǎn)資源有沒(méi)有變化。這個(gè)習(xí)慣后來(lái)幫我快速度過(guò)了好幾次類(lèi)似事件。說(shuō)實(shí)話(huà)底層平臺(tái)的穩(wěn)定性往往就藏在這些不起眼的污點(diǎn)、容忍和資源上報(bào)配置里。硬件壞了至少能修能換配置上的盲區(qū)如果沒(méi)人發(fā)現(xiàn)它會(huì)一直在那里等著某個(gè)維護(hù)操作把它引爆。