2019年5月8日 星期三

zero downtime in kubernetes

這是 原始文章,這篇不是翻譯,算是個人解讀後的摘要,如果有誤請指正~
原文有更詳細的解說,尤其第四篇文章內有關於 PDB 機制圖文並茂的解說。
要注意的是,這篇講的 zero downtime,指的是因某些需求而必須要主動停機的情況,如何在主動停機時不會中斷服務。


* 定義所遇到的挑戰為何
   假設目前有兩台 worker node,上面已經在跑一些服務。如果想要升級 worker node 的 kernel 時應該如何進行?
   一個方式是先裝新的 worker node join cluster,然後把原本 worker node 關機。           

   這會產生哪些考量?
  a.  假如這個 pods 需要在結束前做一些清理動作,但是關機程序沒有時間讓 pods 完成這些動作。
  b.  假如所有原本的 nodes  一起關機,會產生一段時間的服務中斷。

  首先要如何避免讓 pods reschedule 到原本的 pods 上?
  用 kubectl drain 設定原本的 node,確認所有的 pods 都移走後再關機。

  不過用 drain 的方式還是有兩個因素會造成服務中斷:
  a.  pods 裡的 applicaion 要能處理 TERM signal,因為 kubectl drain 是送 TERM signal 來 evicted pods。
       kubernetes 會在送出 TERM signal 後的一定時間內,去強制結束掉還在執行的 container。
  b.  如果 pods 在新的 node 起來前,舊的 node 上的 pods 就先都被終止,會產生服務中斷。

  Kubernetes 提供了三種機制來處理主動中斷 (例如 drain) 的情況
  a. Graceful termination
  b. Lifecycle hooks
  c. PodDisruptionBudgets


* Gracefully shutting down Pods

  kubectl drain 讓 kubelet 去 delete pod 的流程
  a. kubectl drain 發出通知給指定的 node,讓該 node 的 kubelet 開始準備做 delete pods 的動作
  b. kubelet 會執行 pod 定義裡的 preStop 設定
  c. preStop 執行完後,kubelet 會送出 TERM signal 給 pod
  d. kubelet 送出 TERM signal 後,會等待一個時間 (grace period, 預設是 30 秒) 後強制刪除 pod
 
 也就是在 kubelet 強制刪除 pod 前,有兩個時間點可以讓 pod 做清理的動作。
 例如說,可以設定應用程式收到 TERM signal 時停止接受新的工作,並且在完成所有已收到的工作後自行停止。

  如果沒辦法修改應用程式去處理 TERM signal ,可以設定 pod 的 preStop 去終止應用程式。

  不過在後面這種情況下,應用程式雖然可以透過 preStop 停止,但是 service 那邊在可能還不知道的情況下(見下節說明),還會繼續把 request 往 pods 送過去,使用者就會看到錯誤或服務被拒絕的結果。

* Delaying Shutdown to Wait for Pod Deletion Propagation

  回頭來看,當送出 delete pods 時,整個 kubernetes cluster 會進行哪些處理?
  a. kubelet 會開始進行 delete pod 的流程,如同前一節描述的
  b. 所有 node 上 kube-proxy 會移除該 pod ip 相關的 iptables rules
  c. endpoints controller 會移除和該 pod 相關的 endpoint,也就是會從 Service 中移除該 pod
 
  這就是為什麼透過 preStop 終止 container 後,流量 (request) 還可能會繼續往 pods 送的原因。

  一種應對方式是,確保 kube-proxy 和 endpoints controller 都處理完後,再終止 container。
  不過這方式是非常困難的,因為 node 可能有網路延遲,或者 node 數量非常非常多。

  實際上沒有辦法做到 100% 服務不中斷,只能盡可能做到 99%。
  方法是在 preStop 用 sleep 指令延後終止 container 的動作,在 "Kubernetes in Action" 一書中建議 sleep 的時間是 5~10 秒。

  也就說,如果在這 5~10 秒內,endpoints controller 可以移除相關的 endpoint,就可以在 container 終止前停止把流量送過去,也就不會有中斷服務的情況。

  此時 Deployment 會因為要保持 replicas 設定的數量在新的 node 建立新的 pods。         
  接下來的問題是,假如同時 drain 所有原本的 node,可能產生的問題是,在新的 pods 產生前,所有原本的 pods 都終止了,而造成了服務中斷。

  如果不要同時,而是逐步 drain node 會遇到什麼問題?
  第一台終止的 pod ,可能被安排到另一台原本也要停機的 node 上。也就是該 Deployment 的兩個 replicas 都在同一台準備要停機的 node 上。 當輪到這台 node 要 drain 時,就會造成這個 deployment 的 pods 全都準備終止的情況。下一節會來處理這個處理這個問題。


* Avoiding Outages in your Kubernetes Cluster using PodDisruptionBudgets

    使用 PDB (PodDistruptionBudgets) 這個機制來處理上節最後面提到的問題。
    PDB 是用來設定對於終止 pod 行為的容忍機制。

    如果在 PDB 的 yaml 設定 minAvailable: 1,表示至少要有一個 pod 存活。這可以防止所有 pods 全部一起被 delete 。在上述情境中,會等到新的 node 裡至少有一個 pod 起來後,才會讓原本最後一個 node 裡的 pod 被刪除。


綜合以上幾個設定方式,可以達到 zero downtime 的目標。

沒有留言: