資深專家深度剖析Kubernetes API Server第3章(共3章)

程式猿da哥發表於2018-09-12

在本系列的前兩部分中我們介紹了API Server的總體流程,以及API物件如何儲存到etcd中。在本文中我們將探討如何擴充套件API資源。

在一開始的時候,擴充套件API資源的唯一方法是擴充套件相關API原始碼,整合為你所需的資源。或者,推動一個全新的型別為新的核心物件API合入社群程式碼。但是,這樣就會導致核心API資源型別的不斷增加,直至API過載。為了避免這種API資源的無限制擴充套件,在Kubernetes中提供兩種擴充套件核心API的方法:

1.使用自定義資源定義(CRDs),最開始的時候被稱為第三方資源(TPRs)。通過CRD你能夠簡單而靈活的方式定義自己的資源物件型別,並讓API server處理整個生命週期。

2.使用與主API Servers 並行執行的使用者API Servers(UAS)。這種方式,可能更多的設計程式碼開發,可能需要你投入較多的時間及精力。當然,這種方式也能夠讓你對API資源有更細緻,全面的瞭解。

在本文中,我們主要對CRD相關定義以及使用進行探討。

CRDs的宣告及建立

在本系列文章第一部分所提到過的,每個API資源根據Group群組分類,每個物件都有一個對應的版本號與HTTP路徑相關聯。現在如果想要實現一個CRD,首先需要的是就是命名一個新的API Group群組,這個API群組不能與已經存在的群組重複。在你自己新建的API群組中,你可以擁有任意數量的資源,並且它們可以與其他群組中的資源具有相同的名稱。下面我們來列舉一個實際的例子:

資深專家深度剖析Kubernetes API Server第3章(共3章)

在之前我們有介紹過,每個版本的由API群組管理的Kubernetes資源是跟HTTP路徑相關的。CRD類似於物件導向程式設計中一個類的定義,而實際使用的CR可以看做為它的一組例項。首先我們對例子中的一些欄位作說明,第一行中的CRD apiVersion在kube-apiserver 1.7 之後都是這樣定義的。從第5行之後我們定義了spec 的相關欄位。在第6行spec.group是定義了你建立的CRD的API群組(在本例子中定義為了example.com)。第7行定義了CRD物件的版本。每個資源只有一個固定版本,但在API群組中還是能有多個不同版本的資源。第8行的spec.names有兩個必填項:kind,按照慣例第一個字母大寫,plural,按照慣例全為小寫,這個欄位與最終生成的HTTP路徑相關,比如在本例子中,最終的HTTP路徑為https://

上面的kind主要是用來描述物件的型別,而resource 資源是與HTTP路徑相關的。大多數情況下這兩個是匹配的;但是在某些特定情況下在相同的API HTTP路徑下可能返回不通的kind(比如Status 錯誤物件會返回另一種kind)。

值得注意的是resource 資源(在本例中是databases)和group群組(本例中是example.com)必須與metadata.name 欄位匹配(本例為第四行databases.example.com)。

現在我們根據上面的YAML檔案來建立一個CRD:

$ kubectl create -f databases-crd.yaml

customresourcedefinition "databases.example.com" created

由於這個建立過程是非同步進行的,所以你必須檢查一下你建立的CRD的狀態,確認你建立的CRD沒有與其它資源衝突,並且API Server已經呼叫相關處理函式完成建立。你可以在指令碼或程式碼中通過輪詢完成這個過程。最後我們能得到以下狀態:

$ kubectl get crd databases.example.com -o yaml

apiVersion: apiextensions.k8s.io/v1beta1

kind: CustomResourceDefinition

metadata:

creationTimestamp: 2017-08-09T09:21:43Z

name: databases.example.com

resourceVersion: "792"

selfLink: /apis/apiextensions.k8s.io/v1beta1/customresourcedefinitions/databases.example.com

uid: 28c94a05-7ce4-11e7-888c-42010a9a0fd5

spec:

group: example.com

names:

kind: Database

listKind: DatabaseList

plural: databases

singular: database

scope: Namespaced

version: v1

status:

acceptedNames:

kind: Database

listKind: DatabaseList

plural: databases

singular: database

conditions:

- lastTransitionTime: null

message: no conflicts found

reason: NoConflicts

status: "True"

type: NamesAccepted

- lastTransitionTime: 2017-08-09T09:21:43Z

message: the initial names have been accepted

reason: InitialNamesAccepted

status: "True"

type: Established

以上,我們可以看到通過kubectl可以看到我們之前建立的CRD,並且顯示出了CRD的一些狀態資訊。

CRDs的使用

在通過kubectl proxy將Kubernetes API開啟本地代理後,檢視我們剛才建立的CRD:

$ http 127.0.0.1:8001/apis/example.com

HTTP/1.1 200 OK

Content-Length: 223

Content-Type: application/json

Date: Wed, 09 Aug 2017 09:25:44 GMT


{

"apiVersion": "v1",

"kind": "APIGroup",

"name": "example.com",

"preferredVersion": {

"groupVersion": "example.com/v1",

"version": "v1"

},

"serverAddressByClientCIDRs": null,

"versions": [

{

"groupVersion": "example.com/v1",

"version": "v1"

}

]

}

請注意,在預設情況下十分鐘內,kubectl是檢視儲存在~/.kube/cache/discovery目錄的快取。所以,可能會需要10分鐘後你才能看到你新建立的CRD資源。但是,當沒有快取時,kubectl發現不了所需的資源時,那麼會重新快取它。

接下來,我們來看一個CRD例項:

$ cat wordpress-database.yaml

apiVersion: example.com/v1

kind: Database

metadata:

name: wordpress

spec:

user: wp

password: secret

encoding: unicode


$ kubectl create -f wordpress-databases.yaml

database "wordpress" created


$ kubectl get databases.example.com

NAME KIND

wordpress Database.v1.example.com

想要通過API來監控資源的建立與更新,你可以通過對某個resourceVersion(我們通過curl來例項對指定版本的database做監控)之後的修改做監控watch。


$ http 127.0.0.1:8001/apis/example.com/v1/namespaces/default/databases

HTTP/1.1 200 OK

Content-Length: 593

Content-Type: application/json

Date: Wed, 09 Aug 2017 09:38:49 GMT


{

"apiVersion": "example.com/v1",

"items": [

{

"apiVersion": "example.com/v1",

"kind": "Database",

"metadata": {

"clusterName": "",

"creationTimestamp": "2017-08-09T09:38:30Z",

"deletionGracePeriodSeconds": null,

"deletionTimestamp": null,

"name": "wordpress",

"namespace": "default",

"resourceVersion": "2154",

"selfLink": "/apis/example.com/v1/namespaces/default/databases/wordpress",

"uid": "8101a7af-7ce6-11e7-888c-42010a9a0fd5"

},

"spec": {

"encoding": "unicode",

"password": "secret",

"user": "wp"

}

}

],

"kind": "DatabaseList",

"metadata": {

"resourceVersion": "2179",

"selfLink": "/apis/example.com/v1/namespaces/default/databases"

}

}

我們可以對/apis/example.com/v1/namespaces/default/databases/wordpressCRD的HTTP路徑通過curl命令對的"resourceVersion": "2154"進行監控watch:

$ curl -f 127.0.0.1:8001/apis/example.com/v1/namespaces/default/databases?watch=true&resourceVersion=2154

現在我們新開一個shell對話視窗,刪除wordpress CRD資源,我們可以檢視剛才的監控watch視窗是否接收到了這個訊息:

$ kubectl delete databases.example.com/wordpress

請注意:我們能夠使用kubectl delete database wordpress刪除CRD資源,是因為之前在Kubernetes沒有定義有database 資源。此外,database是我們CRD中的spec.name.singular欄位,從英語語法派生而來。

我們可以看到之前監控watch CRD databases從API Server處返回的更新狀態:

{"type":"DELETED","object":{"apiVersion":"example.com/v1","kind":"Database","metadata":{"clusterName":"","creationTimestamp":"2017-0[0/515]

:38:30Z","deletionGracePeriodSeconds":null,"deletionTimestamp":null,"name":"wordpress","namespace":"default","resourceVersion":"2154","selfLink":"/apis/example.com/v1/namespaces/

default/databases/wordpress","uid":"8101a7af-7ce6-11e7-888c-42010a9a0fd5"},"spec":{"encoding":"unicode","password":"secret","user":"wp"}}}

上述shell會話的執行及輸出結果如下圖所示:

資深專家深度剖析Kubernetes API Server第3章(共3章)

最後,讓我們看一下CRD database 的各個資料是如何儲存在etcd中的。下面是我們直接通過HTTP API進入etcd訪問得到的資料:

$ curl -s localhost:2379/v2/keys/registry/example.com/databases/default | jq .

{

"action": "get",

"node": {

"key": "/registry/example.com/databases/default",

"dir": true,

"nodes": [

{

"key": "/registry/example.com/databases/default/wordpress",

"value": "{\"apiVersion\":\"example.com/v1\",\"kind\":\"Database\",\"metadata\":{\"clusterName\":\"\",\"creationTimestamp\":\"2017-08-09T14:53:40Z\",\"deletionGracePeriodSeconds\":null,\"deletionTimestamp\":null,\"name\":\"wordpress\",\"namespace\":\"default\",\"selfLink\":\"\",\"uid\":\"8837f788-7d12-11e7-9d28-080027390640\"},\"spec\":{\"encoding\":\"unicode\",\"password\":\"secret\",\"user\":\"wp\"}}\n",

"modifiedIndex": 670,

"createdIndex": 670

}

],

"modifiedIndex": 670,

"createdIndex": 670

}

}

從上面可以看到,CRD資料在etcd中最終以一個未解析的的狀態存在。現在將CRD刪除,所有的CRD例項也會跟著刪除,這是一個級聯刪除操作。

目前CRDs的使用現狀,侷限及將來的展望

CRDs的發展現狀如下所示:

1.在Kubernetes 1.7版本中CRDs開始取代ThirdPartyResources (TPRs) ,並且TPRs 將會在Kubernetes 1.8被刪除。

2.將TPRs遷移到CRDs例項可以參考文件migration

3.支援一個CRD中只有單個version版本,當然,一個群組中可能有多個version版本。

4.CRDs提供一個API方案,在使用者角度看它與Kubernetes原生的API資源基本沒有區別

5.CRDs是多版本多分支穩定的基礎。關於CRD資源的JSON-Schema的格式有效性校驗可以參考文件CRD validation proposal。相關資源回收可以參考文件Garbage collection。

接下去我們來看一下一些CRDs的侷限:

1.CRD不提供版本轉換功能,也就是說,每個CRD只能有一個版本(預計不會在近期或中期內看到支援CRD版本轉換)。

2.在Kubernetes1.7當中,目前並沒有對於CRD的相關校驗validation。

3.沒有快速,實時的准入(admission)機制(但是可以支援webhooks 形式的初始化及准入)。

4.在Kubernetes1.7中你不能定義子資源(sub-resources),比如scale或者status,不過目前有在這方面proposal的討論。

5.CRD目前不支援預設值配置,即不支援為特定的欄位配預設值(在Kubernetes1.7後續的版本中可能會支援)。

為了解決上述的問題,並且靈活的擴充套件Kubernetes,你可以執行一個與主API Server並行的使用者API Servers。我們將在本博文的以後部分中詳細介紹如何編寫UAS,並編寫一個custom controller完整使用CRD 。


相關文章