從零開始入門 K8s | Kubernetes API 編程範式

作者 | 陳顯鷺  阿里巴巴高級技術專家

本文整理自《CNCF x Alibaba 雲原生技術公開課》第 23 講,點擊“閱讀原文”直達課程頁面。
關注“阿里巴巴雲原生”公衆號,回覆關鍵詞**“入門”**,即可下載從零入門 K8s 系列文章 PPT。

導讀:在 Kubernetes 裏面, API 編程範式也就是 Custom Resources Definition(CRD)。我們常講的 CRD,其實指的就是用戶自定義資源。爲什麼會存在用戶自定義資源問題呢?本文將會從其需求來源出發,對此概念進行逐步深入的講解。

一、需求來源

首先我們先來看一下 API 編程範式的需求來源。

在 Kubernetes 裏面, API 編程範式也就是 Custom Resources Definition(CRD)。我們常講的 CRD,其實指的就是用戶自定義資源。

爲什麼會有用戶自定義資源問題呢?

隨着 Kubernetes 使用的越來越多,用戶自定義資源的需求也會越來越多。而 Kubernetes 提供的聚合各個子資源的功能,已經不能滿足日益增長的廣泛需求了。用戶希望提供一種用戶自定義的資源,把各個子資源全部聚合起來。但 Kubernetes 原生資源的擴展和使用比較複雜,因此誕生了用戶自定義資源這麼一個功能。

二、用例解讀

CRD 的一個實例

我們首先具體地介紹一下 CRD 是什麼。

CRD 功能是在 Kubernetes 1.7 版本被引入的,用戶可以根據自己的需求添加自定義的 Kubernetes 對象資源。值得注意的是,這裏用戶自己添加的 Kubernetes 對象資源都是 native 的、都是一等公民,和 Kubernetes 中自帶的、原生的那些 Pod、Deployment 是同樣的對象資源。在 Kubernetes 的 API Server 看來,它們都是存在於 etcd 中的一等資源。

同時,自定義資源和原生內置的資源一樣,都可以用 kubectl 來去創建、查看,也享有 RBAC、安全功能。用戶可以開發自定義控制器來感知或者操作自定義資源的變化。

下面我們來看一個簡單的 CRD 實例。下圖是一個 CRD 的定義。

1.png

首先最上面的 apiVersion 就是指 CRD 的一個 apiVersion 聲明,聲明它是一個 CRD 的需求或者說定義的 Schema。

kind 就是 CustomResourcesDefinition,指 CRD。name 是一個用戶自定義資源中自己自定義的一個名字。一般我們建議使用“頂級域名.xxx.APIGroup”這樣的格式,比如這裏就是 foos.samplecontroller.k8s.io。

spec 用於指定該 CRD 的 group、version。比如在創建 Pod 或者 Deployment 時,它的 group 可能爲 apps/v1 或者 apps/v1beta1 之類,這裏我們也同樣需要去定義 CRD 的 group。

  • 圖中的 group 爲 samplecontroller.k8s.io;
  • verison 爲 v1alpha1;
  • names 指的是它的 kind 是什麼,比如 Deployment 的 kind 就是 Deployment,Pod 的 kind 就是 Pod,這裏的 kind 被定義爲了 Foo;
  • plural 字段就是一個暱稱,比如當一些字段或者一些資源的名字比較長時,可以用該字段自定義一些暱稱來簡化它的長度;
  • scope 字段表明該 CRD 是否被命名空間管理。比如 ClusterRoleBinding 就是 Cluster 級別的。再比如 Pod、Deployment 可以被創建到不同的命名空間裏,那麼它們的 scope 就是 Namespaced 的。這裏的 CRD 就是 Namespaced 的。

下圖就是上圖所定義的 CRD 的一個實例。

2.png

  • 它的 apiVersion 就是我們剛纔所定義的 samplecontroller.k8s.io/v1alpha1;
  • kind 就是 Foo;
  • metadata 的 name 就是我們這個例子的名字;
  • 這個實例中 spec 字段其實沒有在 CRD 的 Schema 中定義,我們可以在 spec 中根據自己的需求來寫一寫,格式就是 key:value 這種格式,比如圖中的 deploymentName: example-foo, replicas: 1。當然我們也可以去做一些檢驗或者狀態資源去定義 spec 中到底包含什麼。

帶有校驗的 CRD

我們來看一個包含校驗的 CRD 定義:
3.png

可以看到這個定義更加複雜了,validation 之前的字段我們就不再贅述了,單獨看校驗這一段。

它首先是一個 openAPIV3Schema 的定義,spec 中則定義了有哪些資源,以 replicas 爲例,這裏將 replicas 定義爲一個 integer 的資源,最小值爲 1,最大值是 10。那麼,當我們再次使用這個 CRD 的時候,如果我們給出的 replicas 不是 int 值,或者去寫一個 -1,或者大於 10 的值,這個 CRD 對象就不會被提交到 API Server,API Server 會直接報錯,告訴你不滿足所定義的參數條件。

帶有狀態字段的 CRD

再來看一下帶有狀態字段的 CRD 定義。

4.png

我們在使用一些 Deployment 或 Pod 的時候,部署完成之後可能要去查看當前部署的狀態、是否更新等等。這些都是通過增加狀態字段來實現的。另外,Kubernetes 在 1.12 版本之前,還沒有狀態字段。

狀態實際上是一個自定義資源的子資源,它的好處在於,對該字段的更新並不會觸發 Deployment 或 Pod 的重新部署。我們知道對於某些 Deployment 和 Pod,只要修改了某些 spec,它就會重新創建一個新的 Deployment 或者 Pod 出來。但是狀態資源並不會被重新創建,它只是用來回應當前 Pod 的整個狀態。上圖中的 CRD 聲明中它的子資源的狀態非常簡單,就是一個 key:value 的格式。在 “{}” 裏寫什麼,都是自定義的。

5.png

以一個 Deployment 的狀態字段爲例,它包含 availableReplicas、當前的狀態(比如更新到第幾個版本了、上一個版本是什麼時候)等等這些信息。在用戶自定義 CRD 的時候,也可以進行一些複雜的操作來告訴別的用戶它當前的狀態如何。

三、操作演示

下面我們來具體演示一下 CRD。

我們這裏有兩個資源:crd.yaml 和 example-foo.yaml。

6.png

首先創建一下這個 CRD 的 Schema 讓我們的 Kubernetes Server 知道該 CRD 到底是什麼樣的。創建的方式非常簡單,就是 “kuberctl create -f crd.yaml”。
7.png

通過 “kuberctl get crd” 可以看到剛纔的 CRD 已經被創建成功了。

8.png

這個時候我們就可以去創建對應的資源 “kuberctl create -f example-foo.yaml”:

9.png

下面來看一下它裏面到底有什麼東西 “kubectl get foo example-foo -o yaml” :

10.png

可以看到它是一個 Foo 的資源,spec 就是我們剛纔所定義的,被選中的部分是基本上所有的 Kubernetes 的 metadata 資源中都會有的。因此,創建該資源和我們正常創建一個 Pod 的區別並不大,但是這個資源不是一個 Pod,也不是 Kubernetes 本身內置的資源,這就是一個我們自己創建的資源。從使用方式和使用體驗上來說,和 Kubernetes 內置資源的使用幾乎一致。

四、架構設計

控制器概覽

只定義一個 CRD 其實沒有什麼作用,它只會被 API Server 簡單地計入到 etcd 中。如何依據這個 CRD 定義的資源和 Schema 來做一些複雜的操作,則是由 Controller,也就是控制器來實現的。
 
Controller 其實是 Kubernetes 提供的一種可插拔式的方法來擴展或者控制聲明式的 Kubernetes 資源。它是 Kubernetes 的大腦,負責大部分資源的控制操作。以 Deployment 爲例,它就是通過 kube-controller-manager 來部署的。

比如說聲明一個 Deployment 有 replicas、有 2 個 Pod,那麼 kube-controller-manager 在觀察 etcd 時接收到了該請求之後,就會去創建兩個對應的 Pod 的副本,並且它會去實時地觀察着這些 Pod 的狀態,如果這些 Pod 發生變化了、回滾了、失敗了、重啓了等等,它都會去做一些對應的操作。

所以 Controller 纔是控制整個 Kubernetes 資源最終表現出來的狀態的大腦。

用戶聲明完成 CRD 之後,也需要創建一個控制器來完成對應的目標。比如之前的 Foo,它希望去創建一個 Deployment,replicas 爲 1,這就需要我們創建一個控制器用於創建對應的 Deployment 才能真正實現 CRD 的功能。

控制器工作流程概覽

11.png

這裏以 kube-controller-manager 爲例。

如上圖所示,左側是一個 Informer,它的機制就是通過去 watch kube-apiserver,而 kube-apiserver 會去監督所有 etcd 中資源的創建、更新與刪除。Informer 主要有兩個方法:一個是 ListFunc;一個是 WatchFunc。

  • ListFunc 就是像 “kuberctl get pods” 這類操作,把當前所有的資源都列出來;
  • WatchFunc 會和 apiserver 建立一個長鏈接,一旦有一個新的對象提交上去之後,apiserver 就會反向推送回來,告訴 Informer 有一個新的對象創建或者更新等操作。

Informer 接收到了對象的需求之後,就會調用對應的函數(比如圖中的三個函數 AddFunc, UpdateFunc 以及 DeleteFunc),並將其按照 key 值的格式放到一個隊列中去,key 值的命名規則就是 “namespace/name”,name 就是對應的資源的名字。比如我們剛纔所說的在 default 的 namespace 中創建一個 foo 類型的資源,那麼它的 key 值就是 “default/example-foo”。Controller 從隊列中拿到一個對象之後,就會去做相應的操作。

下圖就是控制器的工作流程。

12.png

首先,通過 kube-apiserver 來推送事件,比如 Added, Updated, Deleted;然後進入到 Controller 的 ListAndWatch() 循環中;ListAndWatch 中有一個先入先出的隊列,在操作的時候就將其 Pop() 出來;然後去找對應的 Handler。Handler 會將其交給對應的函數(比如 Add(), Update(), Delete())。

一個函數一般會有多個 Worker。多個 Worker 的意思是說比如同時有好幾個對象進來,那麼這個 Controller 可能會同時啓動五個、十個這樣的 Worker 來並行地執行,每個 Worker 可以處理不同的對象實例。

工作完成之後,即把對應的對象創建出來之後,就把這個 key 丟掉,代表已經處理完成。如果處理過程中有什麼問題,就直接報錯,打出一個事件來,再把這個 key 重新放回到隊列中,下一個 Worker 就可以接收過來繼續進行相同的處理。

五、本文總結

本文的主要內容就到此爲止了,這裏爲大家簡單總結一下:

  • CRD 是 Custom Resources Definition 的縮寫,也就是用戶自定義資源,用戶可以使用這個功能擴展自己的Kubernetes 原生資源信息;
  • CRD 和普通的 Kubernetes 資源一樣,都可以受 RBAC 權限控制,並且支持 status 狀態字段;
  • CRD-controller 也就是 CRD 控制器,能夠實現用戶自行編寫,並且解析 CRD 並把它變成用戶期望的狀態。

直播海報.png

阿里巴巴雲原生關注微服務、Serverless、容器、Service Mesh 等技術領域、聚焦雲原生流行技術趨勢、雲原生大規模的落地實踐,做最懂雲原生開發者的技術圈。”

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章