概述#

三兄弟的最後一位是 Deployment。它為 Pod 與 ReplicaSet 提供宣告式 API(Declarative API)的設定方式,讓使用者只需描述「期望的容器執行狀態」,Kubernetes(K8s)就會持續比對並調整實際狀態。官方建議透過 Deployment 部署 Pod 與 ReplicaSet,而不要單獨使用後兩者。

Deployment 的典型使用情境:

  • 建立 Pod 與 ReplicaSet。
  • 滾動升級與回滾。
  • 水平擴展與縮減。
  • 暫停與繼續部署。

ReplicaSet 是什麼#

ReplicaSet 負責保證指定的 Pod 數量符合使用者期望(desired state)。當實際 Pod 數量少於期望時,它會建立新的 Pod;多於期望時則會刪除多餘的 Pod。

雖然 ReplicaSet 已經能完成這件事,但它的能力相對單純。Deployment 是更上層的抽象,內部會自動建立並管理 ReplicaSet,並額外提供更新策略、版本歷史等功能。三者的關係大致是:

Deployment → 管理多個 ReplicaSet → 各自管理一群 Pod

實戰演練#

1. 建立 Deployment#

下列檔案宣告兩個 Deployment,分別管理 foobar 各一份 Pod:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: foo-deployment
  labels:
    type: demo
spec:
  replicas: 1
  selector:
    matchLabels:
      type: demo
  template:
    metadata:
      labels:
        type: demo
    spec:
      containers:
        - name: foo
          image: mikehsu0618/foo
          ports:
            - containerPort: 8080
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: bar-deployment
  labels:
    type: demo
spec:
  replicas: 1
  selector:
    matchLabels:
      type: demo
  template:
    metadata:
      labels:
        type: demo
    spec:
      containers:
        - name: bar
          image: mikehsu0618/bar
          ports:
            - containerPort: 8080

關鍵欄位說明:

  • kind:選擇為 Deployment
  • spec.replicas:要產生多少個 Pod,是水平擴展的關鍵。
  • spec.selector.matchLabels:用來選擇受此 Deployment 管理的 Pod,必須與 template.metadata.labels 相同。
  • spec.template:Pod 範本,內容與直接撰寫 Pod 設定幾乎一致。

套用設定:

kubectl apply -f ./deployment.yaml

確認結果:

kubectl get all

可以看到 Deployment、ReplicaSet、Pod 三者都被建立起來,Pod 名稱會帶有 ReplicaSet 的雜湊後綴,例如 foo-deployment-6bbf665b47-kfvxr

2. 水平擴展 Deployment#

有三種常見方式調整 Pod 副本數:

方法一:修改 YAML 後重新 apply

foo-deploymentspec.replicas 改為 2,再執行:

kubectl apply -f ./deployment.yaml --record

接著用以下指令觀察 rollout 是否完成:

kubectl rollout status deployment foo-deployment

方法二:使用 scale 指令

kubectl scale deployment bar-deployment --replicas 3

方法三:直接編輯執行中的設定

kubectl edit deploy bar-deployment

執行後會打開預設編輯器,可即時修改設定。

完成後再次用 kubectl get all 觀察,會看到對應的 Pod 數量被調整,ReplicaSet 與 Deployment 的 READYAVAILABLE 欄位也會跟著更新。

3. 用 Rollout 查看歷史與回滾#

更新 Deployment 時,Kubernetes 會在符合條件下產生新的 Revision(部署歷史版本)。

並非每次更新都會產生 Revision,僅當 Deployment 第一次建立、或是 spec.template 範圍下的設定改變時才會記錄。例如只調整 replicas 並不會產生新的 Revision。

下面把 bar-deployment 的容器映像故意指向一個不存在的 tag mikehsu0618/bar:v1,模擬一次失敗的更新:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: bar-deployment
  labels:
    type: demo
spec:
  replicas: 3
  selector:
    matchLabels:
      type: demo
  template:
    metadata:
      labels:
        type: demo
    spec:
      containers:
        - name: bar
          image: mikehsu0618/bar:v1
          ports:
            - containerPort: 8080

套用變更並記錄指令:

kubectl apply -f deployment.yaml --record

--record 會在 Annotation 中存下實際執行的 kubectl 指令,便於日後在 rollout history 中查找變更原因。雖然 --record 已標示為將被棄用,但官方目前尚未提供等效的替代方案,仍是常見作法。

查看歷史版本:

kubectl rollout history deployment bar-deployment

範例輸出:

REVISION  CHANGE-CAUSE
1         <none>
2         kubectl apply --filename=deployment.yaml --record=true

進一步檢視某個 Revision 的細節:

kubectl rollout history deployment bar-deployment --revision=2

由於 tag 不存在,Pod 會卡在 ImagePullBackOff 狀態:

kubectl get all

此時可以使用 rollout 回滾到先前能正常運作的版本:

# 回滾到上一個版本
kubectl rollout undo deployment bar-deployment --record

# 回滾到指定版本
kubectl rollout undo deployment bar-deployment --to-revision=1 --record

回滾完成後,再次執行 kubectl get all,可以看到失敗的 ReplicaSet 副本數歸零,原本正常的 ReplicaSet 重新承擔流量。

小結#

Deployment 把 Pod 的生命週期管理變成「描述期望狀態」這件事,使用者不必親自處理擴展、升級、回滾的細節。透過 ReplicaSet 與 Revision 機制,藍綠部署、金絲雀部署等進階策略也能在這個基礎上實現。理解三兄弟的分工後,後續面對 Ingress、Volume、Autoscaling 等主題會更有脈絡可依。

原文出處#

  • GitHub:https://github.com/MikeHsu0618/2022-ithelp/tree/main/Day8
  • iThome:https://ithelp.ithome.com.tw/articles/10288602