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

歡迎來到深刻學習Kubernetes API Server的系列文章的第二部分。在上一部分中咱們對APIserver整體,相關術語及request請求流進行探討說明。在本部分文章中,咱們主要聚焦於探究如何對Kubernetes 對象的狀態以一種可靠,持久的方式進行管理。以前的文章中提到過API Server自身是無狀態的,而且它是惟一可以與分佈式存儲etcd直接通訊的組件。node

etcd的簡要說明nginx

在*nix操做系統中,咱們通常使用/etc來存儲相關配置數據。實際上etcd的名字就是由此發展而來,在etc後面加上個」d」表示」distributed」分佈式。任何分佈式系統都須要有像etcd這樣可以存儲系統數據的東西,使其可以以一致和可靠的方式檢索相關數據。爲了能實現分佈式的數據訪問,etcd使用Raft 協議。從概念上講,etcd支持的數據模型是鍵值(key-value)存儲。在etcd2中,各個key是以層次結構存在,而在etcd3中這個就變成了遍及模型,但同時也保持了層次結構方式的兼容性。git

使用容器化版本的etcd,咱們能夠建立上面的樹,而後按以下方式檢索它:github

$ docker run --rm -d -p 2379:2379 \web

 --name test-etcd3 quay.io/coreos/etcd:v3.1.0 /usr/local/bin/etcd \docker

 --advertise-client-urls http://0.0.0.0:2379 --listen-client-urls http://0.0.0.0:2379json

$ curl localhost:2379/v2/keys/foo -XPUT -d value="some value"後端

$ curl localhost:2379/v2/keys/bar/this -XPUT -d value=42api

$ curl localhost:2379/v2/keys/bar/that -XPUT -d value=take併發

$ http localhost:2379/v2/keys/?recursive=true

HTTP/1.1 200 OK

Content-Length: 327

Content-Type: application/json

Date: Tue, 06 Jun 2017 12:28:28 GMT

X-Etcd-Cluster-Id: 10e5e39849dab251

X-Etcd-Index: 6

X-Raft-Index: 7

X-Raft-Term: 2

{

    "action": "get",

    "node": {

        "dir": true,

        "nodes": [

            {

                "createdIndex": 4,

                "key": "/foo",

                "modifiedIndex": 4,

                "value": "some value"

            },

            {

                "createdIndex": 5,

                "dir": true,

                "key": "/bar",

                "modifiedIndex": 5,

                "nodes": [

                    {

                        "createdIndex": 5,

                        "key": "/bar/this",

                        "modifiedIndex": 5,

                        "value": "42"

                    },

                    {

                        "createdIndex": 6,

                        "key": "/bar/that",

                        "modifiedIndex": 6,

                        "value": "take"

                    }

                ]

            }

        ]

    }

}

如今咱們已經大體瞭解了etcd是如何工做的,接下去咱們繼續討論etcd在Kubernetes是如何被使用的。

集羣中的etcd

在Kubernetes中,etcd是控制平面中的一耳光獨立組成部分。在Kubernetes1.5.2版本以前,咱們使用的是etcd2版本,而在Kubernetes1.5.2版本以後咱們就轉向使用etcd3版本了。值得注意的是在Kubernetes1.5.x版本中etcd依舊使用的是v2的API模型,以後這將開始變爲v3的API模型,包括使用的數據模型。站在開發者角度而言這個彷佛沒什麼直接影響,由於API Server與存儲以前是抽象交互,而並不關心後端存儲的實現是etcd v2仍是v3。可是若是是站在集羣管理員的角度來看,仍是須要知道etcd使用的是哪一個版本,由於集羣管理員須要平常對數據進行一些備份,恢復的維護操做。

你能夠API Server的相關啓動項中配置使用etcd的方式,API Server的etcd相關啓動項參數以下所示:

$ kube-apiserver -h

...

--etcd-cafile string   SSL Certificate Authority file used to secure etcd communication.

--etcd-certfile string SSL certification file used to secure etcd communication.

--etcd-keyfile string  SSL key file used to secure etcd communication.

...

--etcd-quorum-read     If true, enable quorum read.

--etcd-servers         List of etcd servers to connect with (scheme://ip:port) …

...

Kubernetes存儲在etcd中的數據,是以JSON字符串或Protocol Buffers 格式存儲。下面咱們來看一個具體的例子:在apiserver-sandbox的命名空間中建立一個webserver的pod。而後咱們使用etcdctl工具來查看相關etcd(在本環節中etcd版本爲3.1.0)數據。

$ cat pod.yaml

apiVersion: v1

kind: Pod

metadata:

  name: webserver

spec:

  containers:

  - name: nginx

    image: tomaskral/nonroot-nginx

    ports:

    - containerPort: 80

 

$ kubectl create -f pod.yaml

 

$ etcdctl ls /

/kubernetes.io

/openshift.io

 

$ etcdctl get /kubernetes.io/pods/apiserver-sandbox/webserver

{

  "kind": "Pod",

  "apiVersion": "v1",

  "metadata": {

    "name": "webserver",

...

下面咱們來看一下這個pod對象是如何最終存儲到etcd中,經過kubectl create -f pod.yaml的方式。下圖描繪了這個整體流程:

1.客戶端(好比kubectl)提供一個理想狀態的對象,好比以YAML格式,v1版本提供。

2.Kubectl將YAML轉換爲JSON格式,併發送。

3.對應同類型對象的不一樣版本,API Server執行無損耗轉換。對於老版本中不存在的字段則存儲在annotations中。

4.API Server將接受到的對象轉換爲規範存儲版本,這個版本由API Server指定,通常是最新的穩定版本,好比v1。

5.最後將對象經過JSON 或protobuf方式解析爲一個value,經過一個特定的key存入etcd當中。

咱們能夠經過配置 kube-apiserver的啓動參數--storage-media-type來決定想要序列化數據存入etcd的格式,默認狀況下爲application/vnd.kubernetes.protobuf格式。咱們也能夠經過配置--storage-versions啓動參數,來肯定存入etcd的每一個羣組Group對象的默認版本號。

如今讓咱們來看看無損轉換是如何進行的,咱們將使用Kubernetes 對象Horizontal Pod Autoscaling (HPA)來列舉說明。HPA顧名思義是指經過監控資源的使用狀況結合ReplicationController控制Pod的伸縮。

首先咱們期待一個API代理(以便於咱們可以在本地直接訪問它),並啓動ReplicationController,以及HPA 。

$ kubectl proxy --port=8080 &

$ kubectl create -f https://raw.githubusercontent.com/mhausenblas/kbe/master/specs/rcs/rc.yaml

kubectl autoscale rc rcex --min=2 --max=5 --cpu-percent=80

kubectl get hpa/rcex -o yaml

如今,你可以使用httpie ——固然你也可以使用curl的方式——向API server 請求獲取HPA對象使用當前的穩定版本(autoscaling/v1),或者使用以前的版本(extensions/v1beta1),獲取的兩個版本的區別以下所示:

$ http localhost:8080/apis/extensions/v1beta1/namespaces/api-server-deepdive/horizontalpodautoscalers/rcex > hpa-v1beta1.json

$ http localhost:8080/apis/autoscaling/v1/namespaces/api-server-deepdive/horizontalpodautoscalers/rcex > hpa-v1.json

$ diff -u hpa-v1beta1.json hpa-v1.json

{

  "kind": "HorizontalPodAutoscaler",

-  "apiVersion": "extensions/v1beta1",

+  "apiVersion": "autoscaling/v1",

  "metadata": {

    "name": "rcex",

    "namespace": "api-server-deepdive",

-    "selfLink": "/apis/extensions/v1beta1/namespaces/api-server-deepdive/horizontalpodautoscalers/rcex",

+    "selfLink": "/apis/autoscaling/v1/namespaces/api-server-deepdive/horizontalpodautoscalers/rcex",

    "uid": "ad7efe42-50ed-11e7-9882-5254009543f6",

    "resourceVersion": "267762",

    "creationTimestamp": "2017-06-14T10:39:00Z"

  },

  "spec": {

-    "scaleRef": {

+    "scaleTargetRef": {

      "kind": "ReplicationController",

      "name": "rcex",

-      "apiVersion": "v1",

-      "subresource": "scale"

+      "apiVersion": "v1"

    },

    "minReplicas": 2,

    "maxReplicas": 5,

-    "cpuUtilization": {

-      "targetPercentage": 80

-    }

+    "targetCPUUtilizationPercentage": 80

咱們可以看到HorizontalPodAutoscale的版本從v1beta1變爲了v1。API server可以在不一樣的版本以前無損耗轉換,不論在etcd中實際存的是哪一個版本。

在瞭解整個存儲流程以後,咱們下面來探究一下API server如何將數據進行編碼,解碼存入etcd中以JSON或protobuf的方式,同時也考慮到etcd的版本。

API Server將全部已知的Kubernetes對象類型保存在名爲Scheme的Go類型註冊表(registry)中。在此註冊表中,定義每種了Kubernetes對象的類型以及如何轉換它們,如何建立新對象,以及如何將對象編碼和解碼爲JSON或protobuf。

當API Server從客戶端接收到一個對象時,好比kubectl,經過HTTP路徑,可以知道這個對象的具體版本號。首先會爲這個對象使用對應的版本Scheme建立一個空對象,而後經過JSON或protobuf將HTTP傳過來的對象內容進行解碼轉換。解碼完成後建立對象,存入etcd中。

在API中可能會有不少版本,若是要支持每一個版本之間的直接轉換,這樣每每處理起來比較麻煩。好比某個API下面有三個版本,那麼它就要支持一個版本到另兩個版本的直接轉換(好比v1 ⇔ v1alpha1, v1 ⇔ v1beta1, v1beta1 ⇔ v1alpha1)。爲了不這個問題,在API server中有一個特別的「internal」版本。當兩個版本之間須要轉換時,先轉換爲internal版本,再轉換爲相應轉換的版本。這樣的話,每一個版本只要支持可以轉換爲internal版本,那麼就可以與其它任何版本進行間接的轉換。因此一個對象先轉換爲internal版本,而後在轉換爲穩定的v1版本,而後在存入etcd中。

v1beta1 ⇒ internal ⇒ v1

在轉換的第一步中,若是某些字段用戶沒有賦值指定,那麼這些會被賦爲一個默認值。好比在v1beta1 中確定沒有在v1版本新增的一個字段。在這種狀況下,用戶確定沒法在v1beta1 版本爲這個字段賦值。這時候,在轉換的第一步中,咱們會爲這個字段賦一個默認值以生成一個有效的internal。

校驗及准入

在轉換過程當中有兩個重要的步驟,以下圖所示:

v1beta1 ⇒ internal ⇒    |    ⇒       |    ⇒  v1  ⇒ json/yaml ⇒ etcd

                     admission    validation

准入和校驗是建立和更新對象存入etcd以前必須經過的步驟。它們的一些規則以下所示:

1.准入(Admission):查看集羣中的一些約束條件是否容許建立或更新此對象,並根據此集羣的相關配置爲對象設置一些默認值。在Kubernetes有不少這種約束條件,下面列舉一些例子:

NamespaceLifecycle:若是命名空間不存在,則拒絕該命名空間下的全部傳入請求。

LimitRanger:強制限制命名空間中資源的使用率。

ServiceAccount:爲pod建立 service account 。

DefaultStorageClass:若是用戶沒有爲PersistentVolumeClaims賦值,那麼將它設置爲一個默認值。

ResourceQuota:對羣集上的當前用戶強制執行配額約束,若是配額不足,可能會拒絕請求。

2.校驗(Validation):檢查傳入對象(在建立和更新期間)是否格式是否合法以及相關值是否有效。好比:

1)檢查必填字段是否已填。

2)檢查字符串格式是否正確(好比只容許小寫形式)。

3)是否有些字段存在衝突(好比,有兩個容器的名字同樣)。

校驗(Validation)並不關心其它類型的對象實例,換言之,它只關心每一個對象的靜態檢查,無關集羣配置。

准入(Admission)能夠用flag --admission-control=來啓動或禁用。它們中的大多數能夠有集羣管理配置。此外,在Kubernetes 1.7中能夠用webhook機制來擴展准入機制,使用控制器來實現對對象的傳統的校驗。

遷移存儲對象

關於存儲對象遷移的最後說明:當Kubernetes須要升級到新的版本時,根據每一個版本的相關文檔步驟備份相關集羣的數據是相當重要的。這一方面是因爲etcd2到etcd3的轉變,另外一方面是因爲Kubernetes 對象的Kind及version的不斷髮展。

在etcd中,每一個對象是首選存儲版本(preferred storage version)存在的。可是,隨着時間的推移,etcd存儲中的對象可能以一個很是老的版本存在。若是在未來某個時間這個對象版本被廢棄了,那麼將沒法再解碼它的protobuf 或JSON。所以,在集羣升級以前須要重寫,遷移這些數據。下面這些資料可以對version切換提供一些幫助:

請參閱集羣管理文檔升級API版本部分。Upgrading to a different API version

下一次,在深刻學習Kubernetes APIServer的第三部分中,咱們將討論如何使用Custom Resource Definitions擴展和自定義API資源。

相關文章
相關標籤/搜索