1 Deployment

1.1 什麼是deployment

Deployment(簡稱為deploy)是Kubernetes控制器的一種高級別實現,他構建於ReplicaSet控制器之上。

我們只需要描述Deployment中的目標Pod期望狀態,而Deployment控制器以受控速率更改實際狀態,使其變為期望狀態,也就是説,後期我們部署應用不直接使用Pod和ReplicaSet,

而是使用Deployment控制器來調用ReplicaSet來實現,Deployment控制器在ReplicaSet原有基礎上,添加了部分特性:

  • 事件和狀態查看:可以通過特定的命令查看Deployment對象的更新進度和狀態;
  • 版本記錄:將Deployment對象的更新操作都進行保存,以便後續執行回滾操作使用;
  • 多種更新方案:Recreate重建,可以實現單批次更新所有Pod。RollingUpdate可以實現多批次逐步替換Pod;

1.2 deployment組成部分

Deployment資源對象的格式和ReplicaSet幾乎一致,Deployment控制器也包含了3個基本的組成部分:

  • selector 標籤選擇器:匹配並關聯Pod對象,並對授其管控的Pod對象計數;
  • replicas 期望的副本數:期望在集羣中所運行的Pod對象數量;
  • template Pod模板:實際上就是定義Pod內容,相當於把一個Pod的描述以模板形式嵌入到了ReplicaSet;

K8s控制器Deployment(補充)_nginx

1.3 deployment配置示例

apiVersion: apps/v1
kind: Deployment
metadata:
  name: deploy-nginx
  namespace: default
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      name: nginx
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 80

2 Deployment場景實踐

2.1 場景説明

運行一個demoapp的應用,部署3個副本,然後通過service來實現負載均衡

  1. 創建Deployment資源,部署三個副本;
  2. 創建Service資源,通過標籤選擇器選擇對應的Pod,以實現負載均衡;
  3. 使用curl命令驗證集羣高可用;

K8s控制器Deployment(補充)_nginx_02

2.2 創建應用集羣

cat deploy-demoapp.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: deploy-demoapp
spec:
  replicas: 3
  selector:
    matchLabels:
      app: demoapp
  template:
    metadata:
      labels:
        app: demoapp
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 80
        lifecycle:
          postStart:
            exec:
              command: ["sh", "-c", "echo demoapp v1.0!! NodeName: ${node_name} PodIP: ${pod_ip} > /usr/share/nginx/html/index.html"]
        env:
        - name: pod_ip
          valueFrom:
            fieldRef:
              fieldPath: status.podIP
        - name: node_name
          valueFrom:
            fieldRef:
              fieldPath: spec.nodeName

2.3 檢查集羣狀態

1、檢查集羣的Deployment

K8s控制器Deployment(補充)_nginx_03

  • NAME: 列出了集羣中Deployment的名稱;
  • READY: 顯示應用程序的可用的"副本"數。顯示的模式是"就緒個數/期望個數";
  • UP-TO-DATE: 顯示為了達到期望狀態已經更新的副本數;
  • AVAILABLE: 顯示應用可供用户使用的副本數;
  • AGE: 顯示應用程序運行的時間;

2、檢查集羣的ReplicaSet

K8s控制器Deployment(補充)_Pod_04

  • NAME: 列出名稱空間中ReplicaSet的名稱;
  • DESIRED: 顯示應用的期望副本個數,即在創建Deployment時所定義的值。此為期望狀態;
  • CURRENT: 顯示當前運行狀態中的副本個數;
  • READY: 顯示應用中有多少副本可以為用户提供服務;
  • AGE: 顯示應用已經運行的時間長度

注意ReplicaSet的名稱被格式化為[Deployment名稱]-[隨機字符串]

3、檢查集羣的Pod,所創建的ReplicaSet確保總是存在三個Pod。

K8s控制器Deployment(補充)_Pod_05

2.4 創建Service

cat service-demoapp.yml
apiVersion: v1
kind: Service
metadata:
  name: svc-demoapp
spec:
  selector:
    app: demoapp
  ports:
  - port: 80
    targetPort: 80

查看對應的service地址

K8s控制器Deployment(補充)_nginx_06

2.5 驗證集羣高可用

K8s控制器Deployment(補充)_Deployment_07

2.6 水平伸縮

修改replicas對應的副本數即可。

1、命令方式修改

kubectl scale deploy deploy-demoapp --replicas=6

2、通過yaml來實現

kubectl edit deploy deploy-demoapp

3 Deployment重建策略

3.1 什麼是Recreate

重建(Recreate),當更新策略設定為Recreate,在更新鏡像時,它會先殺死正在運行的Pod,等徹底殺死以後,重新創建新的RS,然後啓動對應的Pod,那麼在這個更新過程中,會造成服務一段時間無法提供服務;

第一步:同時殺死所有舊版本的Pod,此時Pod無法正常對外提供服務;

第二步:創建新的RS,啓動新的Pod;

第三步:等待Pod就緒,對外提供服務;

K8s控制器Deployment(補充)_Deployment_08

3.2 Recreate實踐

1、創建deploy資源,然後將更新策略設定為Recreate,而後修訂鏡像版本

cat deploy-recreate.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: deploy-nginx
spec:
  strategy:
    type: Recreate
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.42
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 80

2、觀察更新過程

將nginx鏡像版本修改為1.43

K8s控制器Deployment(補充)_nginx_09

K8s控制器Deployment(補充)_Deployment_10

通常只有當應用的新舊版本不兼容(例如依賴的後端數據的格式不同且無法兼容)時才會使用Recreate重建策略。

4 Deployment滾動更新

4.1 什麼是滾動更新

滾動更新(RollingUpdate),一次僅更新一批Pod,當更新的Pod就緒後,再更新另一批,直到全部更新完成為止;該策略實現了不間斷服務的目標,在更新過程中可能會出現不同的應用版本並存,同時提供服務的情況。

第一步:創建新的ReplicaSet,然後根據新的鏡像運行新的Pod;

第二步:刪除舊的Pod,啓動新的Pod,新Pod就緒後,繼續刪除酒Pod,啓動新Pod;

第三步:持續第二步過程,一直到所有Pod都被更新成功;

K8s控制器Deployment(補充)_Deployment_11

4.2 滾動更新實踐

1、準備yml文件

cat deploy-rollingupdate.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: deploy-nginx
spec:
  strategy:
    type: RollingUpdate
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.43
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 80

2、觀察更新過程

K8s控制器Deployment(補充)_nginx_12

4.3 應用回退實踐

有些時候,想要回滾Deployment。例如,當Deployment不穩定進入反覆崩潰狀態。默認情況下,Deployment的所有上線記錄都保留在系統中,以便可以隨時回滾。

可以通過修改revisionHistoryLimit調整保留的數量,默認10條。

1、首先檢查Deployment上線的歷史版本

kubectl rollout history deploy deploy-nginx

K8s控制器Deployment(補充)_Pod_13

2、查看每個version對應的具體信息

K8s控制器Deployment(補充)_Deployment_14

3、確認要回退的revision,然後執行回退命令即可

kubectl rollout undo deploy deploy-nginx --to-revisinotallow=1

K8s控制器Deployment(補充)_nginx_15

原先的版本1變成了新的版本3

5 Deployment更新策略

Deployment會在.spec.strategy.type=RollingUpdate時。採取滾動更新的方式更新Pod。可以指定maxUnavailable和maxSurge來控制滾動更新的過程

maxSurge:最大可用Pod

用來指定可以創建超出期望Pod個數的Pod數量。可以是數字,也可以是百分比(例如10%),此字段的默認值為25%。

例如,當此值為20%時,啓動滾動更新後,會立即對新的ReplicaSet擴容,同時保證新舊Pod的總數不超過所需Pod總數的120%。一旦舊Pod被殺死,新的ReplicaSet可以進一步擴容,同時確保更新期間任何時候運行中的Pod總數最多為所需Pod總數的120%。計算公式:10+(10*20%)=12

maxUnavailable:最大不可用Pod

用來指定更新過程中不可用的Pod的個數上限。可以是數字,也可以是百分比(例如10%),此字段的默認值為25%

例如,當此值設置為20%時,滾動更新開始時立即將舊ReplicaSet縮容到期望Pod個數的80%,然後新Pod準備就緒後,繼續縮容舊有的ReplicaSet,然後對新的ReplicaSet擴容,確保在更新期間可用的Pod總數在任何時候都是所需的Pod個數的80%。計算公式:10-(10*20%)=8

和maxUnavailable兩個屬性協同工作,可組合定義出三種不同的策略完成多批次的應用更新。

  • 先增新,後減舊:將maxSurge設置為30%,將maxUnavailable的值設置為0;
  • 先減舊,後增新:將maxUnavailable設置為30%,將maxSurge的值設置為0;
  • 同時增減,將maxSurge和maxUnavailable分別設定為20%,期望是12Pod,至少就緒8個Pod;

5.1 maxSurge

K8s控制器Deployment(補充)_nginx_16

5.2 maxUnavailable

K8s控制器Deployment(補充)_nginx_17

5.3 maxSurge和maxUnavailable

apiVersion: apps/v1
kind: Deployment
metadata:
  name: deploy-nginx
spec:
  strategy:
    rollingUpdate:
      maxSurge: 20%
      maxUnavailable: 20%
    type: RollingUpdate
  replicas: 10
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.42
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 80

5.4 minReadySeconds

Deployment支持使用.spec.minReadySeconds字段來控制滾動更新的速度,默認值為0,表示新建的Pod對象一旦"就緒"將立即被視作可用,隨後即可開始下一輪更新過程。如果設定了spec.minReadySeconds: 3即表示新建的Pod對象至少要成功運行多久才會被視作可用,即就緒之後海耀等待指定的3s才能開始下一批次的更新。在一個批次內新建的所有Pod就緒後在轉為可用狀態前,更新操作會被堵塞,並且任何一個Pod就緒探測失敗,都會導致滾動更新被終止。

因此,為minReadySeconds設定一個合理的值,不僅能夠減緩更新的速度,還能夠讓Deployment提前發現一部分程序因為Bug導致的升級故障。

5.5 revisionHistoryLimit

Deployment保留一部分更新歷史中舊版本的ReplicaSet對象,當我們執行回滾操作的時候,就直接使用舊版本的ReplicaSet,在Deployment資源保存歷史版本數量有spec.revisionHistoryLimit屬性進行定義。

K8s控制器Deployment(補充)_Deployment_18

5.6 progressDeadlineSeconds

滾動更新故障超時時長,默認為600秒,k8s在升級過程中有可能由於各種原因升級卡住(這個時候還沒有明確的升級失敗),比如在拉取被牆的鏡像、權限不夠等錯誤。如果配置progressDeadlineSeconds,當達到了時間如果還卡着,則會上報這個異常情況,這個時候Deployment狀態就會被標記為False,並且註明原因。但是它並不會阻止Deployment繼續進行卡住後面的升級操作。

6 Deployment實現灰度發佈

灰度發佈(又名金絲雀發佈)是指黑與白之間,能夠平滑過渡的一種發佈方式,在上面可以進行A/B Testing

首先讓所有用户使用產品特性A(舊版本);

其次讓一部分人開始使用產品特性B(新版本),前端服務將通過選擇標籤的相同子集來覆蓋兩套副本,這樣,流量會被轉發到兩個應用;

最後如果用户對產品特性B沒有反對意見,那麼逐步擴充新版本,逐步縮減舊版本,直到新版本達到期望應用數量,舊版本下線;

使用灰度發佈的模式,可以及時發現問題,調整問題,以減少影響的範圍,保證整體系統的穩定運行。

K8s控制器Deployment(補充)_nginx_19

如果新版本沒有問題,那麼逐步擴大新版本的訪問流量,然後減少舊版本的訪問流量。

K8s控制器Deployment(補充)_nginx_20

最後刪除舊版本的Deployment,或者將ReplicaSet副本數設定為0,至此所有的流量都進入新版本。

K8s控制器Deployment(補充)_Pod_21

6.1 部署1.0版本應用

cat deploy-demoapp10.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: deploy-demoapp10
spec:
  replicas: 3
  selector:
    matchLabels:
      app: demoapp
      version: v10
  template:
    metadata:
      labels:
        app: demoapp
        version: v10
    spec:
      containers:
      - name: demoapp
        image: nginx:latest
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 80
        lifecycle:
          postStart:
            exec:
              command: ["sh", "-c", "echo demoapp v1.0!! NodeName: ${node_name} PodIP: ${pod_ip} > /usr/share/nginx/html/index.html"]
        env:
        - name: pod_ip
          valueFrom:
            fieldRef:
              fieldPath: status.podIP
        - name: node_name
          valueFrom:
            fieldRef:
              fieldPath: spec.nodeName

6.2 部署負載均衡服務

cat service-demoapp.yml
apiVersion: v1
kind: Service
metadata:
  name: svc-demoapp
spec:
  selector:
    app: demoapp
  ports:
  - port: 80
    targetPort: 80

K8s控制器Deployment(補充)_Deployment_22

K8s控制器Deployment(補充)_Deployment_23

6.3 部署1.1版本應用

cat deploy-demoapp11.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: deploy-demoapp11
spec:
  replicas: 1#部署一個副本作為灰度應用
  selector:
    matchLabels:
      app: demoapp
      version: v11
  template:
    metadata:
      labels:
        app: demoapp
        version: v11
    spec:
      containers:
      - name: demoapp
        image: nginx:latest
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 80
        lifecycle:
          postStart:
            exec:
              command: ["sh", "-c", "echo demoapp v1.1!! NodeName: ${node_name} PodIP: ${pod_ip} > /usr/share/nginx/html/index.html"]
        env:
        - name: pod_ip
          valueFrom:
            fieldRef:
              fieldPath: status.podIP
        - name: node_name
          valueFrom:
            fieldRef:
              fieldPath: spec.nodeName

6.4 測試新老版本共存

K8s控制器Deployment(補充)_Deployment_24

6.5 控制新老版本流量

1、逐步增加1.1版本的數量

2、逐步減少1.0版本的數量