Kubernetes(k8s)是一款開源的優秀的容器編排調度系統,其自己也是一款分佈式應用程序。雖然本系列文章討論的是互聯網架構,可是k8s的一些設計理念很是值得深思和借鑑,本人並不是運維專家,本文嘗試從本身看到的一些k8s的架構理念結合本身的理解來分析 k8s在穩定性、簡單、可擴展性三個方面作的一些架構設計的考量。node
咱們知道,k8s定義了許多資源(好比Pod、Service、Deployment、ReplicaSet、StatefulSet、Job、CronJob
等),在管理資源的時候咱們使用聲明式的配置(JSON、YAML等)來對資源進行增刪改查操做。咱們提供的這些配置就是描述咱們但願這些資源最終達成的一個目標狀態,叫作Spec,k8s會對觀察資源獲得資源的狀態,叫作Status,當Spec!=Status的時候,k8s的各類控制管理程序就會起做用,進行各類操做使得資源最終能夠達到咱們指望的Spec。這種聲明式的管理方式和命令式管理方式相比,雖然沒有後者這麼直接,可是容錯性會很強,後面一節會進一步詳細提到這點。並且,這種管理方式很是的簡潔,只要用戶提供合適的Spec定義便可,並不須要對外暴露幾十個幾百個不一樣的API來實現對資源的各個方面作改變。固然,咱們也能夠靈活的對一些重要的動做單獨開闢管理API(好比擴容,好比修改鏡像),這些API底層作的操做就是修改Spec,底層是統一的。算法
在以前第一季的系列文章S1E2中,我分享過任務表的設計,其實這裏的聲明式對象管理就是相似這樣的思想,咱們在數據庫中保存的是咱們要的結果,而後由不一樣的任務Job來進行處理最終實現這樣的結果(同時也會保存組件當前的狀態到數據庫),即便任務執行失敗也無妨,後續的任務會繼續重試,這種方式是可靠性最高的。數據庫
K8s使用的是聲明式的管理方式,也就是水平觸發。另外一種作法是叫作命令式的管理,也就是邊緣觸發。好比咱們在作支付系統,用戶充值100元,提現100元而後又充值100元,對於命令式管理就是三條命令。若是提現請求丟失了,用戶帳戶的餘額就出錯了,這確定是不能接受的,命令式管理或邊緣觸發必定須要配合補償。而聲明式的管理就是告訴系統,用戶在進行了三次操做後的餘額分別是100、0和100,最終就是100,即便提現請求丟失了,最終用戶的餘額就是100。編程
來看下下圖的例子,在網絡良好的狀況下,邊緣觸發沒任何問題。咱們進行了開、關、開三次操做,最後的狀態是0。設計模式
在網絡出現問題的時候,丟失了關這個操做,對於邊緣觸發,最終停留在了2這個錯誤的狀態。對於水平觸發沒有這個問題,雖然當中有一段時間網絡很差,狀態錯誤停留在了1,可是網絡恢復後咱們立刻能夠感知到當前的狀態應該是0,狀態又能回到0,最終狀態也能回到正確的1。試想一下,若是咱們對咱們的Pod進行擴容縮容,若是每次告知k8s應該增長或減小多少個Pod(的這種命令式方式),最終極可能由於網絡問題,Pod的狀態不是咱們指望的。更好的作法是告訴k8s咱們但願的狀態,無論如今網絡是否有問題,某個管理組件是否有問題,pod是否有問題,最終咱們指望k8s幫咱們調整到咱們指望的狀態,寧肯慢也不要錯。api
(圖來自這裏)緩存
咱們知道etcd是基於Raft協議的分佈式鍵值數據庫/協調系統,自己推薦使用三、五、7這樣奇數節點構成集羣實現高可用。對於Master節點,咱們能夠在每個節點都部署一個etcd,這樣節點上的API Server能夠和本地的etcd直接通信,而API Server由於是輕(無)狀態的,因此能夠在以前使用負載均衡器作代理,無論是Node節點也好仍是客戶端也好均可以由負載均衡分發請求到合適的API Server上。對於相似於Job的Controller Manager以及Scheduler,顯然不適合多個節點同時運行,因此它們都會採用搶佔方式選舉Leader,只有Leader能承擔工做任務,Follower都處於待機狀態。總體結構以下圖所示:安全
咱們能夠想一下其它一些分佈式系統的高可用方案,以及咱們本身設計的系統的高可用方案,無非就是這三種大模式:網絡
經過前面的介紹咱們大概知道了k8s的一個設計原則是etcd會處於API Server以後,集羣內的各類組件是沒法直接和數據庫對話的,不只僅由於把數據庫直接暴露給各組件會特別混亂,更重要的是誰均可以直接讀寫etcd會很是不安全,須要統一通過API Server作身份認證和鑑權等安全控制(後面咱們會提到API Server的插件鏈)。架構
對於k8s集羣內的各類資源,k8s的控制管理器和調度器須要感知到各類資源的狀態變化(好比建立),而後根據變化事件履行本身的管理職責。考慮到解耦,顯然這裏有MQ的需求,各類管理組件能夠監聽各類資源的狀態變化事件,不須要相互感知到對方的存在,本身作本身的事情便可。若是k8s還依賴一些消息中間件實現這個功能,那麼總體的複雜度會上升,並且還須要對消息中間件進行一些安全方面的定製。
K8s給出的實現方式是仍然使用API Server來充當簡單的消息總線的角色,全部的組件經過watch機制創建HTTP長連接來隨時獲悉本身感興趣的資源的變化事件,完成本身的功能後仍是調用API Server來寫入咱們組件新的Spec,這份Spec會被其它管理程序感知到而且進行處理。Watch的機制是推的機制,能夠實時對變化進行處理,可是咱們知道考慮到網絡等各類因素,事件可能丟失,組件可能重啓,這個時候咱們須要推拉結合進行補償,所以API Server還提供了List接口,用於在watch出現錯誤的時候或是組件重啓的時候同步一次最新狀態。經過推拉結合的list-watch機制知足了時效性需求和可靠性需求。
咱們來看一下這個圖,這個圖展現了客戶端建立一個Deployment後k8s大概的工做過程:
組件初始化階段:
集羣資源變動操做:
能夠看到基於list-watch的API Server實現了簡單可靠的消息總線的功能,基於資源消息的事件鏈,解耦了各組件之間的耦合,配合以前提到的基於聲明式的對象管理又確保了管理穩定性。從層次上來講,master的組件都是控制面的組件,用來控制管理集羣的狀態,node的組件是執行面的組件,kubelet是一個無腦執行者的角色,它們的交流橋樑是API Server的各類事件,kubelet是沒法感知到控制器的存在的。
以下圖所示,API Server實現了基於插件+過濾器鏈的方式(好比咱們熟知的Spring MVC的攔截器鏈)來實現資源管理操做的前置校驗(身份認證、受權、准入等等)。
整個流程會有哪些環節呢:
若是是刪除資源,還會有額外的一些環節:
對於複雜的流程式的操做,採用職責鏈+處理鏈+插件的方式來實現是很常見的作法。你可能會說這個API Server的設計整體上就不簡單,怎麼有這麼多環節,其實這纔是最簡單的作法,每個環節都有獨立的插件來運做(插件能夠獨立更新升級,也能夠根據需求動態插拔配置),每個插件只是作本身應該作的事情,若是沒有這樣的設計,恐怕會出現1萬行代碼的一個大方法。
如圖所示,相似於API Server的鏈式設計,Scheduler在作Pod調度算法的時候也採用了鏈式設計:
常見的predicate算法有:
常見的priority算法有:
好比咱們在作相似路由系統這種業務系統的時候能夠借鑑這種設計模式。簡單一詞在於每個小組件簡單,它們能夠組合起來構成複雜的規則系統,這種設計比把全部邏輯堆在一塊兒簡單的多。
K8s的設計理念是相似Linux的分層架構:
以前介紹的一些組件大多數位於核心層和應用層。在更上層的管理層和接口層,咱們每每會作更多的一些二次開發。在以前的文章中我也介紹過,對於複雜的微服務互聯網系統,咱們也應該把微服務進行分層,從下到上分爲基礎服務、業務服務、聚合業務服務等,每一層的服務聚合下層實現一些業務邏輯,不但能夠作到服務重用,並且上層多變的業務服務的變更能夠不影響下層基礎設施的搭建。
除了k8s大量內部組件的實現使用了插件的架構,k8s在總體設計上就把核心和外部的一些資源和服務抽象爲了統一的接口,能夠插件方式插入具體的實現,以下圖所示:
CNI、CSI、CRI咱們比較熟悉了,其它更多的抽象接口這裏就不描述了,k8s就像一個大主板,主板上有各類內存、CPU、IO、網絡方面的接口,具體的實現k8s自己並不關心,用戶和社區甚至能夠根據的須要實現本身的插件。
我以爲這點是最了不得的最困難的,不少時候咱們在設計一個系統的時候一開始是沒法定義出抽象接口的,由於咱們不知道未來會面對什麼樣的實現,只有到實現愈來愈多後咱們才能抽象出接口才能制定標準。
K8s在存儲方面的解耦設計特別值得一提。以下圖所示,咱們來看一下k8s在存儲這塊的解耦設計:
(圖引自Kubernetes in Action一書)
咱們要作的事情很明確,Pod須要綁定存儲資源:
K8s中除了存儲抽象的V、PV、PVC、SC,還有其它的一些組件也有相似層次的抽象以及動態綁定的理念。
咱們在使用OO語言進行編程的時候,很天然知道咱們須要先定義類,而後再實例化類來建立對象,若是類特別複雜(有不一樣的實現)的話,咱們可能會使用工廠模式(或反射,外層傳入目標類型名稱)來建立對象。能夠和k8s存儲抽象比較一下,是否是這個意思,這其實就是一種解耦的方式,在架構設計中,甚至表結構設計中,咱們徹底能夠引入類和實例的概念。好比工做流系統的工做流能夠認爲是一個類模板,每一次發起的工做流就是這個工做流的實例。
好了,本文大概窺探了一下k8s的架構,不知道你是否感覺到了k8s的精良設計,對內考慮了高可用以及高可靠,對外考慮到了高可擴展性。幾乎任何操做都容許失敗,最終實現一致的狀態,幾乎任何組件都容許擴展和替換,讓用戶實現本身的定製需求。
若是你的業務系統也是一套複雜的資源協調系統(k8s抽象的是運維相關的資源,咱們的業務系統能夠抽象的是其它資源),那麼k8s的設計理念有至關多的點能夠借鑑。舉一個例子,咱們在作一套很複雜的流程引擎,咱們就能夠考慮: