如何優雅的維護 K8S Worker 節點

KAnts發表於2020-04-30

前言

正常維護工作節點的流程

當我們要進行 K8S 節點維護時往往需要執行 kubectl drain, 等待節點上的 Pod 被驅逐後再進行維護動作。
命令列如下:

kubectl drain NODE

待節點排空後再進行維護操作, 核心升級等。

存在問題嗎?

drain 命令有一個問題, 他不會考慮資源所定義的 UpdateStrategy, 而直接強制驅逐或刪除 Pod, 這樣就會導致 Deployment 或 StatefulSet 資源的 Pod 達不到所設定的策略數.

思考一個案例

  1. 有一個 Deployment 資源, 它使用瞭如下配置
     replicas: 2
     strategy:
         rollingUpdate:
         maxSurge: 1
         maxUnavailable: 0
     type: RollingUpdate
    
    副本數為 3, 採用了滾動更新, 並且先啟動完成一個 Pod 後再進行舊 Pod 的刪除(最大不可用為0,最小可用為2).
  2. 當下叢集有 2 個 worker 節點
    意味著, 其中一個節點被排程了 2 個 Pod, 其中一個節點被排程了 1 個 Pod.
    假設 node1 執行著 pod1 和 pod3, node2 執行著 pod2.
  3. 這時候 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排程到其它可用節點。

邏輯和原理

  1. 先將需要排空的節點標記為不可排程 (kubectl cordon)
  2. 在找到該節點上的 Deployment 和 StatefulSet 資源
  3. 修改 Deployment 和 StatefulSet 的 PodTemplate, 讓K8S根據對應的更新策略重新部署Pod, 這時候需要排空的節點不可被排程, 從而達到先將排空節點中的Pod安全重建到其它節點的邏輯。

目前支援安全遷移的資源

  1. Deployment
  2. StatefulSet

效果

首先我們有一個 Deployment 配置如下

spec:
    replicas: 2
strategy:
    type: RollingUpdate
    rollingUpdate:
        maxSurge: 1
        maxUnavailable: 0

操作前有兩個可用 Pod

執行 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

  1. 考慮節點親和力和節點選擇器的情況
  2. 輸出更為友好的提示資訊

寫在最後

該專案部分程式碼源於 kubectl 專案。

相關文章