深度剖析Kubernetes API Server三部曲 - part 2
歡迎來到深入學習 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/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 深度剖析Kubernetes API Server三部曲 - part 1APIServer
- 深度剖析Kubernetes API Server三部曲 - part 3APIServer
- 資深專家深度剖析Kubernetes API Server第2章(共3章)APIServer
- 資深專家深度剖析Kubernetes API Server第1章(共3章)APIServer
- 資深專家深度剖析Kubernetes API Server第3章(共3章)APIServer
- Kubernetes API server工作原理APIServer
- Tinyalsa PCM API 實現深度剖析API
- Kubernetes安裝之六:配置master之api-serverASTAPIServer
- kubernetes實踐之四十五:API Server原理分析APIServer
- 深度剖析 Runtime
- kubernetes實戰篇之通過api-server訪問dashboardAPIServer
- 深度剖析一站式分散式事務方案Seata(Fescar)-Server分散式Server
- 深度剖析一站式分散式事務方案 Seata(Fescar)-Server分散式Server
- Kubernetes深入學習之二:編譯和部署映象(api-server)編譯APIServer
- Datatable Scroller (Server Side) Part:3ServerIDE
- Kubernetes Metrics Server元件Server元件
- API Schema in kubernetesAPI
- 使用 Kubernetes APIAPI
- Webshell-Part1&Part2Webshell
- 剖析Unreal Engine超真實人類的渲染技術Part 2 - 眼球渲染Unreal
- offsetParent、offsetLeft/offsetTop深度剖析
- kubernetes部署metrics-serverServer
- kubernetes實踐之七:安全機制API Server認證之Service Account TokenAPIServer
- 剖析虛幻渲染體系(06)- UE5特輯Part 2(Lumen和其它)
- 4-Kubernetes APIAPI
- Kubernetes API 基礎API
- spark核心原始碼深度剖析Spark原始碼
- 深度剖析Reflect + 實戰案例
- Kubernetes: client-go 原始碼剖析(一)clientGo原始碼
- mysqlbinlog命令詳解 Part 8 指定 Server IDMySqlServer
- 4-Overview-Kubernetes APIViewAPI
- Kubernetes Gateway API 介紹GatewayAPI
- 深度剖析Spring Cloud底層原理SpringCloud
- [Android] Toast問題深度剖析(二)AndroidAST
- [Android] Toast問題深度剖析(一)AndroidAST
- Spring AOP 原理原始碼深度剖析Spring原始碼
- 深度剖析Saga分散式事務分散式
- 深度剖析分散式事務效能分散式