Categories
程式開發

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


在 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 的定義。

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

首先最上面的 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 的一個實例。

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

  • 它的 apiVersion 就是我們剛才所定義的 samplecontroller.k8s.io/v1alpha1;

  • kind 就是 Foo;

  • metadata 的 name 就是我們這個例子的名字;

  • 這個實例中spec 字段其實沒有在CRD 的Schema 中定義,我們可以在spec 中根據自己的需求來寫一寫,格式就是key:value 這種格式,比如圖中的deploymentName: example-foo, replicas: 1 。當然我們也可以去做一些檢驗或者狀態資源去定義 spec 中到底包含什麼。

帶有校驗的 CRD

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

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

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

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

帶有狀態字段的 CRD

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

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

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

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

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

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

三、操作演示

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

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

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

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

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

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

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

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

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

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

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

可以看到它是一個 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 的功能。

控制器工作流程概覽

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

這里以 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 從隊列中拿到一個對象之後,就會去做相應的操作。

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

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

首先,通過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 並把它變成用戶期望的狀態。

本文轉載自阿里巴巴雲原生微信公眾號(ID:Alicloudnative)。

相關閱讀:

從零開始入門 K8s:有狀態應用編排 – StatefulSet

從零開始入門 K8s:Kubernetes 存儲架構及插件使用

從零開始入門 K8s:GPU 管理和 Device Plugin 工作機制

從零開始入門 K8s:調度器的調度流程和算法介紹

從零開始入門 K8s:Kubernetes 調度和資源管理

從零開始入門 K8s:etcd 性能優化實踐

從零開始入門 K8s:手把手帶你理解 etcd

從零開始入門 K8s:深入剖析 Linux 容器

從零開始入門 K8s:Kubernetes 中的服務發現與負載均衡

從零開始入門 K8s:Kubernetes 網絡概念及策略控制

從零開始入門 K8s:監控與日誌的可觀測性

從零開始入門 K8s:應用存儲和持久化數據卷:存儲快照與拓撲調度

從零開始入門 K8s:應用存儲和持久化數據卷的核心知識

從零開始入門 K8s:應用配置管理

從零開始入門 K8s:應用編排與管理:Job & DaemonSet

從零開始入門 K8s:應用編排與管理

從零開始入門 K8s:K8s 的應用編排與管理

從零開始入門 K8s:詳解 Pod 及容器設計模式

從零開始入門 K8s:詳解 K8s 容器基本概念

從零開始入門 K8s:詳解 K8s 核心概念