Kubernetes Deployment 原始碼分析(一)

胡說雲原生發表於2021-11-02

概述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:
4  name: nginx-dp
5  labels:
6    app: nginx
7spec:
8  replicas: 3
9  selector:
10    matchLabels:
11      app: nginx
12  template:
13    metadata:
14      labels:
15        app: nginx
16    spec:
17      containers:
18      - name: nginx
19        image: nginx:1.14.2
20        ports:
21        - containerPort: 80

通過 kubectl create -f nginx-dp.yaml 我們可以建立這個 Deployment 資源。

1# kubectl create -f nginx-dp.yaml
2deployment.apps/nginx-dp created
3# kubectl get deploy
4NAME       READY   UP-TO-DATE   AVAILABLE   AGE
5nginx-dp   1/3     3            1           3s
6# kubectl get deploy
7NAME       READY   UP-TO-DATE   AVAILABLE   AGE
8nginx-dp   3/3     3            3           10s

等一會,就可以通過 kubectl get deploy 命令看到所有 Pod 都起來了,這裡關注下輸出欄位含義(NAME 和 AGE 就不用說了):

  • UP-TO-DATE:多少副本已經更新到期望狀態了
  • AVAILABLE:多少副本已經可以提供服務了
  • READY:可以提供服務的副本數/期望副本數

ReplicaSet

  • 查詢 ReplicaSet
1# kubectl get rs --selector=app=nginx
2NAME                  DESIRED   CURRENT   READY   AGE
3nginx-dp-66b6c48dd5   3         3         3       9m54s

前面建立了 Deployment 之後可以看到叢集裡多了一個 ReplicaSet 資源,也就是說 Deployment 管理的其實是 ReplicaSet 而不是直接管理 Pod,我們繼續看下這個 ReplicaSet 的定義來驗證下這個想法:

 1# kubectl get rs nginx-dp-66b6c48dd5 -o yaml
2apiVersion: apps/v1
3kind: ReplicaSet
4// ……
5  ownerReferences:
6  - apiVersion: apps/v1
7    blockOwnerDeletion: true
8    controller: true
9    kind: Deployment
10    name: nginx-dp
11    uid: 97736b65-0171-4916-bb18-feccc343ac14
12  resourceVersion: "1099157"
13  uid: 83ac5660-28eb-4d40-beb1-cb5ceb6928b6
14// ……

這裡可以看到這個 ReplicaSet 屬於 Deployment 型別的 nginx-dp 資源。同樣的方法可以看到對應 Pod 是屬於 ReplicaSet 管理的。

到這裡,我們可以猜下 Deployment Controller 的實現原理,大概可以想到其通過管理 ReplicaSet 的生命週期,藉助 ReplicaSet Controller 提供的能力間接完成了 Pod 生命週期的管理;另外可以通過建立多個 ReplicaSet 資源,控制其副本數來實現滾動更新和回滾等操作。這樣 Deployment Controller 的實現邏輯就相對“高層”了。

滾動更新

  • 通過 kubectl set 命令來更新映象:
1# kubectl set image deployment/nginx-dp nginx=nginx:1.16.1
2deployment.apps/nginx-dp image updated
  • 檢視 Event
 1# kubectl describe deploy nginx-dp
2// ……
3
4Events:
5  Type    Reason             Age   From                   Message
6  ----    ------             ----  ----                   -------
7  Normal  ScalingReplicaSet  26m   deployment-controller  Scaled up replica set nginx-dp-66b6c48dd5 to 3
8  Normal  ScalingReplicaSet  88s   deployment-controller  Scaled up replica set nginx-dp-559d658b74 to 1
9  Normal  ScalingReplicaSet  87s   deployment-controller  Scaled down replica set nginx-dp-66b6c48dd5 to 2
10  Normal  ScalingReplicaSet  87s   deployment-controller  Scaled up replica set nginx-dp-559d658b74 to 2
11  Normal  ScalingReplicaSet  86s   deployment-controller  Scaled down replica set nginx-dp-66b6c48dd5 to 1
12  Normal  ScalingReplicaSet  86s   deployment-controller  Scaled up replica set nginx-dp-559d658b74 to 3
13  Normal  ScalingReplicaSet  84s   deployment-controller  Scaled down replica set nginx-dp-66b6c48dd5 to 0

從 Event 裡可以看到 deployment-controller 通過調整 ReplicaSet 資源 nginx-dp-66b6c48dd5 和 nginx-dp-559d658b74 的副本數完成了這次滾動更新,先看下這兩個 ReplicaSet:

1# kubectl get rs --selector=app=nginx
2NAME                  DESIRED   CURRENT   READY   AGE
3nginx-dp-559d658b74   3         3         3       134m
4nginx-dp-66b6c48dd5   0         0         0       159m

可以看到這時候新增了一個 nginx-dp-559d658b74,副本數是 3,同時老的 nginx-dp-66b6c48dd5 變成了 0 副本。這個過程大概是這樣:

  1. nginx-dp-66b6c48dd5 to 3 / replica set nginx-dp-559d658b74 to 1 -> 新 rs 增加一個副本到 1;合計 4 副本
  2. Scaled down replica set nginx-dp-66b6c48dd5 to 2 -> 老 rs 減少一個副本到 2;合計 3 副本
  3. Scaled up replica set nginx-dp-559d658b74 to 2 -> 新 rs 增加一個副本到 2;合計 4 副本
  4. Scaled down replica set nginx-dp-66b6c48dd5 to 1 -> 老 rs 減少一個副本到 1;合計 3 副本
  5. Scaled up replica set nginx-dp-559d658b74 to 3 -> 新 rs 增加一個副本到 3;合計 4 副本
  6. Scaled down replica set nginx-dp-66b6c48dd5 to 0 -> 老 rs 減少一個副本到 0;合計 3 副本

失敗回滾

歷史版本

先看下怎麼查詢更新歷史:

1# kubectl rollout history deployments/nginx-dp
2deployment.apps/nginx-dp
3REVISION  CHANGE-CAUSE
41         <none>
52         <none>

這裡可以看到一個細節,CHANGE-CAUSE 是空的,這個欄位其實是從 kubernetes.io/change-cause 註解中拿的,我們加個註解試一下:

1kubectl annotate deployment/nginx-dp kubernetes.io/change-cause="image updated to 1.16.1"

再查一次:

1# kubectl rollout history deployments/nginx-dp
2deployment.apps/nginx-dp
3REVISION  CHANGE-CAUSE
41         <none>
52         image updated to 1.16.1

第一個版本怎麼辦呢?這裡大概可以猜到要儲存多個版本的 CHANGE-CAUSE 資訊,這個註解應該是用的 ReplicaSet 裡的,所以我們嘗試這樣補充第一個版本的註解:

1kubectl annotate rs/nginx-dp-66b6c48dd5 kubernetes.io/change-cause="nginx deployment created"

再查一次:

1# kubectl rollout history deployments/nginx-dp
2deployment.apps/nginx-dp
3REVISION  CHANGE-CAUSE
41         nginx deployment created
52         image updated to 1.16.1

現在就比較和諧了,需要指定版本回滾的時候不迷路。

回滾

  • 設定一個不存在的映象版本來模擬更新失敗場景:
 1# kubectl set image deployment/nginx-dp nginx=nginx:1.161
2deployment.apps/nginx-dp image updated
3# kubectl get rs --selector=app=nginx
4NAME                  DESIRED   CURRENT   READY   AGE
5nginx-dp-559d658b74   3         3         3       168m
6nginx-dp-66b6c48dd5   0         0         0       3h13m
7nginx-dp-66bc5d6c8    1         1         0       6s
8# kubectl get pod --selector=app=nginx
9NAME                        READY   STATUS             RESTARTS   AGE
10nginx-dp-559d658b74-l4bq7   1/1     Running            0          170m
11nginx-dp-559d658b74-qhh8m   1/1     Running            0          170m
12nginx-dp-559d658b74-vbtl5   1/1     Running            0          170m
13nginx-dp-66bc5d6c8-tl848    0/1     ImagePullBackOff   0          2m2s
  • 設定個註解:
1# kubectl annotate deployment/nginx-dp kubernetes.io/change-cause="image updated to 1.161"
2deployment.apps/nginx-dp annotated
3# kubectl rollout history deployments/nginx-dp
4deployment.apps/nginx-dp
5REVISION  CHANGE-CAUSE
61         nginx deployment created
72         image updated to 1.16.1
83         image updated to 1.161
  • 回滾到 revision 2:
1# kubectl rollout undo deployment/nginx-dp
2deployment.apps/nginx-dp rolled back
3# kubectl rollout history deployments/nginx-dp
4deployment.apps/nginx-dp
5REVISION  CHANGE-CAUSE
61         nginx deployment created
73         image updated to 1.161
84         image updated to 1.16.1

這時候版本 2 變成了最新的版本:4

  • 檢視某個版本的詳細配置:
 1# kubectl rollout history deployments/nginx-dp --revision=1
2deployment.apps/nginx-dp with revision #1
3Pod Template:
4  Labels:    app=nginx
5    pod-template-hash=66b6c48dd5
6  Annotations:    kubernetes.io/change-cause: nginx deployment created
7  Containers:
8   nginx:
9    Image:    nginx:1.14.2
10    Port:    80/TCP
11    Host Port:    0/TCP
12    Environment:    <none>
13    Mounts:    <none>
14  Volumes:    <none>
  • 指定版本回滾:
1# kubectl rollout undo deployment/nginx-dp --to-revision=1
2deployment.apps/nginx-dp rolled back
3# kubectl rollout history deployments/nginx-dp
4deployment.apps/nginx-dp
5REVISION  CHANGE-CAUSE
63         image updated to 1.161
74         image updated to 1.16.1
85         nginx deployment created

其他特性

最後我們看下 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 有兩個屬性,分別是:typerollingUpdate。type 可選值是:"Recreate" 和 "RollingUpdate",預設為 "RollingUpdate"。strategy.rollingUpdate 有兩個屬性:

  • maxSurge: 表示滾動更新的時候最多可以比期望副本數多幾個,數字或者百分比配置都行;比如 1 表示更新過程中最多同時新增 1 個副本,然後等一個老副本刪掉之後才能繼續增加 1 個新副本;百分比計算的時候向上取整;
  • maxUnavailable:表示滾動更新的時候可以有多少副本不可用,同樣是數字或者百分比配置;比如期望副本數是 3,1 則表示最多刪除副本到剩下 2,然後要等新副本建立才能繼續刪除;百分比計算的時候向下取整;

小結

本文我們的目的是知道 Deployment 的全部特性,進而為後面的原始碼分析做準備。在這個過程中我們沒有贅述 Deployment 的基礎特性,而是主要介紹“滾動更新”和“回滾”等主要功能,另外簡單過一下 Deployment 的 spec 包含的全量配置項,從而心中有個概念,知道 Deployment 的能力邊界在那裡,從而後面看原始碼時更有針對性。

(轉載請保留本文原始連結 https://www.danielhu.cn)

 

 

相關文章