Kubernetes Deployment 原始碼分析(一)
概述Deployment 基礎建立 DeploymentReplicaSet滾動更新失敗回滾歷史版本回滾其他特性小結
概述
Deployment 是最常用的 Kubernetes 原生 Workload 資源之一,我們一開始嘗試使用 Kubernetes 的時候大概率就是從執行一個 Deployment 型別的工作負載開始的。今天開始我們計劃分成幾講來從 Deployment 的特性介紹、原始碼分析等方面深度剖析 Deployment 資源和其背後的 Deployment 控制器。
Deployment 的基礎特性大家基本都熟悉,所以本文我們不計劃贅述 Deployment 的所有功能細節,而是從滾動更新等不算太基礎的特性入手,看下 Deployment 支援哪些玩法,為後面分析原始碼做準備。
Deployment 基礎
我們建立一個簡單的 Deployment,然後看下一些小細節。
建立 Deployment
以執行 nginx 為例,我們可以通過 Deployment 來拉起一個 3 副本的 nginx 負載:nginx-dp
- nginx-dp.yaml
1apiVersion:apps/v1
2kind:Deployment
3metadata:
4name:nginx-dp
5labels:
6app:nginx
7spec:
8replicas:3
9selector:
10matchLabels:
11app:nginx
12template:
13metadata:
14labels:
15 app:nginx
16spec:
17containers:
18-name:nginx
19image:nginx:1.14.2
20ports:
21-containerPort:80
通過 kubectl create -f nginx-dp.yaml
我們可以建立這個 Deployment 資源。
1#kubectlcreate-fnginx-dp.yaml
2deployment.apps/nginx-dpcreated
3#kubectlgetdeploy
4NAMEREADYUP-TO-DATEAVAILABLEAGE
5nginx-dp1/3313s
6#kubectlgetdeploy
7NAMEREADYUP-TO-DATEAVAILABLEAGE
8 nginx-dp3/33310s
等一會,就可以通過 kubectl get deploy
命令看到所有 Pod 都起來了,這裡關注下輸出欄位含義(NAME 和 AGE 就不用說了):
- UP-TO-DATE:多少副本已經更新到期望狀態了
- AVAILABLE:多少副本已經可以提供服務了
- READY:可以提供服務的副本數/期望副本數
ReplicaSet
- 查詢 ReplicaSet
1#kubectlgetrs--selector=app=nginx
2NAMEDESIREDCURRENTREADYAGE
3nginx-dp-66b6c48dd53339m54s
前面建立了 Deployment 之後可以看到叢集裡多了一個 ReplicaSet 資源,也就是說 Deployment 管理的其實是 ReplicaSet 而不是直接管理 Pod,我們繼續看下這個 ReplicaSet 的定義來驗證下這個想法:
1#kubectlgetrsnginx-dp-66b6c48dd5-oyaml
2apiVersion:apps/v1
3kind:ReplicaSet
4//……
5ownerReferences:
6-apiVersion:apps/v1
7blockOwnerDeletion:true
8controller:true
9kind:Deployment
10name:nginx-dp
11uid:97736b65-0171-4916-bb18-feccc343ac14
12resourceVersion:"1099157"
13uid:83ac5660-28eb-4d40-beb1-cb5ceb6928b6
14//……
這裡可以看到這個 ReplicaSet 屬於 Deployment 型別的 nginx-dp 資源。同樣的方法可以看到對應 Pod 是屬於 ReplicaSet 管理的。
到這裡,我們可以猜下 Deployment Controller 的實現原理,大概可以想到其通過管理 ReplicaSet 的生命週期,藉助 ReplicaSet Controller 提供的能力間接完成了 Pod 生命週期的管理;另外可以通過建立多個 ReplicaSet 資源,控制其副本數來實現滾動更新和回滾等操作。這樣 Deployment Controller 的實現邏輯就相對“高層”了。
滾動更新
- 通過
kubectl set
命令來更新映象:
1#kubectlsetimagedeployment/nginx-dpnginx=nginx:1.16.1
2deployment.apps/nginx-dpimageupdated
- 檢視 Event
1#kubectldescribedeploynginx-dp
2//……
3
4Events:
5TypeReasonAgeFromMessage
6-------------------------
7NormalScalingReplicaSet26mdeployment-controllerScaledupreplicasetnginx-dp-66b6c48dd5to3
8NormalScalingReplicaSet88sdeployment-controllerScaledupreplicasetnginx-dp-559d658b74to1
9NormalScalingReplicaSet87sdeployment-controllerScaleddownreplicasetnginx-dp-66b6c48dd5to2
10NormalScalingReplicaSet87sdeployment-controllerScaledupreplicasetnginx-dp-559d658b74to2
11NormalScalingReplicaSet86sdeployment-controllerScaleddownreplicasetnginx-dp-66b6c48dd5to1
12NormalScalingReplicaSet86sdeployment-controllerScaledupreplicasetnginx-dp-559d658b74to3
13NormalScalingReplicaSet84sdeployment-controllerScaleddownreplicasetnginx-dp-66b6c48dd5to0
從 Event 裡可以看到 deployment-controller 通過調整 ReplicaSet 資源 nginx-dp-66b6c48dd5 和 nginx-dp-559d658b74 的副本數完成了這次滾動更新,先看下這兩個 ReplicaSet:
1#kubectlgetrs--selector=app=nginx
2NAMEDESIREDCURRENTREADYAGE
3nginx-dp-559d658b74333134m
4nginx-dp-66b6c48dd5000159m
可以看到這時候新增了一個 nginx-dp-559d658b74,副本數是 3,同時老的 nginx-dp-66b6c48dd5 變成了 0 副本。這個過程大概是這樣:
- nginx-dp-66b6c48dd5 to 3 / replica set nginx-dp-559d658b74 to 1 -> 新 rs 增加一個副本到 1;合計 4 副本
- Scaled down replica set nginx-dp-66b6c48dd5 to 2 -> 老 rs 減少一個副本到 2;合計 3 副本
- Scaled up replica set nginx-dp-559d658b74 to 2 -> 新 rs 增加一個副本到 2;合計 4 副本
- Scaled down replica set nginx-dp-66b6c48dd5 to 1 -> 老 rs 減少一個副本到 1;合計 3 副本
- Scaled up replica set nginx-dp-559d658b74 to 3 -> 新 rs 增加一個副本到 3;合計 4 副本
- Scaled down replica set nginx-dp-66b6c48dd5 to 0 -> 老 rs 減少一個副本到 0;合計 3 副本
失敗回滾
歷史版本
先看下怎麼查詢更新歷史:
1#kubectlrollouthistorydeployments/nginx-dp
2deployment.apps/nginx-dp
3REVISIONCHANGE-CAUSE
41<none>
52<none>
這裡可以看到一個細節,CHANGE-CAUSE 是空的,這個欄位其實是從 kubernetes.io/change-cause 註解中拿的,我們加個註解試一下:
1kubectlannotatedeployment/nginx-dpkubernetes.io/change-cause="imageupdatedto1.16.1"
再查一次:
1#kubectlrollouthistorydeployments/nginx-dp
2deployment.apps/nginx-dp
3REVISIONCHANGE-CAUSE
41<none>
52imageupdatedto1.16.1
第一個版本怎麼辦呢?這裡大概可以猜到要儲存多個版本的 CHANGE-CAUSE 資訊,這個註解應該是用的 ReplicaSet 裡的,所以我們嘗試這樣補充第一個版本的註解:
1kubectlannotaters/nginx-dp-66b6c48dd5kubernetes.io/change-cause="nginxdeploymentcreated"
再查一次:
1#kubectlrollouthistorydeployments/nginx-dp
2deployment.apps/nginx-dp
3REVISIONCHANGE-CAUSE
41nginxdeploymentcreated
52imageupdatedto1.16.1
現在就比較和諧了,需要指定版本回滾的時候不迷路。
回滾
- 設定一個不存在的映象版本來模擬更新失敗場景:
1#kubectlsetimagedeployment/nginx-dpnginx=nginx:1.161
2deployment.apps/nginx-dpimageupdated
3#kubectlgetrs--selector=app=nginx
4NAMEDESIREDCURRENTREADYAGE
5nginx-dp-559d658b74333168m
6nginx-dp-66b6c48dd50003h13m
7nginx-dp-66bc5d6c81106s
8#kubectlgetpod--selector=app=nginx
9NAMEREADYSTATUSRESTARTSAGE
10nginx-dp-559d658b74-l4bq71/1Running0170m
11nginx-dp-559d658b74-qhh8m1/1Running0170m
12nginx-dp-559d658b74-vbtl51/1Running0170m
13nginx-dp-66bc5d6c8-tl8480/1ImagePullBackOff02m2s
- 設定個註解:
1#kubectlannotatedeployment/nginx-dpkubernetes.io/change-cause="imageupdatedto1.161"
2deployment.apps/nginx-dpannotated
3#kubectlrollouthistorydeployments/nginx-dp
4deployment.apps/nginx-dp
5REVISIONCHANGE-CAUSE
61nginxdeploymentcreated
72imageupdatedto1.16.1
83imageupdatedto1.161
- 回滾到 revision 2:
1#kubectlrolloutundodeployment/nginx-dp
2deployment.apps/nginx-dprolledback
3#kubectlrollouthistorydeployments/nginx-dp
4deployment.apps/nginx-dp
5REVISIONCHANGE-CAUSE
61nginxdeploymentcreated
73imageupdatedto1.161
84imageupdatedto1.16.1
這時候版本 2 變成了最新的版本:4
- 檢視某個版本的詳細配置:
1#kubectlrollouthistorydeployments/nginx-dp--revision=1
2deployment.apps/nginx-dpwithrevision#1
3PodTemplate:
4Labels:app=nginx
5pod-template-hash=66b6c48dd5
6Annotations:kubernetes.io/change-cause:nginxdeploymentcreated
7Containers:
8nginx:
9Image:nginx:1.14.2
10Port:80/TCP
11HostPort:0/TCP
12Environment:<none>
13Mounts:<none>
14Volumes:<none>
- 指定版本回滾:
1#kubectlrolloutundodeployment/nginx-dp--to-revision=1
2deployment.apps/nginx-dprolledback
3#kubectlrollouthistorydeployments/nginx-dp
4deployment.apps/nginx-dp
5REVISIONCHANGE-CAUSE
63imageupdatedto1.161
74imageupdatedto1.16.1
85nginxdeploymentcreated
其他特性
最後我們看下 Deployment 型別 spec 的全部屬性:
- minReadySeconds:預設值為 0,表示一個 pod reday 之後多長時間可以提供服務;換句話說配置成1就是 pod ready 之後 1s 才對外提供服務;
- paused:掛起;
- progressDeadlineSeconds:預設 600,表示處理一個 Deployment 任務的超時時間,比如 10 分鐘到了還沒有升級成功,則標記為 failed 狀態;
- replicas:副本數;
- revisionHistoryLimit:預設是 10,表示保留的歷史版本數量;
- selector:標籤選擇器;
- strategy:表示 Deployment 更新 pod 時的替換策略;
- template:Pod 模板;
這裡的 strategy 有兩個屬性,分別是:type 和 rollingUpdate。type 可選值是:"Recreate" 和 "RollingUpdate",預設為 "RollingUpdate"。strategy.rollingUpdate 有兩個屬性:
- maxSurge: 表示滾動更新的時候最多可以比期望副本數多幾個,數字或者百分比配置都行;比如 1 表示更新過程中最多同時新增 1 個副本,然後等一個老副本刪掉之後才能繼續增加 1 個新副本;百分比計算的時候向上取整;
- maxUnavailable:表示滾動更新的時候可以有多少副本不可用,同樣是數字或者百分比配置;比如期望副本數是 3,1 則表示最多刪除副本到剩下 2,然後要等新副本建立才能繼續刪除;百分比計算的時候向下取整;
小結
本文我們的目的是知道 Deployment 的全部特性,進而為後面的原始碼分析做準備。在這個過程中我們沒有贅述 Deployment 的基礎特性,而是主要介紹“滾動更新”和“回滾”等主要功能,另外簡單過一下 Deployment 的 spec 包含的全量配置項,從而心中有個概念,知道 Deployment 的能力邊界在那裡,從而後面看原始碼時更有針對性。
(轉載請保留本文原始連結 https://www.danielhu.cn)