在上一篇介紹了Kubernetes(以下簡稱K8s)最小單位Pod,接著要介紹的就是Deployment,它和Pod的關係就像廚師與盤子中的菜色一樣,廚師需要決定要做哪些菜品,就像在Deployment中設置Pod模板一樣,你需要設置應用程式的基本配置和資源要求。接下來,你需要準備一些盤子,這些盤子就是Pod。每個盤子都可以裝載一道菜,就像Pod可以容納一個或多個容器(以上其實是ChatGPT幫我幻想的XDD)



編寫Deployment定義檔

我們建立一個my-first-deployment.yml的定義檔案,VSCode打開後直接輸入k-deployment」快速產出簡單的範例


接著我們修改幾個部分:

  1. 將上一篇寫好的Pod【點我前往】從metadata開始貼到deployment的spec.template裡面
  2. spec.selector.mathLabels.app設定成template.metadata.labels
  3. 將spec.replicas設定成2
apiVersion: apps/v1
kind: Deployment
metadata:
  name: wadweb-deployment
  labels:
    app: wadweb-deployment
spec:
  selector:
    matchLabels:
      app: wadweb
  replicas: 2
  strategy:
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
    type: RollingUpdate
  template:
    metadata:
      name: wadweb
      labels:
        app: wadweb
    spec:
      containers:
      - name: wadwebapp
        image: "你的registryURL/wad-web:v2.0.0"
				imagePullPolicy: Always
        resources:
          limits:
            cpu: 200m
            memory: 500Mi
          requests:
            cpu: 100m
            memory: 200Mi
        ports:
          - containerPort: 80
            name: http
      - name: wadwebapi
        image: "你的registryURL/wad-web-api:v2.0.0"
				imagePullPolicy: Always
        resources:
          limits:
            cpu: 2000m
            memory: 2Gi
          requests:
            cpu: 1000m
            memory: 1Gi
        env:
          - name: DOTNET_RUNNING_IN_CONATINER
            value: "true"
          - name: ASPNETCORE_ENVIRONMENT
            value: "Development"
          - name: ASPNETCORE_URLS
            value: "http://+:8080"
          - name: ConnectionStrings__DefaultConnection
            value: "Server=你的SQLServer Connection;Initial Catalog=WADREQM;Persist Security Info=False;User ID=你的SQLServer帳號;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=true;Connection Timeout=30;"
          - name: Connection__Key
            value: "我是密碼"
          - name: HostUrl__RemoteHost
            value: "http://192.168.1.34:8080"
          - name: TZ
            value: "Asia/Taipei"
        volumeMounts:
          - name: fileupload
            mountPath: /app/upload
        ports:
          - containerPort: 8080
            name: http        
      volumes:
        - name: fileupload
          hostPath:
            path: /run/desktop/mnt/host/d/Kubernetes/fileupload
            type: DirectoryOrCreate
      restartPolicy: Always

  • spec.selector.matchLabels:用於指定Deployment、Service、ReplicaSet等資源對象所要選擇的Pod標籤(Label),以範例來說就是套用Deployment到有app=wadweb的Pod身上
  • spec.replicas:設定要運行的Pod副本數量
    💡 如果設定0就會直接刪除所有的Pod,在某些情況真的會設定成0,不過我還沒遇到就是了XD

  • spec.strategy:指定Deployment或ReplicaSet的更新策略
    💡 有兩個可選的更新策略:Recreate和RollingUpdate,Recreate是默認的更新策略。當需要更新Deployment或ReplicaSet時,Kubernetes會停止所有的Pod副本,然後重新創建新的Pod副本,它優點是簡單易行,缺點是在更新過程中會導致服務中斷。而RollingUpdate則會逐步地將現有的Pod副本替換成新的Pod副本,從而實現無停機更新,因此建議需要無痛升級的可以選RollingUpdate
  • spec.strategy.rollingUpdate:設定為滾動更新
  • spec.strategy.rollingUpdate.maxSurge:更新時可以額外創建的Pod副本數量
    💡 預設值是25%,可以是固定值或是百分比(%)
          允許的格式:
          1(1個Pod)
          25%
  • spec.strategy.rollingUpdate.maxUnavailable:更新時可以同時關閉的Pod副本數量
    💡 maxSurge和maxUnavailable我也是搞了好久才弄懂,總之如果我們希望在更新過程中維持足夠的Pod副本,以確保應用程式的可用性,那麼可以將maxUnavailable設置為0,以禁止同時關閉Pod副本。這樣一來,在更新期間始終會保持足夠的Pod副本運行也就是1上1下的概念,只是速度上可能會慢一點就是了XD
  • spec.template:就是Pod的定義檔案內容




執行Deployment

建立好定義檔之後使用kubectl來建立Deployment,目前在Docker Desktop上一樣是沒有任何容器在執行


執行以下指令

kubectl apply -f my-first-deployment.yml


部署後我們可以使用以下語法查看一下剛剛部署的Deployment資訊

kubectl get deployment


這邊需要視容器情況等待一下,等待的過程如果想要即時監控Deployment的部署狀況則可以使用下列語法

kubectl rollout status deployment/wadweb-deployment


等待完成後就重新下Deployment的指令就可以看到READY是2/2並且AVAILABLE數量不會是0,另外也可以使用指令查看Pod的狀態或是從Docker Desktop上看也可以哦!


💡 由於spec.replicas是設定2因此我們可以看到總共是1個Deployment對上2個Pod,2個Pod共有4個容器




更新Deployment

成功部署Deployment後日後要做的就是

  1. 增加/減少Replicas的數量
  2. 新增容器
  3. 修改容器內容
  4. 刪除容器
  5. 等等等

我們先試著關掉Web API這個容器,關掉後定義檔會是以下這樣

apiVersion: apps/v1
kind: Deployment
metadata:
  name: wadweb-deployment
  labels:
    app: wadweb-deployment
spec:
  selector:
    matchLabels:
      app: wadweb
  replicas: 2
  strategy:
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
    type: RollingUpdate
  template:
    metadata:
      name: wadweb
      labels:
        app: wadweb
    spec:
      containers:
      - name: wadwebapp
        image: "你的registryURL/wad-web:v2.0.0"
        resources:
          limits:
            cpu: 200m
            memory: 500Mi
          requests:
            cpu: 100m
            memory: 200Mi
        ports:
          - containerPort: 80
            name: http
      restartPolicy: Always

接著就直接使用Apply的方式執行,讓K8s覆蓋掉原本的定義檔,覆蓋完成後基本上K8s就會依照你的定義檔去調整相對應的理想狀態




這裡稍微針對Pods的NAME補充一下,我們可以看到每一次Apply修改後查看Pods都會得到不同的名稱(NAME)後面這串名稱叫做「Pod-template-hash」,此標籤可以確保Deployment的子ReplicaSets不重疊,其餘的詳細內容可以參考官網的說明【點我前往





滾動升級

想像一下你正在經營一家餐廳,每天有很多客人來用餐。為了滿足客人的需求,你需要在餐廳中同時運行多個服務生,以便能夠及時地為客人提供服務。當你需要更新服務生的工作內容或調整他們的排班時,你不能直接把他們全部叫回家,然後再讓他們重新開始工作,因為這樣會影響餐廳的正常運營,因此你需要採用一種滾動更新的方式,逐步地調整服務生的工作內容和排班,使得餐廳始終能夠保持正常運營。在K8s中,滾動升級也採用了類似的策略。當你需要更新一個Deployment物件中的Pod時,K8s會先啟動一些新的 Pod,然後再逐步地停止舊的 Pod。在這個過程中,控制器會根據 maxSurge 和 maxUnavailable 屬性來決定同時運行的 Pod 數量,以及最多可以停止的 Pod 數量。這樣可以保證應用程式始終能夠保持正常運營,同時還能進行必要的更新或調整呢!


首先把剛剛的Deployment全數刪除掉
kubectl delete deployment wadweb-deployment


然後重新調整一下定義檔內容
  1. spec.replicas 改成5
  2. spec.strategy.rollingUpdate.maxSurge 改為2
  3. spec.strategy.rollingUpdate.maxUnavailable 改為2
  4. 把剛剛刪除的Web API加回來
修改完後就可以直接部署上去K8s,以下是修改後的定義檔
apiVersion: apps/v1
kind: Deployment
metadata:
  name: wadweb-deployment
  labels:
    app: wadweb-deployment
spec:
  selector:
    matchLabels:
      app: wadweb
  replicas: 5
  strategy:
    rollingUpdate:
      maxSurge: 2
      maxUnavailable: 2
    type: RollingUpdate
  template:
    metadata:
      name: wadweb
      labels:
        app: wadweb
    spec:
      containers:
      - name: wadwebapp
        image: "你的registryURL/wad-web:v2.0.0"
				imagePullPolicy: Always
        resources:
          limits:
            cpu: 200m
            memory: 500Mi
          requests:
            cpu: 100m
            memory: 200Mi
        ports:
          - containerPort: 80
            name: http
      - name: wadwebapi
        image: "你的registryURL/wad-web-api:v2.0.0"
				imagePullPolicy: Always
        resources:
          limits:
            cpu: 2000m
            memory: 2Gi
          requests:
            cpu: 1000m
            memory: 1Gi
        env:
          - name: DOTNET_RUNNING_IN_CONATINER
            value: "true"
          - name: ASPNETCORE_ENVIRONMENT
            value: "Development"
          - name: ASPNETCORE_URLS
            value: "http://+:8080"
          - name: ConnectionStrings__DefaultConnection
            value: "Server=你的SQLServer Connection;Initial Catalog=WADREQM;Persist Security Info=False;User ID=你的SQLServer帳號;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=true;Connection Timeout=30;"
          - name: Connection__Key
            value: "我是密碼"
          - name: HostUrl__RemoteHost
            value: "http://192.168.1.34:8080"
          - name: TZ
            value: "Asia/Taipei"
        volumeMounts:
          - name: fileupload
            mountPath: /app/upload
        ports:
          - containerPort: 8080
            name: http        
      volumes:
        - name: fileupload
          hostPath:
            path: /run/desktop/mnt/host/d/Kubernetes/fileupload
            type: DirectoryOrCreate
      restartPolicy: Always



接著我們修改一下Web APP改成一個不存在的容器的版本v2.0.0.21,修改完後直接部署上去然後使用指令觀察Pod的狀態
apiVersion: apps/v1
kind: Deployment
metadata:
  name: wadweb-deployment
  labels:
    app: wadweb-deployment
spec:
  selector:
    matchLabels:
      app: wadweb
  replicas: 5
  strategy:
    rollingUpdate:
      maxSurge: 2
      maxUnavailable: 2
    type: RollingUpdate
  template:
    metadata:
      name: wadweb
      labels:
        app: wadweb
    spec:
      containers:
      - name: wadwebapp
        image: "你的registryURL/wad-web:v2.0.0.21"
				imagePullPolicy: Always
        resources:
          limits:
            cpu: 200m
            memory: 500Mi
          requests:
            cpu: 100m
            memory: 200Mi
        ports:
          - containerPort: 80
            name: http
      - name: wadwebapi
        image: "你的registryURL/wad-web-api:v2.0.0"
				imagePullPolicy: Always
.
.
.
略過



maxSurge=2時表示在更新期間最多可以同時啟動 2 個額外的 Pod,因此會看到總共有 7 個 Pod 在運行,同時又因為maxUnavailable=2,表示在更新期間最多可以停止 2 個 Pod。因此當我們開始更新時,最少會有 3 個 Pod 在運行,直到新 Pod 完全啟動後,舊的 Pod 才會被停止,但又因為我們設定了不存在Image,因此圖片才會看到兩個Pod的ImagePullBackOff的狀態、兩個處於Pending的狀態、三個持續運作中,所以在整個滾動升級的過程中要確保有足夠數量的Pod服務提供給客戶使用,而這個足夠的數量就是介於3 ≤ 實際運作中的Pod ≤ 7」這個區間內哦!




回滾機制

假設你經營的餐廳,為了吸引更多的客人,你決定將菜單上的產品進行更新。你找了一位經驗豐富的廚師進行新產品的製作,但在進行更新時,你發現新產品的味道不如預期,而且還影響到了其他產品的銷售。此時,如果你有一個回滾機制,就可以快速恢復到之前的菜單,避免進一步損失。K8s中提供了回滾機制,當你需要回滾一個Deployment物件時,可以使用回滾指令並指定回滾到指定的版本。K8s會自動從指定的版本開始回滾,將舊的 Pod 啟動起來,同時停止新的 Pod。當回滾完成後,應用程式就可以快速恢復到之前的穩定狀態,簡單來說就是上一步(Undo)的概念。


要使用回滾功能可以直接使用下面的命令
kubectl rollout undo deployment/wadweb-deployment



另外也可以增加參數來指定要恢復到哪一個版本,恢復版本前還可以先用以下命令查詢一下歷史版本
kubectl rollout history deployment wadweb-deployment



接著只要在回滾的命令加上上圖的對應版本(VERSION)即可
kubectl rollout undo deployment/wadweb-deployment --to-revision=[REVISION]


最後在執行完Undo後原本spec.replicas的5個Pod全部都正常運行了!仔細看會發現原本好好的那三個Pod的Pod-template-hash」沒有變過。從時間(AGE)來看可以發現K8s就是把壞掉的那兩個給復原回來,而原本的Pod都不會動到,也因此完全不會影響到現有的使用者哦!是不是很厲害的機制呢!