Motivation
Zookeeper 是一個開源的分散式協調服務,它為分散式應用提供了一種集中管理的方式。Zookeeper 允許各種服務進行資料同步,維持 configuration 資訊,以及執行分散式 lock 和 queue 等操作。這對於維護分佈式系統的狀態一致性和可 靠性是非常重要的。
在 cloud native 的架構中,Zookeeper 的穩定性直接影響著依賴它進行協調的各項服務。為了保證 Zookeeper 可以在不同的情況下穩定運作,尤其是在 Cloud 環境下重新部署(deploy)時,我們必須確保它能夠準確地與 K8s assign 的新 IP 進行綁定。我們發現這個過程有時會遇到問題,意即在進行服務發現(DNS 查詢)的過程中可能會出現 Race Condition 導致 Zookeeper pod 沒有辦法正常重啟。
深入了解後,我們發現問題是出自於 K8s 在執行 pod deletion 這個操作時牽涉了兩個非同步的事件,而只要兩個事件的互動順序不同,就會導致 Race Condition 發生預期之外的行為(在這個 case 就是重啟 Zoookeeper 後,new pod 沒辦法 bind 到正確的 IP 上)。
為了解決這個問題,我們探索現有的解法(workaround),並探究如何使用 Kubernetes 中的 graceful shutdown 策略來提高 Zookeeper 重啟時的穩定性,透過實驗我們也驗證了這個方法的有效性。
在這篇文章中,我們將探討造成 Zookeeper 沒辦法 bind 到正確(newly created) IP 的根本原因、介紹現有的workaround 及新的(本文想嘗試的)方法(K8s graceful shutdown approach),並透過實驗了解這個方法的有效性。
Root Cause
Potential Race Condition When Deleting a Pod in K8s
這篇文章詳細地介紹了 K8s 在 delete 一個 pod 時的步驟及 overall 的 process。值得一提的是,當 deletion request 送到 API server 時,會 trigger 兩個平行的事件,DNS Update & Process Termination。
(Deleting the endpoint and deleting the Pod happen at the same time)
其中 DNS Update(上圖的 Event 1)就是負責 notify 監聽 endpoint 的 components (Kube-proxy, Ingress, Core DNS, ...) endpoint 有發生變動(因為 pod 被 delete 了),因此需要去更新他們 maintain 的 iptables;至於 Process Termination(上圖的 Event 2)就是透過 kubelet 去將指定的 application 結束,通常就是送 SIGTERM
給 application。
而當 Event 2 比 Event 1 早做完的話(上圖的 case),其實會面臨一個狀況,就是在 Kube-proxy 及相關的 components 在還沒更新成正確的 iptables 時就結束了 application,這樣就會造成 ingress, Core DNS 將 requests 繼續導到已經不存在的 IP (old IP),發生 downtime issue。
對應 Zookeeper 的狀況,則是 Zookeeper instance 在被 delete 完(Event 2)並重新啟動時,cluster 內的 Core DNS 還沒完成更新(Event 1),造成 Zookeeper 在問 IP 時問到錯的(已經不存在的)IP 而沒有辦法 bind 到新的 IP。