Kubernetes聲明式API與編程范式


聲明式API vs 命令時API

計算機系統是分層的,也就是下層做一些支持的工作,暴露接口給上層用。注意:語言的本質是一種接口。

計算機的最下層是CPU指令,其本質就是用“變量定義+順序執行+分支判斷+循環”所表達的邏輯過程。計算機應用的最上層是實現人類社會的某種功能。所以所有計算機編碼的過程,就是用邏輯表達現實的過程。層與層之間定義的借口,越接近現實的表達就叫越“聲明式”(declarative),越接近計算機的執行過程就叫越“命令式”(imperative)。注意這不是絕對的概念,而是相對的概念。

當接口越是在表達“要什么”,就是越聲明式;越是在表達“要怎樣”,就是越命令式。SQL就是在表達要什么(數據),而不是表達怎么弄出我要的數據,所以它就很“聲明式”。C++就比C更聲明式,因為面向對象本身就是一種聲明式的體現。HTML也很聲明式,它只描述我要一張什么樣的表,並不表達怎么弄出一張表。

越是聲明式,意味着下層要做更多的東西,或者說能力越強。也意味着效率的損失。越是命令式,意味着上層對下層有更多的操作空間,可以按照自己特定的需求要求下層按照某種方式來處理。

Kubernetes聲明式API

想要使用Kubernetes 的 API 對象,需要編寫一個對應的 YAML 文件交給 Kubernetes,而聲明式API,則為kubectl apply 命令。而先 kubectl create,再 replace 的操作,稱為命令式配置文件操作,並不是聲明式API。

kube-apiserver 在響應命令式請求(如kubectl replace)的時候, 一次只能處理一個寫請求,否則會有產生沖突的可能;而對於聲明式請求(如kubectl apply),一次能處理多個寫操作,並且具備 Merge 能力。

聲明式 API是 Kubernetes 項目編排能力“賴以生存”的核心所在:

  • 首先,“聲明式”就是提交一個定義好的API對象來聲明所期望的狀態是什么

  • 其次,聲明式API允許有多個API寫端,以PATCH的方式對API對象進行修改,而無需關心本地原始YAML文件的內容

    RESTful 使用POST來創建一個資源,使用PUT或者PATCH來更新一個資源
    
    區別是:
    – PUT用來整體更新一個資源,所以請求中必須包含完整的資源信息。如果缺少部分信息,會導致這部分數據被更新為NULL。
    – PATCH則是部分更新。僅更新提供的字段,請求中缺少的字段仍保持不變
    
  • 最后,也是最重要的,有了上述兩個能力,Kubernetees項目才可以基於對API對象的增、刪、改、查在完全無需外界干預的情況下,完成對“實際狀態”和“期望狀態”的調諧(Reconcile)過程。

工作原理

首先知道一下一個 API 對象在 Etcd 里的完整資源路徑,是由:Group(API 組)、 Version(API 版本)和 Resource(API 資源類型)三個部分組成的,可以用如下圖的樹形結構表示出來:

在這里插入圖片描述

API對象的組織方式是層層遞進的,Kubernetes會對Group、Version和Resource進行解析,也就是層層匹配,得到相應的對象定義,如Cronjob(Pod、Node 等核心API對象不需要Group,直接匹配Version)。

把YAML 文件提交給 Kubernetes 之后,創建出 API 對象的流程:以創建 CronJob為例

  1. 發起創建CronJob的POST請求后,編寫的YAML的信息就被提交給api-server

  2. Api-server過濾這個請求,完成一些前置性的工作,比如授權、超時處理、審計等

  3. 請求進入mux和routes流程,mux和routees是api-server完成url和handler綁定的場所,api-server的Handler要做的事情就是按照層層匹配的過程,找到對應的CronJob類型定義

  4. Api-server根據crontJob類型定義,使用用戶提交的YAML文件里的字段,創建一個CrontJob對象

    Api-server會進行一個convert工作,即把用戶提交的YAML文件轉換成一個叫做Super Version的對象,它正是該API資源類型所有版本的字段全集,這樣用戶提交的不同版本的YAML就都可以用這個Super Version對象來進行處理了

  5. 先后進行admission()和validation()操作

    Admission Controller和Initializer都屬於Admission的內容,Validation負責驗證這個對象里的各個字段是否合法,這個被驗證過的API對象,都保存在來api-server里一個叫做Registry的數據結構中,也就是說只要一個API對象的定義能在Registry里查到,他就是一個有效的Kubernetes API對象

  6. 把驗證過的API對象轉換成用戶最初提交的版本,進行序列化操作,並調用Etcd的API把它保存起來

自定義API對象

如果想要添加自定義API資源類型,建議使用CRD( Custom Resource Definition),它允許用戶在 Kubernetes 中添加一個跟 Pod、Node 類似的、新的 API 資源類型,即:自定義 API 資源。

使用CRD創建出自定義API對象后,就是為這個 API 對象編寫一個自定義控制器(Custom Controller),這樣, Kubernetes 才能根據 自定義 API 對象的“增、刪、改”操作,在真實環境中做出相應的響應。

編寫自定義控制器分為三個過程:編寫 main 函數、編寫自定義控制器的定義,以編寫控制器里的業務邏輯。

自定義控制器工作流程

在這里插入圖片描述

  1. 首先從 Kubernetes 的 APIServer 里獲取它所關心的對象,也就是自定義的控制器對象。這個操作,依靠的是一個叫作 Informer(通知器)的代碼庫完成的;Informer 與 API 對象是一一對應的,所以需要傳遞給自定義控制器一個Informer;

    Informer是一個帶有本地緩存和索引機制的、可以注冊EventHandler 的 client。它是自定義控制器跟 APIServer 進行數據同步的重要組件。

    創建Informer的時候需要傳一個Client,Informer 正是使用Client,跟 APIServer 建立了連接;真正負責維護這個連接的是 Informer 所使用的 Reflector 包。

    Reflector 使用的是一種叫作ListAndWatch的方法,來“獲取”並“監聽”這些API對象實例的變化(Informer通過 ListAndWatch,把 APIServer 中的 API 對象緩存在了本地,並負責更新和維護這個緩存。)在 ListAndWatch 機制下,一旦 APIServer 端有新的API對象實例被創建、刪除或者更新, Reflector 都會收到“事件通知”;這時,該事件及它對應的 API 對象這個組合,就被稱為增量 (Delta),它會被放進一個 Delta FIFO Queue(即:增量先進先出隊列)中;Informe 會不斷地從 Delta FIFO Queue 里讀取(Pop)增量。每拿到一個增量,Informer 就會判斷這個增量里的事件類型,然后創建或者更新本地對象的緩存;這個緩存,在 Kubernetes 里一般被叫作 Store

    Informer 的第二個職責,則是根據這些事件的類型,觸發事先注冊好的 ResourceEventHandler;
    這些 Handler,需要在創建控制器的時候注冊給它對應的 Informer。

  2. Informer 與要編寫的控制循環之間,則使用了一個工作隊列來進行協同,防止控制循環執行過慢把Informer拖死。

  3. 接下來就是熟悉的控制循環的邏輯了

參考鏈接:

https://www.zhihu.com/question/22285830/answer/469177185

https://www.pianshen.com/article/40791568713/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM