天天看點

全面進化:Kubernetes CRD 1.16 GA前瞻

全面進化:Kubernetes CRD 1.16 GA前瞻
作者: Min Kim(Github:yue9944882),Kubernetes社群CRD維護者,螞蟻金服進階研發工程師。Kubernetes 1.16擴充性GA Sprint疊代組成員,主要負責CRD OpenAPI聚合釋出相關特性。

随着近段時間以CRD(CustomResourceDefinition)的形式擴充Kubernetes火爆起來,在開源社群湧現了許許多多用CRD作為平台建構應用的成功開源項目。整個社群對CRD的積極擁抱其實是遠在維護者們的期望之外的。然而經過5個release的洗禮,CRD從最開始的實驗性項目漸漸變得清晰了起來,社群對CRD模型提出的許許多多建議或者挑戰,将在本次1.16的版本得到完整的解決和回應。CRD的GA計劃從1.14版本開始斬露萌芽到Kubenretes 1.16全力沖刺的研發周期中,主要過程是來自谷歌和紅帽的工程師在執行,還有少數的社群志願者。

通過本篇文章,作者将通過梳理CRD的GA進化中的主要革新點,同時結合CRD面臨的問題/缺陷進而推導出其中的聯系/因果,在最後還會對CRD未來面臨的問題和挑戰進行分析。希望讀者可以通過本篇文章對CRD的正負面有個立體的認識,也借此抛磚引玉提出如何在複雜場景下強化使用CRD的未來議題。

GA版本CRD主要帶來的革新

總覽

大的方向上,一言蔽之CRD模型的進化之路是支援所承載的使用者資源(CR)可持續演進發展之路,其中又主要分為兩個路線:

  • 從“無結構化”進化到“強結構化”
  • 從“無版本化”進化到“多版本化”

在結構化的CRD中,為了在簡單的CRD的Yaml中定義出一套結構化的模型,我們首先借助了OpenAPI文法力量來描述模型的結構,并在此基礎上再推動CRD全面向已經成熟的内建原生API模型多版本機制(包括Defaulting,Validating,多版本Round-Tripping)靠攏。最直覺來看,在GA的模型定義層面上,最大的變化是删除了CRD的Spec中頂層的模型描述相關定義,以此推動使用者全面使用多版本的CRD,這也是與Kubernetes原生模型實作同步的第一步,同時GA版本帶來的其他特性大多是為了給使用者更好的使用多版本CRD提供配套的基礎設施。

CRD Defaulting/Pruning 支援

在CRD的GA計劃中,把Defaulting/Pruning結對放在同一個條目中是因為這兩者孿生對應對模型字段的增加和删除兩種變更。深入一步講,Defaulting/Pruning的意義是為了讓使用者對CRD的模型變更加平滑:

  • Defaulting:為CRD添加一個新字段的時候,由于資料面(即Etcd存儲)中存量的資料并沒有該字段,會使得從APIServer中讀取到的對象該字段初始值為Go語言中的“零值”。除非在使用者改動Controller的處理邏輯在其中添加對應“零值”的處理分支,這往往會導緻一個正在運作的控制面Controller出現問題。Defaulting的支援允許你在CRD定義“如果該字段從資料面讀出時不存在,預設設為何值”,這對于模型的新字段變更是非常有幫助的。
  • Pruning:使用者在為CRD删除一個字段的時候,由于資料面中存量的資料已經存在了該字段,會使得從APIServer讀出的實際對象仍然擁有該值。這和使用者希望變更CRD的模型删除某個字段的意圖是相悖的。那麼設定Pruning可以幫助你在字段删除後保持存儲資料面和API層面的一緻。順帶一提的是,對于原生内置的Kubernetes模型,APIServer在從Etcd資料面讀取資料後反序列化成對象時,使用的是“非嚴格”的Json/Yaml序列化器,是以可以天然将模型中不認識的字段過濾掉。

CRD Webhook Conversion 支援

除非你可以迫使使用者在版本間進行遷移,多版本的CRD往往被要求可以同時服務多個版本的模型。比如在同一個叢集中,一個使用者1可能正在使用v1beta1版本的模型運作他的應用操作資源A,而另一個使用者2也同時正在使用最新v1的模型運作他的應用操作資源A,如果v1模型比v1beta1模型新增/遷移了一個字段,那麼使用者1的寫操作自然不會存在該字段,而使用者2在通過寫操作為該字段指派時,很有可能會被使用者1的寫操作将該字段再次抹除。這個典型的問題場景,我們在社群叫做Multi-Version Round-Tripping。解決這個問題也是整個Kubernetes社群SIG API-Machinery工作組的核心問題之一。那麼如果保證這兩個使用者對這多版本的模型進行的讀寫都成功呢?Kubernetes的解法是在資源真正落入資料面之前進行版本之間的轉化(Conversion),并确認統一其中一個版本為資料面存儲使用的版本。對于原生的Kubernetes資源,轉化的實作是一系列Go語言的函數,那麼對CRD而言,我們解決多版本轉化的辦法是通過為該資源植入一個Conversion Webhook,這個Conversion Webhook的功能隻有一個,就是告訴Kubernetes叢集如何将這個CRD承載的多個版本的模型之間進行轉化。

進一步的CRD OpenAPI Schema內建

目前OpenAPI的标準規範主要涵蓋v2和v3兩個版本,v2标準隻能用來描述非常簡單模型定義遠不能滿足Kubernetes的模型,而v3标準遠過于複雜适配成本極高,并且和v2标準不完全向後相容。事實上目前行業内幾乎沒有對OpenAPIv3的标準的完整适配,這也是OpenAPIv3最大的痛點。又由于在Go語言生态中沒有理想的OpenAPIv3标準庫,是以目前CRD對OpenAPI文法的适配其實走到了一個非常尴尬的階段,他完整的适配了OpenAPIv2标準,又隻取了OpenAPIv3标準中大概不到30%的文法支援,這也是CRD在未來後GA疊代周期中主要需要解決的問題之一。本次CRD的GA釋出中,加強了對OpenAPIv3文法的支援,包括anyOf,oneOf,allOf以及nullable關鍵字的适配。這些關鍵字的支援将幫助使用者表達出更複雜的模型層次結構。

CRD子資源支援正式畢業

子資源相對來講是是目前CRD的比較穩定的功能之一,盡管在1.16版本中宣布畢業,但是其實之前的版本的子資源以及趨于穩定,通過支援Status子資源,使用者結合資源的Generation資源在自定義的控制器Controller中有效判斷出定義(Spec)的變更和狀态(Status)的變更。如果使用者需要使用更加自定義的子資源比如類似pod/logs,pod/exec,社群推薦使用APIServer Aggregation的方式進行擴充,詳請參考近期從孵化器畢業的 kubernetes-sigs/apiserver-builder-alpha 項目。

更加複雜場景下的CRD使用

如何通過CRD部署“資料密集型”資源?

  • 單獨編譯(Stand-alone)并且部署提供CRD服務的 apiextension-apiserver,再以AA(APIServer Aggregation的方式)接入叢集,通過這樣可以将CRD的存儲隔離到一個獨立的Etcd叢集,進而隔絕對”Kubernetes主APIServer“的影響。
  • 通過使用Rancher研發的Etcd-on-SQL适配庫KINE将自定義資源直接轉移到SQL存儲上。目前KINE已經具體高可用部署以及适配Etcd Watch/Txn接口的能力,并且已在K3S的內建測試得到了完整驗證也通過CNCF一緻性測試。目前在Rancher正在考慮将KINE捐贈給Kubernetes社群發展有望成為行業标準。對Kubernetes适配MySQL感興趣的開發者可以考慮通過官方提供的Example進行PoC演練。

如何通過CRD生成其他語言的用戶端?