kubernetes list/watch設計原理

鋼閘門發表於2021-12-12

overview

kubernetes的設計裡面大致上分為3部分:

  • API驅動型的特點 (API-driven)
  • 控制迴圈(control loops)與 條件觸發 (Level Trigger
  • API的可延伸性

而正因為這些設計特性,才使得kubernetes工作非常穩定。

什麼是Level Trigger與 Edge trigger

看到網上有資料是這麼解釋兩個屬於的:

  • 條件觸發(level-trigger,也被稱為水平觸發)LT指: 只要滿足條件,就觸發一個事件(只要有資料沒有被獲取,就不斷通知)。

  • 邊緣觸發(edge-trigger)ET: 每當狀態變化時,觸發一個事件。

通過查詢了一些資料,實際上也不明白這些究竟屬於哪門科學中的理論,但是具體解釋起來看的很明白。

LEVEL TRIGGERING:當電流有兩個級別,VHVL。代表了兩個觸發事件的級別。如果將VH 設定為LED在正時鐘。當電壓為VH時,LED可以在該時間線任何時刻點亮。這稱為LEVEL TRIGGERING,每當遇到VH 時間線就會觸發事件。事件是在時間內的任何時刻開始,直到滿足條件。

Edge TRIGGERING:

如圖所示,會看到上升線與下降線,當事件在上升/下降邊緣觸發時(兩個狀態的交點),稱為邊緣觸發(Edge TRIGGERING:)。

如果需要開啟LED燈,則當時鍾從VL轉換到VH時才會亮起,而不是一家處在對應的時鐘線上,僅僅是在過渡時亮起。

為什麼kubernetes使用Level Trigger而不使用Edge trigger

如圖所述,兩種不同的設計模式,隨著時間形狀進行相應,當系統在由高轉低,或由低轉高時,系統處在關閉或者不可控的異常狀態下,應如何觸發對應的事件呢。

換一種方式來來解釋,比如說通過 加法運算,如下,i=3,當給I+4作為一個操作觸發事件。

# let i=3
# let i+=4
# let i
# echo $i
7

當為Edge trigger時操作的情況下,將看到 i+4 ,而在 level trigger 時看到的是 i=7。這裡將會從``i+4` 一直到下一個訊號的觸發。

訊號的干擾

通常情況下,兩者是沒有區別的,但在大規模分散式網路環境中,有很多因素的影響下,任何都是不可靠的,在這種情況下會改變了我們對事件訊號的感知。

如圖所示,圖為Level TriggerEdge trigger 的訊號發生模擬,在理想情況下,兩者間並沒有什麼不同。

一次中斷場景

由圖可知,Edge trigger當在恰當的時間點發生訊號中斷,會對整個流產生很大的影響,甚至改變了整個狀態,對於較少的干擾並不會對有更好的結果,而單次的中斷,使Edge trigger錯過了從高到低的變化,而 level trigger 基本上保證了整個訊號量的所有改變狀態。

兩次中斷的場景下

由圖可看到,訊號的上升和下降中如果存在了中斷,Edge trigger 丟失了上升的訊號,但最終狀態是正確的。

在訊號狀態的兩次變化時發生了兩次中斷,Level TriggerEdge trigger 之間的區別很明顯,Edge trigger 的訊號錯過了第一次上升,而Level Trigger 保持了最後觀察到的狀態,知道拿到了其他狀態,這種模式保證了得到的訊號基本的正確性,但是發生延遲到中斷恢復後。

通過運算來表示兩種模式的變化情況

完整的訊號

# let i=2

# let i+1
# let i-=1
# let i+1

# echo $i
3

Edge trigger

# let i=2

# let i+1  
(# let i-=1) miss this
# let i+1

# echo $i
4

如何使理想狀態和實際狀態一樣呢?

在Kubernetes中,不僅僅是觀察物件的一個訊號,還觀察了其他兩個訊號,叢集的期待狀態與實際狀態,期望的狀態是使用者期望叢集所處的狀態,如我執行了2個例項(pod)。在最理想的場景下,叢集的實際狀態與期待狀態是相同的,但這個過程會受到任意的外界因素干擾被影響下,實際狀態與理想狀態發生偏差。

Kubernetes必須接受實際狀態,並將其與所需狀態調和。不斷地這樣做,採取兩種狀態,確定其之間的差異,並糾正其不斷的更改,以使實際狀態達到理想狀態。

如圖所示,在一個Edge trigger 中,最終的結果很可能會與理想中的結果發生偏差。

當初始例項為1時,並希望擴充套件為5個副本,然後再向下縮容到2個副本,則Edge trigger環境下將看到以下狀態:系統的實際狀態不能立即對這些命令作出反應。正如圖所述,當只有3個副本在執行時,它可能會終止3個副本。這就給我們留下了0個副本,而不是所需的2個副本。

# let replicas=1
# let replicas += 4 # 此時副本數為5,但是這個過程需要時間而不是立即完成至理想狀態
# let replicas -= 3 # 當未完成時又接到訊號的變化,此時副本數為3,減去3,很可能實際狀態為0,與理想狀態2發生了偏差

而使用Level Trigger時,會總是比較完整的期望狀態和實際狀態,直到實際狀態與期望狀態相同。這大大減少了狀態同步間(錯誤)的產生。

summary

每一種觸發器的產生一定有其道理,Edge trigger本身並不是很差,只是應用場景的不同,而使用的模式也不同,比如nginx的高效能就是使用了Edge trigger模型,如nginx使用了 Level trigger在大併發下,當發生了變更訊號等待返回時,發生大量客戶端連線在偵聽佇列,而Edge trigger模型則不會出現這種情況。

綜上所述,kubernetes在設計時,各個元件需要感知資料的最終理想狀態,無需擔心錯過資料變化的過程。而設計kubernentes系統訊息通知機制(或資料實時通知機制),也應滿足以下要求:

  • 實時性(即資料變化時,相關元件感覺越快越好)。訊息必須是實時的。在list/watch機制下,每當apiserver資源有狀態變化事件時,都會及時將事件推送到客戶端,以保證訊息的實時性。

  • 訊息序列:訊息的順序也很重要。在併發場景下,客戶端可能會在短時間內收到同一資源的多個事件。對於關注最終一致性的kubernetes來說,它需要知道哪個是最新的事件,並保證資源的最終狀態與最新事件所表達的一致。kubernetes在每個資源事件中都攜帶一個resourceVersion標籤,這個標籤是遞增的。因此,客戶端在併發處理同一資源的事件時,可以比較resourceVersion,以確保最終狀態與最新事件的預期狀態一致。

  • 訊息的可靠性,保證訊息不丟失或者有可靠的重新獲取的機制(比如 kubeletkube-apisever之間的網路波動(network flashover )需要保證kubelet在網路恢復後可以接收到網路故障時產生的訊息)。

正是因為Kubernetes使用了 Level trigger才讓叢集更加可靠。

Reference

nginx-event-driven-architecture

What-is-meant-by-edge-triggering-and-level-triggering

相關文章