天天看點

K8S學習筆記之Kubernetes 部署政策詳解

0x00 概述

在​

​Kubernetes​

​中有幾種不同的方式釋出應用,是以為了讓應用在更新期間依然平穩提供服務,選擇一個正确的釋出政策就非常重要了。

選擇正确的部署政策是要依賴于我們的業務需求的,下面我們列出了一些可能會使用到的政策:

  • 重建(recreate):停止舊版本部署新版本
  • 滾動更新(rolling-update):一個接一個地以滾動更新方式釋出新版本
  • 藍綠(blue/green):新版本與舊版本一起存在,然後切換流量
  • 金絲雀(canary):将新版本面向一部分使用者釋出,然後繼續全量釋出
  • A/B測(a/b testing):以精确的方式(HTTP 頭、cookie、權重等)向部分使用者釋出新版本。​

    ​A/B測​

    ​實際上是一種基于資料統計做出業務決策的技術。在 Kubernetes 中并不原生支援,需要額外的一些進階元件來完成改設定(比如Istio、Linkerd、Traefik、或者自定義 Nginx/Haproxy 等)。

你可以在​

​Kubernetes​

​​叢集上來對上面的這些政策進行測試,下面的倉庫中有需要使用到的資源清單:​​https://github.com/ContainerSolutions/k8s-deployment-strategies​​

接下來我們來介紹下每種政策,看看在什麼場景下面适合哪種政策

0x01 重建(Recreate) - 最好在開發環境

政策定義為​

​Recreate​

​​的​

​Deployment​

​,會終止所有正在運作的執行個體,然後用較新的版本來重新建立它們。

spec:
  replicas: 3
  strategy:
    type: Recreate      
K8S學習筆記之Kubernetes 部署政策詳解

重新建立政策是一個虛拟部署,包括關閉版本A,然後在關閉版本A後部署版本B. 此技術意味着服務的停機時間取決于應用程式的關閉和啟動持續時間。

我們這裡建立兩個相關的資源清單檔案,app-v1.yaml:

apiVersion: v1
kind: Service
metadata:
  name: my-app
  labels:
    app: my-app
spec:
  type: NodePort
  ports:
  - name: http
    port: 80
    targetPort: http
  selector:
    app: my-app
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
  labels:
    app: my-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: my-app
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
        version: v1.0.0
      annotations:
        prometheus.io/scrape: "true"
        prometheus.io/port: "9101"
    spec:
      containers:
      - name: my-app
        image: containersol/k8s-deployment-strategies
        ports:
        - name: http
          containerPort: 8080
        - name: probe
          containerPort: 8086
        env:
        - name: VERSION
          value: v1.0.0
        livenessProbe:
          httpGet:
            path: /live
            port: probe
          initialDelaySeconds: 5
          periodSeconds: 5
        readinessProbe:
          httpGet:
            path: /ready
            port: probe
          periodSeconds: 5      

app-v2.yaml 檔案内容如下:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
  labels:
    app: my-app
spec:
  replicas: 3
  strategy:
    type: Recreate
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
        version: v2.0.0
      annotations:
        prometheus.io/scrape: "true"
        prometheus.io/port: "9101"
    spec:
      containers:
      - name: my-app
        image: containersol/k8s-deployment-strategies
        ports:
        - name: http
          containerPort: 8080
        - name: probe
          containerPort: 8086
        env:
        - name: VERSION
          value: v2.0.0
        livenessProbe:
          httpGet:
            path: /live
            port: probe
          initialDelaySeconds: 5
          periodSeconds: 5
        readinessProbe:
          httpGet:
            path: /ready
            port: probe
          periodSeconds: 5      

上面兩個資源清單檔案中的 Deployment 定義幾乎是一直的,唯一不同的是定義的環境變量​

​VERSION​

​​值不同,接下來按照下面的步驟來驗證​

​Recreate​

​政策:

  1. 版本1提供服務
  2. 删除版本1
  3. 部署版本2
  4. 等待所有副本準備就緒

首先部署第一個應用:

$ kubectl apply -f app-v1.yaml
service "my-app" created
deployment.apps "my-app" created      

測試版本1是否部署成功:

$ kubectl get pods -l app=my-app
NAME                      READY     STATUS    RESTARTS   AGE
my-app-7b4874cd75-m5kct   1/1       Running   0          19m
my-app-7b4874cd75-pc444   1/1       Running   0          19m
my-app-7b4874cd75-tlctl   1/1       Running   0          19m
$ kubectl get svc my-app
NAME      TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
my-app    NodePort   10.108.238.76   <none>        80:32532/TCP   5m
$ curl http://127.0.0.1:32532
Host: my-app-7b4874cd75-pc444, Version: v1.0.0      

可以看到版本1的應用正常運作了。為了檢視部署的運作情況,打開一個新終端并運作以下指令:

$ watch kubectl get pods -l app=my-app      

然後部署版本2的應用:

$ kubectl apply -f app-v2.yaml      

這個時候可以觀察上面新開的終端中的 Pod 清單的變化,可以看到之前的3個 Pod 都會先處于​

​Terminating​

​狀态,并且3個 Pod 都被删除後才開始建立新的 Pod。

然後測試第二個版本應用的部署進度:

$ while sleep 0.1; do curl http://127.0.0.1:32532; done
curl: (7) Failed connect to 127.0.0.1:32532; Connection refused
curl: (7) Failed connect to 127.0.0.1:32532; Connection refused
......
Host: my-app-f885c8d45-sp44p, Version: v2.0.0
Host: my-app-f885c8d45-t8g7g, Version: v2.0.0
Host: my-app-f885c8d45-sp44p, Version: v2.0.0
......      

可以看到最開始的階段服務都是處于不可通路的狀态,然後到第二個版本的應用部署成功後才正常通路,可以看到現在通路的資料是版本2了。

最後,可以執行下面的指令來清空上面的資源對象:

$ kubectl delete all -l app=my-app      

結論:

  • 應用狀态全部更新
  • 停機時間取決于應用程式的關閉和啟動消耗的時間

0x02 滾動更新(rolling-update)

滾動更新通過逐個替換執行個體來逐漸部署新版本的應用,直到所有執行個體都被替換完成為止。它通常遵循以下過程:在負載均衡器後面使用版本 A 的執行個體池,然後部署版本 B 的一個執行個體,當服務準備好接收流量時(Readiness Probe 正常),将該執行個體添加到執行個體池中,然後從執行個體池中删除一個版本 A 的執行個體并關閉,如下圖所示:

K8S學習筆記之Kubernetes 部署政策詳解

下圖是滾動更新過程應用接收流量的示意圖:

K8S學習筆記之Kubernetes 部署政策詳解

下面是 Kubernetes 中通過 Deployment 來進行滾動更新的關鍵參數:

spec:
  replicas: 3
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 2        # 一次可以添加多少個Pod
      maxUnavailable: 1  # 滾動更新期間最大多少個Pod不可用      

現在仍然使用上面的 app-v1.yaml 這個資源清單檔案,建立一個定義滾動更新的資源清單檔案 app-v2-rolling-update.yaml,檔案内容如下:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app
  labels:
    app: my-app
spec:
  replicas: 10
  # maxUnavailable設定為0可以完全確定在滾動更新期間服務不受影響,還可以使用百分比的值來進行設定。
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  selector:
    matchLabels:
      app: my-app
  template:
    metadata:
      labels:
        app: my-app
        version: v2.0.0
      annotations:
        prometheus.io/scrape: "true"
        prometheus.io/port: "9101"
    spec:
      containers:
      - name: my-app
        image: containersol/k8s-deployment-strategies
        ports:
        - name: http
          containerPort: 8080
        - name: probe
          containerPort: 8086
        env:
        - name: VERSION
          value: v2.0.0
        livenessProbe:
          httpGet:
            path: /live
            port: probe
          initialDelaySeconds: 5
          periodSeconds: 5
        readinessProbe:
          httpGet:
            path: /ready
            port: probe
          # 初始延遲設定高點可以更好地觀察滾動更新過程
          initialDelaySeconds: 15
          periodSeconds: 5      

上面的資源清單中我們在環境變量中定義了版本2,然後通過設定​

​strategy.type=RollingUpdate​

​來定義該 Deployment 使用滾動更新的政策來更新應用,接下來我們按下面的步驟來驗證滾動更新政策:

  1. 版本1提供服務
  2. 部署版本2
  3. 等待直到所有副本都被版本2替換完成

同樣,首先部署版本1應用:

$ kubectl apply -f app-v1.yaml
service "my-app" created
deployment.apps "my-app" created      

測試版本1是否部署成功:

$ kubectl get pods -l app=my-app
NAME                      READY     STATUS    RESTARTS   AGE
my-app-7b4874cd75-h8c4d   1/1       Running   0          47s
my-app-7b4874cd75-p4l8f   1/1       Running   0          47s
my-app-7b4874cd75-qnt7p   1/1       Running   0          47s
$ kubectl get svc my-app
NAME      TYPE       CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
my-app    NodePort   10.109.99.184   <none>        80:30486/TCP   1m
$ curl http://127.0.0.1:30486
Host: my-app-7b4874cd75-qnt7p, Version: v1.0.0      

同樣,在一個新終端中執行下面指令觀察 Pod 變化:

$ watch kubectl get pods -l app=my-app      

然後部署滾動更新版本2應用

$ kubectl apply -f app-v2-rolling-update.yaml
deployment.apps "my-app" configured      

這個時候在上面的 watch 終端中可以看到多了很多 Pod,還在建立當中,并沒有一開始就删除之前的 Pod,同樣,這個時候執行下面指令,測試應用狀态:

$ while sleep 0.1; do curl http://127.0.0.1:30486; done
Host: my-app-7b4874cd75-vrlj7, Version: v1.0.0
......
Host: my-app-7b4874cd75-vrlj7, Version: v1.0.0
Host: my-app-6b5479d97f-2fk24, Version: v2.0.0
Host: my-app-7b4874cd75-p4l8f, Version: v1.0.0
......
Host: my-app-6b5479d97f-s5ctz, Version: v2.0.0
Host: my-app-7b4874cd75-5ldqx, Version: v1.0.0
......
Host: my-app-6b5479d97f-5z6ww, Version: v2.0.0      

我們可以看到上面的應用并沒有出現不可用的情況,最開始通路到的都是版本1的應用,然後偶爾會出現版本2的應用,直到最後全都變成了版本2的應用,而這個時候看上面 watch 終端中 Pod 已經全部變成10個版本2的應用了,我們可以看到這就是一個逐漸替換的過程。

如果在滾動更新過程中發現新版本應用有問題,我們可以通過下面的指令來進行一鍵復原:

$ kubectl rollout undo deploy my-app
deployment.apps "my-app"      

如果你想保持兩個版本的應用都存在,那麼我們也可以執行 pause 指令來暫停更新:

$ kubectl rollout pause deploy my-app
deployment.apps "my-app" paused      

這個時候我們再去循環通路我們的應用就可以看到偶爾會出現版本1的應用資訊了。

如果新版本應用程式沒問題了,也可以繼續恢複更新:

$ kubectl rollout resume deploy my-app
deployment.apps "my-app" resumed      

最後,可以執行下面的指令來清空上面的資源對象:

$ kubectl delete all -l app=my-app      

結論:

  • 版本在執行個體之間緩慢替換
  • rollout/rollback 可能需要一定時間
  • 無法控制流量

0x03 藍/綠(blue/green) - 最好用來驗證 API 版本問題

藍/綠釋出是版本2 與版本1 一起釋出,然後流量切換到版本2,也稱為紅/黑部署。藍/綠釋出與滾動更新不同,版本2(​

​綠​

​​) 與版本1(​

​藍​

​)一起部署,在測試新版本滿足要求後,然後更新更新 Kubernetes 中扮演負載均衡器角色的 Service 對象,通過替換 label selector 中的版本标簽來将流量發送到新版本,如下圖所示:

K8S學習筆記之Kubernetes 部署政策詳解

下面是藍綠釋出政策下應用方法的示例圖:

K8S學習筆記之Kubernetes 部署政策詳解

在 Kubernetes 中,我們可以用兩種方法來實作藍綠釋出,通過單個 Service 對象或者 Ingress 控制器來實作藍綠釋出,實際操作都是類似的,都是通過 label 标簽去控制。

實作藍綠釋出的關鍵點就在于 Service 對象中 label selector 标簽的比對方法,比如我們重新定義版本1 的資源清單檔案 app-v1-single-svc.yaml,檔案内容如下:

apiVersion: v1
kind: Service
metadata:
  name: my-app
  labels:
    app: my-app
spec:
  type: NodePort
  ports:
  - name: http
    port: 80
    targetPort: http
  # 注意這裡我們比對 app 和 version 标簽,當要切換流量的時候,我們更新 version 标簽的值,比如:v2.0.0
  selector:
    app: my-app
    version: v1.0.0
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app-v1
  labels:
    app: my-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: my-app
      version: v1.0.0
  template:
    metadata:
      labels:
        app: my-app
        version: v1.0.0
      annotations:
        prometheus.io/scrape: "true"
        prometheus.io/port: "9101"
    spec:
      containers:
      - name: my-app
        image: containersol/k8s-deployment-strategies
        ports:
        - name: http
          containerPort: 8080
        - name: probe
          containerPort: 8086
        env:
        - name: VERSION
          value: v1.0.0
        livenessProbe:
          httpGet:
            path: /live
            port: probe
          initialDelaySeconds: 5
          periodSeconds: 5
        readinessProbe:
          httpGet:
            path: /ready
            port: probe
          periodSeconds: 5      

上面定義的資源對象中,最重要的就是 Service 中 label selector 的定義:

selector:
  app: my-app
  version: v1.0.0      

版本2 的應用定義和以前一樣,建立檔案 app-v2-single-svc.yaml,檔案内容如下:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app-v2
  labels:
    app: my-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: my-app
      version: v2.0.0
  template:
    metadata:
      labels:
        app: my-app
        version: v2.0.0
      annotations:
        prometheus.io/scrape: "true"
        prometheus.io/port: "9101"
    spec:
      containers:
      - name: my-app
        image: containersol/k8s-deployment-strategies
        ports:
        - name: http
          containerPort: 8080
        - name: probe
          containerPort: 8086
        env:
        - name: VERSION
          value: v2.0.0
        livenessProbe:
          httpGet:
            path: /live
            port: probe
          initialDelaySeconds: 5
          periodSeconds: 5
        readinessProbe:
          httpGet:
            path: /ready
            port: probe
          periodSeconds: 5      

然後按照下面的步驟來驗證使用單個 Service 對象實作藍/綠部署的政策:

  1. 版本1 應用提供服務
  2. 部署版本2 應用
  3. 等到版本2 應用全部部署完成
  4. 切換入口流量從版本1 到版本2
  5. 關閉版本1 應用

首先,部署版本1 應用:

$ kubectl apply -f app-v1-single-svc.yaml
service "my-app" created
deployment.apps "my-app-v1" created      

測試版本1 應用是否部署成功

$ kubectl get pods -l app=my-app
NAME                         READY     STATUS    RESTARTS   AGE
my-app-v1-7b4874cd75-7xh6s   1/1       Running   0          41s
my-app-v1-7b4874cd75-dmq8f   1/1       Running   0          41s
my-app-v1-7b4874cd75-t64z7   1/1       Running   0          41s
$ kubectl get svc -l app=my-app
NAME      TYPE       CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
my-app    NodePort   10.106.184.144   <none>        80:31539/TCP   50s
$ curl http://127.0.0.1:31539
Host: my-app-v1-7b4874cd75-7xh6s, Version: v1.0.0      

同樣,新開一個終端,執行如下指令觀察 Pod 變化:

$ watch kubectl get pod -l app=my-app      

然後部署版本2 應用:

$ kubectl apply -f app-v2-single-svc.yaml
deployment.apps "my-app-v2" created      

然後在上面 watch 終端中可以看到會多3個​

​my-app-v2​

​開頭的 Pod,待這些 Pod 部署成功後,我們再去通路目前的應用:

$ while sleep 0.1; do curl http://127.0.0.1:31539; done
Host: my-app-v1-7b4874cd75-dmq8f, Version: v1.0.0
Host: my-app-v1-7b4874cd75-dmq8f, Version: v1.0.0
......      

我們會發現通路到的都是版本1 的應用,和我們剛剛部署的版本2 沒有任何關系,這是因為我們 Service 對象中通過 label selector 比對的是​

​version=v1.0.0​

​​這個标簽,我們可以通過修改 Service 對象的比對标簽,将流量路由到标簽​

​version=v2.0.0​

​的 Pod 去:

$ kubectl patch service my-app -p '{"spec":{"selector":{"version":"v2.0.0"}}}'
service "my-app" patched      

然後再去通路應用,可以發現現在都是版本2 的資訊了:

$ while sleep 0.1; do curl http://127.0.0.1:31539; done
Host: my-app-v2-f885c8d45-r5m6z, Version: v2.0.0
Host: my-app-v2-f885c8d45-r5m6z, Version: v2.0.0
......      

如果你需要復原到版本1,同樣隻需要更改 Service 的比對标簽即可:

$ kubectl patch service my-app -p '{"spec":{"selector":{"version":"v1.0.0"}}}'      

如果新版本已經完全符合我們的需求了,就可以删除版本1 的應用了:

$ kubectl delete deploy my-app-v1      

最後,同樣,執行如下指令清理上述資源對象:

$ kubectl delete all -l app=my-app      

結論:

  • 實時部署/復原
  • 避免版本問題,因為一次更改是整個應用的改變
  • 需要兩倍的資源
  • 在釋出到生産之前,應該對整個應用進行适當的測試

0x04 金絲雀(Canary) - 讓部分使用者參與測試

金絲雀部署是讓部分使用者通路到新版本應用,在 Kubernetes 中,可以使用兩個具有相同 Pod 标簽的 Deployment 來實作金絲雀部署。新版本的副本和舊版本的一起釋出。在一段時間後如果沒有檢測到錯誤,則可以擴充新版本的副本數量并删除舊版本的應用。

如果需要按照具體的百分比來進行金絲雀釋出,需要盡可能的啟動多的 Pod 副本,這樣計算流量百分比的時候才友善,比如,如果你想将 1% 的流量發送到版本 B,那麼我們就需要有一個運作版本 B 的 Pod 和 99 個運作版本 A 的 Pod,當然如果你對具體的控制政策不在意的話也就無所謂了,如果你需要更精确的控制政策,建議使用服務網格(如 Istio),它們可以更好地控制流量。

K8S學習筆記之Kubernetes 部署政策詳解

在下面的例子中,我們使用 Kubernetes 原生特性來實作一個窮人版的金絲雀釋出,如果你想要對流量進行更加細粒度的控制,請使用豪華版本的 Istio。下面是金絲雀釋出的應用請求示意圖:

K8S學習筆記之Kubernetes 部署政策詳解

接下來我們按照下面的步驟來驗證金絲雀政策:

  1. 10個副本的版本1 應用提供服務
  2. 版本2 應用部署1個副本(意味着小于10%的流量)
  3. 等待足夠的時間來确認版本2 應用足夠穩定沒有任何錯誤資訊
  4. 将版本2 應用擴容到10個副本
  5. 等待所有執行個體完成
  6. 關閉版本1 應用

 首先,建立版本1 的應用資源清單,app-v1-canary.yaml,内容如下:

apiVersion: v1
kind: Service
metadata:
  name: my-app
  labels:
    app: my-app
spec:
  type: NodePort
  ports:
  - name: http
    port: 80
    targetPort: http
  selector:
    app: my-app
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app-v1
  labels:
    app: my-app
spec:
  replicas: 10
  selector:
    matchLabels:
      app: my-app
      version: v1.0.0
  template:
    metadata:
      labels:
        app: my-app
        version: v1.0.0
      annotations:
        prometheus.io/scrape: "true"
        prometheus.io/port: "9101"
    spec:
      containers:
      - name: my-app
        image: containersol/k8s-deployment-strategies
        ports:
        - name: http
          containerPort: 8080
        - name: probe
          containerPort: 8086
        env:
        - name: VERSION
          value: v1.0.0
        livenessProbe:
          httpGet:
            path: /live
            port: probe
          initialDelaySeconds: 5
          periodSeconds: 5
        readinessProbe:
          httpGet:
            path: /ready
            port: probe
          periodSeconds: 5      

其中核心的部分也是 Service 對象中的 label selector 标簽,不在具有版本相關的标簽了,然後定義版本2 的資源清單檔案,app-v2-canary.yaml,檔案内容如下:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app-v2
  labels:
    app: my-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: my-app
      version: v2.0.0
  template:
    metadata:
      labels:
        app: my-app
        version: v2.0.0
      annotations:
        prometheus.io/scrape: "true"
        prometheus.io/port: "9101"
    spec:
      containers:
      - name: my-app
        image: containersol/k8s-deployment-strategies
        ports:
        - name: http
          containerPort: 8080
        - name: probe
          containerPort: 8086
        env:
        - name: VERSION
          value: v2.0.0
        livenessProbe:
          httpGet:
            path: /live
            port: probe
          initialDelaySeconds: 5
          periodSeconds: 5
        readinessProbe:
          httpGet:
            path: /ready
            port: probe
          periodSeconds: 5      

版本1 和版本2 的 Pod 都具有一個共同的标簽​

​app=my-app​

​,是以對應的 Service 會比對兩個版本的 Pod。

首先,部署版本1 應用:

$ kubectl apply -f app-v1-canary.yaml
service "my-app" created
deployment.apps "my-app-v1" created      

然後測試版本1 應用是否正确部署了:

$ kubectl get svc -l app=my-app
NAME          TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
my-app        NodePort    10.105.133.213   <none>        80:30760/TCP   47s
$ curl http://127.0.0.1:30760
Host: my-app-v1-7b4874cd75-tsh2s, Version: v1.0.0      

同樣,新開一個終端,檢視 Pod 的變化:

$ watch kubectl get po      

然後部署版本2 應用:

$ kubectl apply -f app-v2-canary.yaml
deployment.apps "my-app-v2" created      

然後在 watch 終端頁面可以看到多了一個 Pod,現在一共 11 個 Pod,其中隻有1 個 Pod 運作新版本應用,然後同樣可以循環通路該應用,檢視是否會有版本2 的應用資訊:

$ while sleep 0.1; do curl http://127.0.0.1:30760; done
Host: my-app-v1-7b4874cd75-bhxbp, Version: v1.0.0
Host: my-app-v1-7b4874cd75-wmcqc, Version: v1.0.0
Host: my-app-v1-7b4874cd75-tsh2s, Version: v1.0.0
Host: my-app-v1-7b4874cd75-ml58j, Version: v1.0.0
Host: my-app-v1-7b4874cd75-spsdv, Version: v1.0.0
Host: my-app-v2-f885c8d45-mc2fx, Version: v2.0.0
......      

正常情況下可以看到大部分都是傳回的版本1 的應用資訊,偶爾會出現版本2 的應用資訊,這就證明我們的金絲雀釋出成功了,待确認了版本2 的這個應用沒有任何問題後,可以将版本2 應用擴容到10 個副本:

$ kubectl scale --replicas=10 deploy my-app-v2
deployment.extensions "my-app-v2" scaled      

其實這個時候通路應用的話新版本和舊版本的流量配置設定是1:1了,确認了版本2 正常後,就可以删除版本1 的應用了:

$ kubectl delete deploy my-app-v1
deployment.extensions "my-app-v1" deleted      

最終留下的是 10 個新版本的 Pod 了,到這裡我們的整個金絲雀釋出就完成了。

同樣,最後,執行下面的指令删除上面的資源對象:

$ kubectl delete all -l app=my-app      

結論:

  • 部分使用者擷取新版本
  • 友善錯誤和性能監控
  • 快速復原
  • 釋出較慢
  • 流量精準控制很浪費(99%A / 1%B = 99 Pod A,1 Pod B)
# 如果你對新功能的釋出沒有信心,建議使用金絲雀釋出的政策。      

0x05 A/B測試(A/B testing) - 最适合部分使用者的功能測試

A/B 測試實際上是一種基于統計資訊而非部署政策來制定業務決策的技術,與業務結合非常緊密。但是它們也是相關的,也可以使用金絲雀釋出來實作。

除了基于權重在版本之間進行流量控制之外,A/B 測試還可以基于一些其他參數(比如 Cookie、User Agent、地區等等)來精确定位給定的使用者群,該技術廣泛用于測試一些功能特性的效果,然後按照效果來進行确定。

# 我們經常可以在今日頭條的用戶端中就會發現有大量的 A/B 測試,同一個地區的使用者看到的用戶端有很大不同。      

要使用這些細粒度的控制,仍然還是建議使用 Istio,可以根據權重或 HTTP 頭等來動态請求路由控制流量轉發。

K8S學習筆記之Kubernetes 部署政策詳解

下面是使用 Istio 進行規則設定的示例,因為 Istio 還不太穩定,以下示例規則将來可能會更改:

route:
- tags:
  version: v1.0.0
  weight: 90
- tags:
  version: v2.0.0
  weight: 10      

關于在 Istio 中具體如何做 A/B 測試,我們這裡就不再詳細介紹了,我們在​

​istio-book​

​文檔中有相關的介紹。

K8S學習筆記之Kubernetes 部署政策詳解

結論:

  • 幾個版本并行運作
  • 完全控制流量配置設定
  • 特定的一個通路錯誤難以排查,需要分布式跟蹤
  • Kubernetes 沒有直接的支援,需要其他額外的工具

0x06 總結

 釋出應用有許多種方法,當釋出到開發/測試環境的時候,​

​重建​

​​或者​

​滾動更新​

​​通常是一個不錯的選擇。在生産環境,​

​滾動更新​

​​或者​

​藍綠釋出​

​​比較合适,但是新版本的提前測試是非常有必要的。如果你對新版本的應用不是很有信心的話,那應該使用​

​金絲雀​

​​釋出,将使用者的影響降到最低。最後,如果你的公司需要在特定的使用者群體中進行新功能的測試,例如,移動端使用者請求路由到版本 A,桌面端使用者請求路由到版本 B,那麼你就看使用​

​A/B 測試​

​,通過使用 Kubernetes 服務網關的配置,可以根據某些請求參數來确定使用者應路由的服務。

​​參考​​