前言
正常維護工作節點的流程
當我們要進行 K8S 節點維護時往往需要執行
kubectl drain
, 等待節點上的 Pod 被驅逐後再進行維護動作。
指令行如下:
kubectl drain NODE
待節點排空後再進行維護操作, 核心更新等。
存在問題嗎?
drain
指令有一個問題, 他不會考慮資源所定義的 UpdateStrategy, 而直接強制驅逐或删除 Pod, 這樣就會導緻 Deployment 或 StatefulSet 資源的 Pod 達不到所設定的政策數.
思考一個案例
- 有一個 Deployment 資源, 它使用了如下配置
副本數為 3, 采用了滾動更新, 并且先啟動完成一個 Pod 後再進行舊 Pod 的删除(最大不可用為0,最小可用為2).replicas: 2 strategy: rollingUpdate: maxSurge: 1 maxUnavailable: 0 type: RollingUpdate
-
當下叢集有 2 個 worker 節點
意味着, 其中一個節點被排程了 2 個 Pod, 其中一個節點被排程了 1 個 Pod.
假設 node1 運作着 pod1 和 pod3, node2 運作着 pod2.
- 這時候 drain node1, 會出現 Deployment 隻有一個 Pod 可用
更糟糕的情況
Deployment 的 Pod 全部運作在需要維護的節點上, 這時候執行
drain
那将是一個災難, 這個 Deployment 在新的Pod啟動之前它無法在對外提供服務了, 恢複的時間取決于新 Pod 的啟動速度。
kubectl-safe-drain 項目
GitHub:
https://github.com/majian159/kubectl-safe-drain一個 kubectl 插件, 用于更為安全的排空節點。
對于 Deployment 和 StatefulSet 資源會根據其配置的更新政策先将Pod排程到其它可用節點。
邏輯和原理
- 先将需要排空的節點标記為不可排程 (kubectl cordon)
- 在找到該節點上的 Deployment 和 StatefulSet 資源
- 修改 Deployment 和 StatefulSet 的 PodTemplate, 讓K8S根據對應的更新政策重新部署Pod, 這時候需要排空的節點不可被排程, 進而達到先将排空節點中的Pod安全重建到其它節點的邏輯。
目前支援安全遷移的資源
- Deployment
- StatefulSet
效果
首先我們有一個 Deployment 配置如下
spec:
replicas: 2
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
操作前有兩個可用 Pod

執行 safe-drain
後
safe-drain
檢視 Deployment 變化過程
檢視 Pod 變化過程
流程簡述
從 Deployment watch 的資訊中可見最小 Ready 數沒有小于 2, 從 Pod watch 的資訊中可見 kind-worker2 上承載了 2 個準備就緒的 nginx Pod, 也就是說 nginx 從 kind-worker 安全的移動到了 kind-worker2 節點上。
與 PDB (Pod Disruption Budget) 有什麼差別?
PDB 隻會保障 Pod 不被驅逐, 而不會幫助它在其它可用節點上重建。
使用了 PDB 後能防止服務不可用的尴尬情況,但它還是需要人工手動遷移 Pod。
理想的情況是搭配 PDB 使用, 防止嚴苛情況下服務不可用的問題。
安裝
二進制檔案
Linux
curl -sLo sdrain.tgz https://github.com/majian159/kubectl-safe-drain/releases/download/v0.0.1-preview1/kubectl-safe-drain_0.0.1-preview1_linux_amd64.tar.gz \
&& tar xf sdrain.tgz \
&& rm -f sdrain.tgz \
&& mv kubectl-safe-drain /usr/local/bin/kubectl-safe_drain
macOS
curl -sLo sdrain.tgz https://github.com/majian159/kubectl-safe-drain/releases/download/v0.0.1-preview1/kubectl-safe-drain_0.0.1-preview1_darwin_amd64.tar.gz \
&& tar xf sdrain.tgz \
&& rm -f sdrain.tgz \
&& mv kubectl-safe-drain /usr/local/bin/kubectl-safe_drain
Windows
https://github.com/majian159/kubectl-safe-drain/releases/download/v0.0.1-preview1/kubectl-safe-drain_0.0.1-preview1_windows_amd64.tar.gz基于 Krew
curl -O https://raw.githubusercontent.com/majian159/kubectl-safe-drain/master/krew.yaml \
&& kubectl krew install --manifest=krew.yaml \
&& rm -f krew.yaml
使用
kubectl safe-drain NODE
# safe-drain并沒有調用 drain指令, 而是利用了 SchedulingDisabled 機制
# 是以如有需要可以繼續使用 drain 指令來確定節點被驅逐
kubectl drain NODE
TODO
- 考慮節點親和力和節點選擇器的情況
- 輸出更為友好的提示資訊
寫在最後
該項目部分代碼源于 kubectl 項目。