深度剖析Kubernetes API Server三部曲 - part 2

PaaS小魔仙發表於2018-08-30

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

etcd 的簡要說明

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

使用容器化版本的 etcd ,我們可以建立上面的樹,然後按如下方式檢索它:

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

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

 --advertise-client-urls --listen-client-urls

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

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

$ 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 。然後我們使用   工具來檢視相關 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 當中。

我們可以透過配置   的啟動引數 --storage-media-type 來決定想要序列化資料存入 etcd 的格式,預設情況下為 application/vnd.kubernetes.protobuf 格式。我們也可以透過配置 --storage-versions 啟動引數,來確定存入 etcd 的每個群組 Group 物件的預設版本號。

現在讓我們來看看無損轉換是如何進行的,我們將使用 Kubernetes  物件  (HPA) 來列舉說明。 HPA 顧名思義是指透過監控資源的使用情況結合 ReplicationController 控制 Pod 的伸縮。

首先我們期待一個 API 代理(以便於我們能夠在本地直接訪問它),並啟動 ReplicationController ,以及 HPA 

$ kubectl proxy --port=8080 &

$ kubectl create -f

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

kubectl get hpa/rcex -o yaml

現在,你能夠使用 —— 當然你也能夠使用 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 型別登錄檔( )中。在此登錄檔中,定義每種了 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=<plugins> 來啟動或禁用。它們中的大多數可以有叢集管理配置。此外,在 Kubernetes 1.7 中可以用 webhook 機制來擴充套件准入機制,使用控制器來實現對物件的傳統的校驗。

遷移儲存物件

關於儲存物件遷移的最後說明:當 Kubernetes 需要升級到新的版本時,根據每個版本的相關文件步驟備份相關叢集的資料是至關重要的。這一方面是由於 etcd2 etcd3 的轉變,另一方面是由於 Kubernetes  物件的 Kind version 的不斷髮展。

etcd 中,每個物件是首選儲存版本( preferred storage version )存在的。但是,隨著時間的推移, etcd 儲存中的物件可能以一個非常老的版本存在。如果在將來某個時間這個物件版本被廢棄了,那麼將無法再解碼它的 protobuf  JSON 。因此,在叢集升級之前需要重寫,遷移這些資料。 下面這些資料能夠對 version 切換提供一些幫助:

請參閱叢集管理文件升級 API 版本部分。

下一次,在深入學習 Kubernetes APIServer 的第三部分中,我們將討論如何使用 擴充套件和自定義 API 資源。https://www.huaweicloud.com/product/cce.html

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31543630/viewspace-2213243/,如需轉載,請註明出處,否則將追究法律責任。

相關文章