聲明式API
所謂「聲明式」,指的就是我只須要提交一個定義好的 API 對象來「聲明」,我所指望的狀態是什麼樣子nginx
「聲明式 API」容許有多個 API 寫端,以 PATCH 的方式對 API 對象進行修改,而無需關心本地原始 YAML 文件的內容git
Kubernetes 項目才能夠基於對 API 對象的增、刪、改、查,在徹底無需外界干預的狀況下,完成對「實際狀態」和「指望狀態」的調諧(Reconcile)過程編程
聲明式 API,纔是 Kubernetes 項目編排能力「賴以生存」的核心所在api
AdmissionControl機制
在K8S中 當一個Pod或者任何一個API對象提交給APIServer以後 總須要作一些
初始化的工做 好比在自動爲Pod添加上某些標籤
這些功能的實現依賴於一組Admission Controller來實現 能夠選擇性的編譯到
APIServer中 在API對象建立以後會被當即調用
須要從新編譯本身的APIServer添加本身的規則 比較麻煩緩存
熱插拔Admission機制(Dynamic Admission)
Istio實現機制
編寫一個用來爲全部Pod自動注入本身定義的容器的Initializer
這個Initializer的定義會以ConfigMap的方式進行保存在集羣中
在Initializer更新用戶的Pod對象的時候,必須使用PATCH API來完成
Istio將一個編寫好Initializer作爲一個Pod運行在k8s集羣中
在Pod YAML文件提交給K8S以後 在建立好的Pod的API對象上自動添加Envoy容器配置服務器
Initializer初始化器介紹數據結構
Initializer能夠是以一個Pod的形式運行在集羣當中
Initializer就是初始化器的意思 就是在任何一個API對象剛剛建立成功後立刻調用初始化器給這個對象添加一些自定義的屬性併發
Initializer 要作的工做,就是把這部分單獨定義相關的字段,自動添加到用戶提交的Pod的API對象裏.但是,用戶提交的 Pod 裏原本就有containers字段和volumes字段mvc
因此Kubernetes 在處理這樣的更新請求時,就必須使用相似於git merge 這樣的操做,才能將這兩部份內容合併在一塊兒 最後按照合併後的結果建立容器和掛載卷等
Initializer在更新用戶pod對象的時候 必須使用PATCH API來完成 而PATCH API正是聲明式API的最主要的能力
k8s可以對API對象進行在線更新的能力app
Initializer邏輯流程
1.首先從ConfigMap中拿到相關數據建立一個空的Pod對象
2.使用新舊兩個Pod對象作爲參數調用k8s中TwoWayMergePatch返回patch數據
3.經過client發起PATCH請求更新原來的Pod API對象,此時Pod還只是個API對象 沒有被真正的建立出來
4.根據Merge後的Pod對象定義 建立出Pod
API對象都有revision因此apiserver處理merge的流程跟git Server是同樣的
Initializer和Preset都能注入Pod配置
preset至關於initializer的子集,比較適合在發佈流程裏處理比較簡單的狀況 initializer是要開發代碼
Initializer與新的pod在git merge衝突時就不會出入成功
kubectl apply是經過mvcc實現的併發寫
envoy和nginx比的優勢
envoy的設計支持熱更新和熱重啓很適合聲明式規則的開發範式
envoy提供了api形式的配置入口,更方便作流量治理
envoy編程友好的api,方便容器化,配置方便
nginx的reload須要把worker進程退出比較面向命令
initializer步驟發生在Pod建立以前
initializer發生在admission階段,這個階段完成後pod纔會建立出來.並非先建立好原始Pod而後再把initializer裏面對原始Pod的修改進行滾動更新
一個用戶提交的 Pod 對象裏,就會被自動加上 Envoy容器相關的字段 使用 Kubernetes的Initializer特性,完成 Envoy 容器「自動注入」的原理
聲明式API設計
API對象在Etcd裏的完整路徑由Group/Version/Resource組成 同一種API對象能夠有多個Version k8s進行API版本化的重要手段
1.首先匹配API對象的組
Pod Node等核心API對象是不須要Group的 它們的Group是"" 直接從/api開始查找
2.根據完整路徑找到k8s的類型定義後使用用戶提交的YAML文件中的字段建立一個實例
在建立實例的過程當中會進行一個Convert操做 把用戶提交的YAML文件轉成一個super version對象
它是API資源類型全部版本的字段全集 方便用戶提交不一樣API版本的YAML
3.進行API對象的Initializer操做和Validation操做
validation操做驗證對象中各個字段的合法性 驗證後保存到Registry數據結構中 一個API對象的定義能在Registry裏能查到 那麼它就是一個有效的k8s API對象
4.把super version對象轉換成用戶提交版本的對象 序列化後保存到etcd中
自定義API資源類型(CRD)
成功建立CRD以後 只是完成聲明式API的一半工做
由於尚未爲這個CRD建立控制器 因此在k8s中只能對這個CRD進行增刪改查操做
但沒法對CRD對象發生增刪改的操做時觸發對應的業務邏輯 必須爲每一個CRD建立一個對應的CRD控制器來實現當CRD對象發生變化時候觸發控制器裏面的業務邏輯代碼
「registry」的做用就是註冊一個類型(Type)給 APIServer.其中Network(CRD)資源類型在服務器端的註冊的工做APIServer 會自動幫咱們完成
但與之對應的,咱們還須要讓客戶端也能「知道」Network 資源類型的定義.這就須要咱們在項目裏添加一個 register.go 文件(v1/register.go)
自定義控制器
1.編寫main函數
2.編寫自定義控制器定義
3.編寫控制器的業務邏輯
Informer機制
Reflector使用ListAndWatch方法來獲取並監聽對象實例的變化 一旦任何一個實例有任何變化Reflector都會收到事件通知
收到通知後把(事件和對象)的組合存入一個先進先出的隊列中
Informer不斷從隊列中讀取對象 判斷事件的類型 而後建立或者更新本地對象的緩存 同步本地緩存的工做 是Informer的重要職責
Informer的第二個職責 就是根據事件的類型 觸發事先註冊好的ResourceEventHandler
經過監聽到的事件變化,Informer 就能夠實時地更新本地緩存,而且調用這些事件對應的EventHandler了
每通過 resyncPeriod 指定的時間,Informer 維護的本地緩存,都會使用最近一次 LIST 返回的結果強制更新一次,從而保證緩存的有效性
所謂的 Informer,就是一個自帶緩存和索引機制,能夠觸發 Handler 的客戶端庫。
這個本地緩存在 Kubernetes 中通常被稱爲 Store,索引通常被稱爲 Index.Informer 使用了 Reflector 包,它是一個能夠經過 ListAndWatch 機制獲取並監視 API 對象變化的客 戶端封裝.Reflector 和 Informer 之間,用到了一個「增量先進先出隊列」進行協同.
而 Informer與你要編寫的控制循環之間,則使用了一個工做隊列來進行協同.在實際應用中,除了控制循環以外的全部代碼,實際上都是 Kubernetes 爲你自動生成的
控制循環的邏輯
1.等待Informer完成一次本地緩存的數據同步操做
2.經過goroutine啓動一個或多個無限循環任務
3.在每個循環週期中 執行的就是用戶真正關心的業務邏輯代碼
1.若是控制循環在Informer緩存中獲取不到相應的對象的信息 說明要執行刪除邏輯
2.若是從緩存中獲取到對象信息 就執行控制器模式對比指望狀態和實際狀態 指望狀態來自於etcd 實際狀態來自於集羣自己
這套流程不只能夠用在自定義API資源上也徹底能夠用在Kubernetes原生的默認API 對象上
這就使得在這個自定義控制器裏面,我能夠經過對自定義API對象和默認API 對象進行協同,從而實現更加複雜的編排功能
只須要關注如何拿到「實際狀態」,而後如何拿它去跟「指望狀態」作對比,從而決定接下來要作的業務邏輯便可 這個就是Kubernetes API編程範式的核心思想