歡迎光臨
每天分享高質量文章

KubeVirt:通過CRD擴展Kubernetes實現虛擬機管理

KubeVirt是什麼?

KubeVirt[1]是個Kubernetes的一個插件,使其在原本調度容器之餘能夠並行調度傳統虛擬機。它通過運用自定義資源定義(以下簡稱CRD)及其他Kubernetes相關功能來無縫擴展現有的集群,提供一系列可用於管理虛擬機的虛擬化API。

 

為什麼要使用CRD而不是Aggregated API Server?

時光迴轉到2017年中期,那時工作在KubeVirt的團隊面臨選擇的十字路口。他們必須決定使用何種方式來擴展Kubernetes——究竟該用Aggregated API Server,還是新出的CRD功能。
那時CRD缺乏相當多KubeVirt所需的相關功能,而通過創建Aggregated API Server能夠提供這些所需的靈活性。不過它有一個主要缺陷——大大增加了安裝及運維KubeVirt的複雜度。 
此處問題的關鍵在於Aggregated API Server需要訪問etcd來進行物件的持久化。這意味著集群管理員將不得不為KubeVirt單獨部署一個etcd,這樣會增加複雜度;或讓KubeVirt直接訪問Kubernetes本身的etcd,而這樣則會引入風險。
當時團隊無法接受Aggregated API Server這些問題。他們的標的並不僅僅只是完成擴展Kubernetes來管理虛擬機的任務,而是用儘量最無縫集成和最小管理代價的方式來完成這個任務。他們認為使用Aggregated API Server增加了複雜度,犧牲了安裝和運維KubeVirt的用戶體驗。 
最終KubeVirt團隊選擇使用CRD來實現功能,並相信Kubernetes生態圈會持續成長改進相關功能以滿足KubeVirt的使用場景。他們最終得償所願。此時此刻,他們當初2017年做評估時CRD所存在的功能差距都已經有瞭解決方案,或至少解決方案正在討論之中。

 

使用CRD來構建分層的類Kubernetes的API

KubeVirt的API是按照用戶業已熟悉的Kubernetes核心API的樣式進行設計的。
舉例來說,在Kubernetes里用戶創建的最底層的運行單元是Pod。當然Pod的確有可能包含多個容器,但邏輯上Pod是整個Kubernetes棧里最底層的單元。一個Pod代表了一份有始有終的執行任務。Pod會被調度到集群中,當工作完成後Pod也會被停止,同時意味著Pod生命周期的結束。 
ReplicaSet和StatefulSet這樣的工作負載控制器是構建在Pod之上的一層,分別用於管理Pod的伸縮和有狀態的應用。在ReplicaSet之上還會有更高層的Deployment控制器用於實現諸如滾動更新這樣的功能。
KubeVirt的API也是以如上多層次控制器的概念為中心進行設計的。KubeVirt里的虛擬機實體(VirtualMachineInstance, 以下簡稱VMI)物件是KubeVirt棧里的最底層單元。一個VMI代表了一份有始有終的虛擬化的執行任務,被創建出來被執行任務直到結束(此處意味著關機),與Pod的概念相同。 
在VMI這層之上是VirtualMachine(以下簡稱VM)控制器。從VM控制器開始我們可以真正看到管理虛擬化的工作負載與容器化的工作負載之間的區別。在現有的Kubernetes功能體系之內,最能描述VM控制器行為的方法是把它和單一尺寸的StatefulSet進行比較。這是因為VM控制器能構建一個單獨的有狀態的虛擬機,這樣的虛擬機能夠在節點出錯或VMI多次重啟時依舊保持狀態。VM物件的表現方式與我們在AWS,GCE,OpenStack或是其他類似的IaaS雲平臺上管理的虛擬機很類似。用戶可以關閉虛擬機,然後又在隨後的某個時間再重新啟動這一臺虛擬機。
除了VM控制器外,KubeVirt中還有VirtualMachineInstanceReplicaSet(以下簡稱VMIRS)控制器,用來管理VMI物件的伸縮。這個控制器與Kubernetes的ReplicaSet控制器表現行為幾乎一模一樣,它們兩個的區別僅在於VMIRS管理VMI物件,而ReplicaSet管理Pod物件。不過這樣說來,是不是如果能有方法使用Kubernetes的ReplicaSet控制器來伸縮管理VMI會更好一些?
VMI,VM和VMIRS這些物件定義都是在KubeVirt的安裝清單檔案提交到集群中時被作為CRD定義註冊進Kubernetes里的。而CRD這樣的註冊方式能讓所有Kubernetes集群管理相關的工具(比如kubectl)能像訪問其他原生Kubernetes物件的方式來訪問KubeVirt的API。


使用動態的網絡回呼(Webhook)做CRD校驗

Kubernetes API Server的職責之一是在物件被持久化進etcd之前攔截和驗證進入的請求。舉例來說,如果有人想嘗試用有問題的Pod定義來創建一個新Pod,Kubernetes API Server會馬上發現錯誤並拒絕這個創建請求。這樣的動作都是發生在物件被持久化進etcd之前,從而防止了有問題的Pod定義進入到集群之中。
在Kubernetes里具體執行校驗的過程被稱為準入控制(admission control)。在之前,想要擴展預設的Kubernetes準入控制器都還需要修改Kubernetes API Server的代碼並做重新編譯和部署。這意味著如果想要對傳入集群的KubeVirt相關的物件做準入控制和校驗,KubeVirt團隊就一定要編譯一個獨有的API Server的二進製版本並說服用戶來使用。這對於KubeVirt團隊來說並不是一個可行方案。
Kubernetes 1.9版本新引入了動態準入控制功能。通過使用這個功能提供的ValidatingAdmissionWebhook,KubeVirt團隊終於有辦法來對相關物件做自定義校驗。KubeVirt會在安裝到集群中時動態地註冊一個HTTPS的網絡回呼。而在註冊完後,所有與KubeVirt的API物件相關的請求都會被Kubernetes API Server轉發到KubeVirt自己的HTTPS接口上做校驗處理。如果這個HTTPS接口出於任何原因拒絕了這個請求,那麼相關的物件也無法被持久化進etcd中,而發送請求方也會收到包含拒絕原因的傳回。
舉個例子,當有人發送了一個有問題的VM物件,他會收到如下的傳回:

$ kubectl create -f my-vm.yaml 
Error from servererror when creating "my-vm.yaml": admission webhook "virtualmachine-validator.kubevirt.io" denied the request: spec.template.spec.domain.devices.disks[0].volumeName 'registryvolume' not found.
以上輸出中的報錯信息傳回時直接由KubeVirt註冊到準入控制的網絡回呼發出的。
使用OpenAPIv3做CRD校驗

除了用網絡回呼來做校驗外,KubeVirt也提供了OpenAPIv3校驗樣式(OpenAPIv3 validation schema[2])。OpenAPIv3樣式並不能幫我們表達很多準入控制的網絡回呼里提供的那些高級校驗規則。 但它提供了一些簡單輕便的校驗能力,比如必填欄位檢查,最大/最小值範圍控制,正則運算式驗證內容等。
使用動態網絡回呼達成類似PodPreset的效果

Kubernetes的動態準入控制功能不僅僅能做校驗,還為KubeVirt這樣的應用提供了攔截和修改進入集群的請求的能力。這個功能具體是通過操作Kubernetes的MutatingAdmissionWebhook物件來完成。KubeVirt也在尋求通過使用這樣的網絡回呼來支持VirtualMachinePreset(以下簡稱VMPreset)功能。
VMPreset的表現行為與PodPreset相似。就像PodPreset允許用戶定義一些在Pod創建期需要自動註入的預定義值,VMPreset允許用戶定義在VM創建期需要註入的引數。通過使用網絡回呼,KubeVirt可以攔截創建VM的請求,把VMPreset配置應用到VM定義上,並且完成了VM的校驗。這些活動都在VM物件被持久化進etcd之前發生,這樣一旦有任何衝突或問題,執行這個操作的用戶都會第一時間得到KubeVirt提供的反饋。

 

CRD的子資源(Subresources)

再拿CRD與Aggregated API Server進行比較,你還會發現CRD缺少了子資源的能力。子資源提供了額外的資源相關的功能。舉例來說,kubectl logs和kubectl exec命令背後就是去讀取pod/logs和pod/exec這兩個子資源接口所提供的信息。
就像Kubernetes通過pod/exec子資源來提供訪問Pod環境那樣,KubeVirt也想使用子資源的方式來提供串口終端,VNC或者SPICE的訪問虛擬機環境的能力。而且結合子資源來為虛擬機添加用戶訪問控制的話,則可以利用Kubernetes的RBAC能力來對此做訪問控制。 
所以既然當初KubeVirt團隊選擇了使用CRD而不是Aggregated API Server來實現自定義物件,那麼他們能怎麼樣在CRD不支持子資源的情況又用上子資源呢?
KubeVirt團隊搞了個變通方案——他們實現了一個僅僅是服務於子資源的無狀態Aggregated API Server。既然沒有狀態,那麼就無需擔心之前識別出的etcd訪問相關的問題。這意味著KubeVirt的API實際上是CRD提供的資源和Aggregated API Server提供的無狀態子資源的組合。 
這對於KubeVirt來說並不是一個完美的方案。CRD和Aggregated API Server都需要在Kubernetes里註冊API組名稱(GroupName)。而API組名稱欄位基本上就是REST API路徑的命名空間,因此這個機制會防止多個第三方應用在API命名上有互相衝突。由於CRD與Aggregated API Server不能共享同一個組名稱,所以KubeVirt不得不註冊了兩個不同的組名稱。一個由CRD使用,而另一個由Aggregated API Server使用。 
在KubeVirt API里包含兩個組名稱會有一點點地不那麼方便,因為這意味著子資源相關的API路徑與資源相關的API路徑會有差異。 
如下是創建一個VMI物件的API呼叫:

/apis/kubevirt.io/v1alpha2/namespaces/my-namespace/virtualmachineinstances/my-vm
而通過子資源方式來訪問圖形化VNC的API呼叫是這樣的:
/apis/subresources.kubevirt.io/v1alpha2/namespaces/my-namespace/virtualmachineinstances/my-vm/vnc

此處註意第一個請求使用了kubevirt.io,而第二個請求使用了subresource.kubevirt.io。KubeVirt團隊也坦言他們並不喜歡這樣的做法,但這是他們解決融合CRD實現和Aggregated API Server實現的方式。 
值得一提的是,Kubernetes從1.10版本開始支持了兩個最基礎的CRD的子資源接口:/status和/scale。 這兩個新接口的支持與KubeVirt交付的虛擬化功能所需要的子資源功能並沒什麼幫助,但至少社區里已經有關於在未來的Kubernetes里將自定義CRD子資源當做webhook暴露出來的討論。如果這個功能能夠實現,那麼KubeVirt團隊將會非常樂意把那個無狀態的Aggregated API Server方案扔掉並遷移到這個功能上。 
CRD清理器

CRD清理器讓一個CRD物件從etcd中被移除前能夠通過預刪除鉤子(pre-delete hook)來執行一些自定義操作。KubeVirt使用清理器來確保VMI物件被從etcd中移除前,對應的虛擬機已經完全被終止了。
CRD的API版本控制

Kubernetes的核心API有能力對一個物件型別支持多個版本,併在這些不同版本間做互相轉換。這樣一來它們就有方法把一個物件從v1alpha1版本升級到v1beta1版本乃至之後其的其他版本。 
在Kubernetes的1.11版之前,CRD並不支持多版本。這意味著當KubeVirt想要把一個CRD從kubevirt.io/v1alpha1版本升級到kubevirt.io/v1beta1版本,唯一的方法是先備份CRD物件,再從集群中刪除已註冊的CRD,接著註冊新版本的CRD進集群,然後把備份的CRD轉化為新的版本,最後把轉化完的版本再添加進集群中。
這樣的策略實在是太不可行了。
幸好現在最新的Kubernetes 1.11版里支持了CRD多版本功能。這要感謝近期社區里的相關工作解決了這個問題。 但還是要註意這個初始的版本在能力上還很有限。儘管當前CRD可以有多版本, 但這個功能並沒有提供版本間轉化遷移的能力。而對於KubeVirt來說,缺少轉化遷移功能會使API版本進化非常困難。但幸運的是,對版本間遷移的支持目前已經在開發之中,而KubeVirt也期待這個功能在未來Kubernetes發佈時能第一時間利用上它。
相關鏈接:
  1. https://github.com/kubevirt/kubevirt

  2. https://kubernetes.io/docs/tasks/access-kubernetes-API/extend-api-custom-resource-definitions/#advanced-topics

原文鏈接:https://kubernetes.io/blog/2018/07/27/kubevirt-extending-kubernetes-with-crds-for-virtualized-workloads/

Kubernetes實戰培訓

Kubernetes應用實戰培訓將於2018年10月12日在深圳開課,3天時間帶你系統學習Kubernetes本次培訓包括:容器基礎、Docker基礎、Docker進階、Kubernetes架構及部署、Kubernetes常用物件、Kubernetes網絡、儲存、服務發現、Kubernetes的調度和服務質量保證、監控和日誌、Helm、專案實踐等,點擊下方圖片查看詳情。

赞(0)

分享創造快樂