你知道什么叫金絲雀分布嗎?你知道如何在Service Mesh微服務架構中實現嗎?

什么是金絲雀發布

既然要聊具體的實現,那么在開始之前,先科普下什么是“金絲雀發布”。金絲雀發布也叫“灰度發布”,具體來說就是在發布線上版本時,先將少量的生產流量打到服務的新版本,以驗證新版本的準確性和可靠性,待發布的新版本得到線上流量的全面驗證后,在逐步將所有流量放入新版本,以實現生產服務版本的穩定更新。

為什么叫金絲雀發布呢,是因為金絲雀對礦場中的毒氣比較敏感,所以在礦場開工前工人們會放一只金絲雀進去,以驗證礦場是否存在毒氣,這便是金絲雀發布名稱的由來。

在不同技術棧場景中,金絲雀發布的實現方式也不盡相同:有通過nginx實現的、也有借助A/B測試實現的。而隨著以Kubernetes為代表的云原生基礎設施的普及,金絲雀發布作為一項基本的服務發布功能,其實現方式也有了一些新的趨勢——那就是逐步與云原生基礎設施融為一體,成為基礎設施服務的一部分。

Kubernetes中的金絲雀(灰度)發布

接下來,先看看在Kubernetes中是如何實現版本更新的。以下內容假設你已經有了一套可用的Kubernetes環境,如果沒有可以查看文末推薦閱讀的文章鏈接,參考相關分享自行部署。

1.滾動更新

在介紹Kubernetes中的金絲雀(灰度)發布之前,先來了解下Kubernetes中最重要的應用部署方式——“滾動升級”

所謂“滾動升級”:是指當更新了Kubernetes中Deployment編排資源的Pod模版(例如更新鏡像版本號)之后,Deployment就需要遵循一種叫做“滾動更新(rolling update)”的方式,來升級現有的容器,從而實現應用對外服務的“不中斷更新部署”。Kubernetes實現“滾動升級”的示意圖如下:

如上圖所示,滾動升級的過程為:

1)當容器開始升級時,集群中會先啟動一個新版本的Pod,并終止一個舊版本的Pod。

2)如果此時,新版本的Pod有問題啟動不了,那么“滾動升級”就會停止,并允許開發和運維人員介入。而在這個過程中,由于應用本身還有兩個舊版本的Pod在線,所以服務并不會受到太大的影響。

3)而如果新版本的Pod啟動成功,且服務訪問正常,則繼續滾動升級,直至按照Deployment編排器設置的副本數量,完成后續舊版本Pod的升級。

在Kubernetes中Deployment還可以通過相應地“滾動升級”策略,來控制Pod的滾動升級行為,以進一步保證服務的連續性。例如:“在任何時間窗口內,只有指定比例的Pod處于離線狀態;在任何時間窗口內,只有指定比例的新Pod被創建出來"。可以通過相應地控制參數進行設置,如下:

...
spec:
  selector:
    matchLabels:
      app: micro-api
  replicas: 3
  #設置滾動升級策略
  #Kubernetes在等待設置的時間后才開始進行升級,例如5秒
  minReadySeconds: 5
  strategy:
    type: RollingUpdate
    rollingUpdate:
      #升級過程中最多可以比原先設置多出的Pod數量
      maxSurge: 1
      #升級過程中Deployment控制器最多可以刪除多少個舊Pod,主要用于提供緩沖時間
      maxUnavailable: 1
...

在上面RollingUpdate Strategy(滾動升級策略)的配置中:

  • maxSurge:指定的是,除了設定的Pod副本數量之外,在一次“滾動”中,Deployment控制器還可以創建多少個新的Pod。

  • maxUnavailable:指的是,在一次“滾動”中,Deployment控制器可以刪除多少個舊Pod。

通過這種精確的“滾動升級”策略,可以使得Kubernetes服務版本發布的過程更加平滑。此外,這兩個配置還可以通過百分比的方式來表示,比如“maxUnavailable=50%”,指的是Deployment控制器最多可以一次刪除“50%*設定Pod副本數”個Pod。

接下來具體演示下在Kubernetes中進行服務滾動升級的詳細過程。

使用的示例代碼說明:

項目以Spring Boot編寫的Java服務為主,在體驗上更接近真實的項目開發場景。項目的結構如下:

該項目所在的GitHub地址為:

https://github.com/manongwudi/istio-micro-service-demo

“滾動升級”演示:

這里先借助示例項目中的“micro-api”服務來演示其在Kubernetes中進行“滾動升級”的過程,步驟如下:

(1)首先準備“micro-api”服務的k8s發布文件(如:micro-api.yaml)。代碼如下:

apiVersion: v1
kind: Service
metadata:
  name: micro-api
spec:
  type: ClusterIP
  ports:
    - name: http
      port: 19090
      targetPort: 9090
  selector:
    app: micro-api

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: micro-api
spec:
  selector:
    matchLabels:
      app: micro-api
  replicas: 3
  #設置滾動升級策略
  #Kubernetes在等待設置的時間后才開始進行升級,例如5秒
  minReadySeconds: 5
  strategy:
    type: RollingUpdate
    rollingUpdate:
      #升級過程中最多可以比原先設置多出的Pod數量
      maxSurge: 1
      #升級過程中Deployment控制器最多可以刪除多少個舊Pod
      maxUnavailable: 1
  template:
    metadata:
      labels:
        app: micro-api
    spec:
      #設置的阿里云私有鏡像倉庫登陸信息的secret(對應2.1.2的設置)
      imagePullSecrets:
        - name: regcred
      containers:
        - name: micro-api
          image: registry.cn-hangzhou.aliyuncs.com/wudimanong/micro-api:1.0-SNAPSHOT
          imagePullPolicy: Always
          tty: true
          ports:
            - name: http
              protocol: TCP
              containerPort: 19090

上述部署文件設置了“micro-api”服務的Pod副本個數為“3”,并且設置了相應地滾動升級策略。

(2)接下來執行k8s部署命令如下:

$ kubectl apply -f micro-api.yaml 

成功后,查看Deployment創建后的狀態信息,命令效果如下:

$ kubectl get deployments
NAME          READY   UP-TO-DATE   AVAILABLE   AGE
micro-api     3/3     3            3           190d

從上述命令的返回結果中,可以看到三個狀態字段,它們的含義如下所示:

  • READY:表示用戶期望的Pod副本個數,以及當前處于Running狀態的Pod個數。

  • UP-TO-DATE:當前處于最新版本的Pod個數。所謂最新版本,指的是Pod的Spec部分與Deployment中Pod模版里定義的完全一致。

  • AVAILABLE:當前已經可用的Pod的個數——既是Running狀態,又是最新版本,并且已經處于Ready(監控檢查正確)狀態的Pod個數。

(3)模擬服務版本升級,觸發滾動升級。

接下來重新構建“micro-api”服務的版本,并將其上傳至私有鏡像倉庫。之后,通過命令修改“micro-api”的Deployment所使用的鏡像,并觸發滾動升級。

修改Deployment所使用的鏡像的命令如下:

$ kubectl set image deployment/micro-api micro-api=registry.cn-hangzhou.aliyuncs.com/wudimanong/micro-api:1.1-SNAPSHOT
deployment.apps/micro-api image updated

這里使用了“kubectl set image”指令,主要是為了方便操作,也可以直接在k8s部署文件中進行鏡像版本的修改。

修改完Deployment的鏡像版本后,Kubernetes會立即觸發“滾動升級”的過程。可以通過“kubectl rollout status”指令來查看Deployment資源的狀態變化。具體如下:

$ kubectl rollout status deployment/micro-api

Waiting for deployment "micro-api" rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for deployment "micro-api" rollout to finish: 2 out of 3 new replicas have been updated...

Waiting for deployment "micro-api" rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for deployment "micro-api" rollout to finish: 2 of 3 updated replicas are available...
Waiting for deployment "micro-api" rollout to finish: 2 of 3 updated replicas are available...
deployment "micro-api" successfully rolled out

這時,也可以通過查看Deployment的Events,看到這個“滾動升級”的過程。具體如下:

$ kubectl describe deployment micro-api
...
OldReplicaSets:  <none>
NewReplicaSet:   micro-api-d745d8649 (3/3 replicas created)
Events:
  Type    Reason             Age   From                   Message
  ----    ------             ----  ----                   -------
  Normal  ScalingReplicaSet  12m   deployment-controller  Scaled up replica set micro-api-677dd4d5b6 to 1
  Normal  ScalingReplicaSet  12m   deployment-controller  Scaled down replica set micro-api-57c7cb5b74 to 2
  Normal  ScalingReplicaSet  12m   deployment-controller  Scaled up replica set micro-api-677dd4d5b6 to 2
  Normal  ScalingReplicaSet  5m1s  deployment-controller  Scaled down replica set micro-api-677dd4d5b6 to 0
  Normal  ScalingReplicaSet  5m    deployment-controller  Scaled up replica set micro-api-d745d8649 to 2
  Normal  ScalingReplicaSet  56s   deployment-controller  Scaled down replica set micro-api-57c7cb5b74 to 0
  Normal  ScalingReplicaSet  56s   deployment-controller  Scaled up replica set micro-api-d745d8649 to 3

可以看到,當你修改了Deployment里的Pod定義后,"Deployment Controller"會使用這個修改后的Pod模版,創建一個新的ReplicaSet,這個新的ReplicaSet的初始Pod副本數是:0。

然后在Age=12 m的位置,開始將這個新的ReplicaSet所控制的Pod副本數從0個變成1個。

緊接著,在Age=12 m的位置,又將舊ReplicaSet所控制的Pod副本數減少1個,即“水平收縮”成兩個副本。

如此交替進行,新ReplicaSet所管理的Pod的副本數,從0個變成1個,再變成2個,最后變成3個;而舊ReplicaSet所管理的Pod的副本數則從3個變成2個,最后變成0個。

這樣,就完成了一組Pod的版本升級過程。而像這樣將一個Kubernetes集群中正在運行的多個Pod版本,交替逐一升級的過程,就是“滾動升級”。

2.金絲雀(灰度)發布

前面“1.”小標題中,比較詳細的演示了Kubernetes的“滾動升級”的方式,雖然通過滾動升級的方式可以方便、平滑的實現版本更新,但是這個過程,并沒有灰度功能。滾動升級的方式,雖然中間有緩沖交替的過程,但這種過程是自動的、迅速的,滾動升級過程結束就相當于直接進行了新版本的全量發布

而對于需要進行金絲雀(灰度)發布的場景,“滾動升級”的方式很顯然是不夠用的。那么,在Kubernetes中應該如何結合版本更新做到金絲雀(灰度)發布呢?

具體步驟如下:

(1)編寫實現新版本灰度發布的部署文件。

為了實現在Kubernetes中的金絲雀(灰度)發布過程的可觀測,我們重新定義下具體的k8s發布文件(如:micro-api-canary.yaml)的內容如下:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: micro-api
spec:
  selector:
    matchLabels:
      app: micro-api
  replicas: 3
  #設置滾動升級策略
  #Kubernetes在等待設置的時間后才開始進行升級,例如5秒
  minReadySeconds: 5
  strategy:
    type: RollingUpdate
    rollingUpdate:
      #升級過程中最多可以比原先設置多出的Pod數量
      maxSurge: 1
      #升級過程中Deployment控制器最多可以刪除多少個舊Pod,主要用于提供緩沖時間
      maxUnavailable: 1
  template:
    metadata:
      labels:
        app: micro-api
        #增加新的標簽(演示k8s的灰度發布)
        track: canary
    spec:
      #設置的阿里云私有鏡像倉庫登陸信息的secret(對應2.1.2的設置)
      imagePullSecrets:
        - name: regcred
      containers:
        - name: micro-api
          image: registry.cn-hangzhou.aliyuncs.com/wudimanong/micro-api:1.3-SNAPSHOT
          imagePullPolicy: Always
          tty: true
          ports:
            - name: http
              protocol: TCP
              containerPort: 19090

上述發布文件與“1.”小標題中演示滾動升級時,發布文件的內容一致,只是為了方便觀察灰度發布過程的實現,這里通過“track: canary”對新發布的Pod版本進行標記。

設置新版本的鏡像為:“micro-api:1.3-SNAPSHOT”。并且通過spec.selector.matchLabels.app:micro-api與歷史版本Pod所對應的Service(micro-api.yaml文件中定義的Service)資源定義匹配。**

(2)執行"滾動升級"發布命令,實現“灰度發布”效果。

$ kubectl apply -f micro-api-canary.yaml && kubectl rollout pause deployment/micro-api

上面通過"kubectl rollout pause"命令實現對Deployment的金絲雀(灰度發布)。執行發布命令之后的運行效果如下:

$ kubectl get pods --show-labels -o wide
NAME                         READY   STATUS    RESTARTS   AGE     IP          NODE         NOMINATED NODE   READINESS GATES   LABELS
micro-api-57c7cb5b74-mq7m9   1/1     Running   0          6m20s   10.32.0.3   kubernetes   <none>           <none>            app=micro-api,pod-template-hash=57c7cb5b74
micro-api-57c7cb5b74-ptptj   1/1     Running   0          6m20s   10.32.0.4   kubernetes   <none>           <none>            app=micro-api,pod-template-hash=57c7cb5b74
micro-api-7dbb6c5d66-4rbdc   1/1     Running   0          5m33s   10.32.0.6   kubernetes   <none>           <none>            app=micro-api,pod-template-hash=7dbb6c5d66,track=canary
micro-api-7dbb6c5d66-cfk9l   1/1     Running   0          5m33s   10.32.0.5   kubernetes   <none>           <none>            app=micro-api,pod-template-hash=7dbb6c5d66,track=canary

查看Deployment的滾動升級情況,命令如下:

$ kubectl get deployments
NAME            READY   UP-TO-DATE   AVAILABLE   AGE
micro-api       4/3     2            4           194d

可以看到此時“micro-api” ready的數量為4,其中兩個舊版本Pod,兩個新版本Pod。

(3)接下來進行流量測試。

查詢兩組Pod版本所對應的Service資源的IP,命令如下:

# kubectl get svc micro-api
NAME        TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)     AGE
micro-api   ClusterIP   10.110.169.161   <none>        19090/TCP   194d

接下來,模擬對服務的接口進行批量訪問,命令如下:

$ for i in {1..10}; do curl 10.110.169.161:19090/test/test; done

{"code":0,"data":"V3|無依賴測試接口返回->OK!","message":"成功"}
{"code":0,"data":"V3|無依賴測試接口返回->OK!","message":"成功"}
{"code":0,"data":"無依賴測試接口返回->OK!","message":"成功"}
{"code":0,"data":"無依賴測試接口返回->OK!","message":"成功"}
{"code":0,"data":"V3|無依賴測試接口返回->OK!","message":"成功"}
{"code":0,"data":"V3|無依賴測試接口返回->OK!","message":"成功"}
{"code":0,"data":"無依賴測試接口返回->OK!","message":"成功"}
{"code":0,"data":"無依賴測試接口返回->OK!","message":"成功"}
{"code":0,"data":"V3|無依賴測試接口返回->OK!","message":"成功"}
{"code":0,"data":"V3|無依賴測試接口返回->OK!","message":"成功"}

可以看到,此時流量會隨機的流向舊版本和新版本(日志標記為V3)的服務。

(4)將服務版本升級為新版本。

如果新版本的服務經過線上流量測試驗證沒有問題,則可以通過"rollout resume"命令將整體服務的版本升級為新版本。命令如下:

$ kubectl rollout resume deployment micro-api
deployment.apps/micro-api resumed

升級后的效果如下:

$ kubectl get pods --show-labels -o wide
NAME                         READY   STATUS    RESTARTS   AGE   IP          NODE         NOMINATED NODE   READINESS GATES   LABELS
micro-api-7dbb6c5d66-4rbdc   1/1     Running   0          18m   10.32.0.6   kubernetes   <none>           <none>            app=micro-api,pod-template-hash=7dbb6c5d66,track=canary
micro-api-7dbb6c5d66-bpjtg   1/1     Running   0          84s   10.32.0.3   kubernetes   <none>           <none>            app=micro-api,pod-template-hash=7dbb6c5d66,track=canary
micro-api-7dbb6c5d66-cfk9l   1/1     Running   0          18m   10.32.0.5   kubernetes   <none>           <none>            app=micro-api,pod-template-hash=7dbb6c5d66,track=canary

可以看到,此時目標服務已經通過“滾動升級”的方式完成了全量更新。而如果存在問題,則通過“kubectl rollout undo”命令進行回滾即可!

從上述過程可以看到,Kubernetes中的金絲雀(灰度發布)主要是通過操縱(如:pause)“滾動升級”的過程來實現的——通過發布一定數量的新版本Pod,并利用Service資源類型本身的負載均衡能力來實現流量在新/舊Pod之間的隨機交替。

這樣的方式雖然已經可以滿足一些簡單的場景,但是沒有辦法做到更精準的灰度流量控制。這時候就需要借助 Service Mesh 中的解決方案了,下面我們來看看在 Istio 中如何做到精準流量的金絲雀(灰度)發布。

Istio中的金絲雀(灰度)發布

Istio與Kubernetes實現金絲雀(灰度)發布的方式不一樣,Istio通過Envoy(SideCar)強大的路由規則管理能力,可以非常靈活地控制對應版本的流量占比,從而實現具備精準流量控制能力的金絲雀(灰度)發布功能。

Istio通過Envoy(SideCar)實現金絲雀(灰度)發布的流量路由示意圖如下(繼續以“micro-api”服務為例):

從上圖中可以大致看出,Istio具備強大的流量管理能力,而這種能力對于實現流量精準控制的金絲雀(灰度)發布功能來說,自然是水到渠成的。

具體來說,在Istio中是通過VirtualService(虛擬服務)這種特定的資源在服務網格中實現流量路由的。通過VirtualService可以方便地定義流量路由規則,并在客戶端試圖連接到服務時應用這些規則,并最終到達目標服務。

接下來,具體演示如何在Istio中通過VirtualService實現金絲雀(灰度)發布。步驟如下:

(1)首先發布一個v1版本的服務。

要在Istio中實現更精準的版本控制,需要在發布Pod資源時,通過明確的“版本標簽”進行指定。準備“micro-api”服務v1版本的k8s部署文件(micro-api-canary-istio-v1.yaml):

apiVersion: v1
kind: Service
metadata:
  name: micro-api
spec:
  type: ClusterIP
  ports:
    - name: http
      port: 19090
      targetPort: 9090
  selector:
    app: micro-api

---

apiVersion: apps/v1
kind: Deployment
meta data:
  name: micro-api-v1
spec:
  selector:
    matchLabels:
      app: micro-api
      #這里是關鍵,需要設置版本標簽,以便實現灰度發布
      version: v1
  replicas: 3
  #設置滾動升級策略
  #Kubernetes在等待設置的時間后才開始進行升級,例如5秒
  minReadySeconds: 5
  strategy:
    type: RollingUpdate
    rollingUpdate:
      #升級過程中最多可以比原先設置多出的Pod數量
      maxSurge: 1
      #升級過程中Deployment控制器最多可以刪除多少個舊Pod,主要用于提供緩沖時間
      maxUnavailable: 1
  template:
    metadata:
      labels:
        app: micro-api
        #設置版本標簽,便于灰度發布
        version: v1
    spec:
      #設置的阿里云私有鏡像倉庫登陸信息的secret
      imagePullSecrets:
        - name: regcred
      containers:
        - name: micro-api
          image: registry.cn-hangzhou.aliyuncs.com/wudimanong/micro-api:1.1-SNAPSHOT
          imagePullPolicy: Always
          tty: true
          ports:
            - name: http
              protocol: TCP
              containerPort: 19090

“spec.selector.matchLabels.version:v1”標簽用來標注服務的版本,該標簽是后續Istio的流量管理規則中,識別服務版本的主要依據。

準備好發布文件后,執行發布命令:

$ kubectl apply -f micro-api-canary-istio-v1.yaml

此時,一個低版本的服務就運行成功了!接下來我們模擬對其實施金絲雀(灰度)發布。

(2)發布一個v2版本的服務(升級的目標版本)。

與v1版本服務一樣,發布的v2版本的服務也需要明確版本標簽,其發布文件(micro-api-canary-istio-v2.yaml)的內容如下:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: micro-api-v2
spec:
  selector:
    matchLabels:
      app: micro-api
      #設置好版本標簽,便于灰度發布
      version: v2
  replicas: 3
  #設置滾動升級策略
  #Kubernetes在等待設置的時間后才開始進行升級,例如5秒
  minReadySeconds: 5
  strategy:
    type: RollingUpdate
    rollingUpdate:
      #升級過程中最多可以比原先設置多出的Pod數量
      maxSurge: 1
      #升級過程中Deployment控制器最多可以刪除多少個舊Pod,主要用于提供緩沖時間
      maxUnavailable: 1
  template:
    metadata:
      labels:
        app: micro-api
        #設置好版本標簽,便于灰度發布
        version: v2
    spec:
      #設置的阿里云私有鏡像倉庫登陸信息的secret
      imagePullSecrets:
        - name: regcred
      containers:
        - name: micro-api
          image: registry.cn-hangzhou.aliyuncs.com/wudimanong/micro-api:1.3-SNAPSHOT
          imagePullPolicy: Always
          tty: true
          ports:
            - name: http
              protocol: TCP
              containerPort: 19090

執行發布命令:

$ kubectl apply -f micro-api-canary-istio-v2.yaml 
deployment.apps/micro-api-v2 created

此時,系統中就存在了兩組版本的Pod資源,具體如下:

# kubectl get pods
NAME                            READY   STATUS    RESTARTS   AGE
micro-api-v1-565d749dd4-7c66z   1/1     Running   2          13h
micro-api-v1-565d749dd4-7dqfb   1/1     Running   2          13h
micro-api-v1-565d749dd4-l62wc   1/1     Running   2          13h
micro-api-v2-6f98c598c9-5stlw   1/1     Running   0          82s
micro-api-v2-6f98c598c9-f2ntq   1/1     Running   0          82s
micro-api-v2-6f98c598c9-l8g4j   1/1     Running   0          82s

接下來將演示如何利用Istio強大的流量管理功能,來實現流量在這兩組版本Pod資源之間的精確控制!

(3)創建Istio網關資源。

在Istio中要實現流量的精確控制,需要將VirtualService綁定到具體的Ingressgateway(入口網關)資源。因此在創建VirtualService資源實現流量路由及控制前,需要創建一個Istio網關。部署文件(micro-gateway.yaml)的內容如下:

apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
  name: micro-gateway
spec:
  selector:
    istio: ingressgateway
  servers:
    - port:
        number: 80
        name: http
        protocol: HTTP
      hosts:
        - "*"

上述部署文件執行后將創建一個名稱為“micro-gateway”的Istio網關,并允許所有主機(hosts:"*"指定)通過該網關。

(4)創建Istio虛擬服務資源VirtualService。

前面提到過在Istio中主要是通過VirtualService(虛擬服務)來實現服務網格內的流量路由及控制。接下來我們看看VirtualService資源的具體創建方式,準備資源文件(如virtual-service-all.yaml),內容如下:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: micro-api-route
spec:
  #用于定義流量被發送到的目標主機(這里為部署在k8s中的micro-api服務)
  hosts:
    - micro-api.default.svc.cluster.local
  #將VirtualService綁定到Istio網關,通過網關來暴露路由目標
  gateways:
    - micro-gateway
  http:
    - route:
        #設置舊版本(V1)版本的流量占比為70%
        - destination:
            host: micro-api.default.svc.cluster.local
            subset: v1
          #通過權重值來設置流量占比
          weight: 70
        #設置新版本(V2)版本的流量占比為30%
        - destination:
            host: micro-api.default.svc.cluster.local
            subset: v2
          weight: 30

如上所示,VirtualService資源具備針對http的精準流量控制能力,可以將指定占比的流量路由到特定的“subset”指定的版本。而為了實現這一能力,VirtualService資源還需要與Istio網關綁定,通過Istio網關來暴露路由目標。

(5)創建Istio目標路由規則資源。

虛擬服務VirtualService在Istio中主要用于控制流量的行為,而定義流量行為的路由規則則需要通過“DestinationRule”路由規則資源來定義。創建路由規則文件(destination-rule-all.yaml),具體內容如下:

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: micro-api-destination
spec:
  #與Deployment資源對應的Service資源名稱關聯
  host: micro-api
  #流量策略設置:負載均衡策略、連接池大小、局部異常檢測等,在路由發生后作用于流量
  trafficPolicy:
    #限流策略
    connectionPool:
      tcp:
        maxConnections: 10
      http:
        http1MaxPendingRequests: 1
        maxRequestsPerConnection: 1
    #設置目的地的負債均衡算法
    loadBalancer:
      simple: ROUND_ROBIN
  #目的地指的是不同的子集(subset)或服務版本。通子集(subset),可以識別應用程序的不同版本,以實現流量在不同服務版本之間的切換
  subsets:
    - name: v1
      labels:
        version: v1
    - name: v2
      labels:
        version: v2

如上所示,通過subsets屬性,定義了VirtualService資源用于路由的具體版本標簽匹配信息。至此,針對兩個版本服務的灰度流量控制規則就設置好了,接下來測試具體的金絲雀(灰度)發布效果。

(6)測試Istio實現金絲雀(灰度)發布的流量控制效果。

在正式測試之前,可以通過命令查看下當前的部署資源情況:

#查看部署的Deployment資源
kubectl get deploy  | grep micro-api

micro-api-v1             3/3     3            3           21h
micro-api-v2             3/3     3            3           8h
#查看兩組版本Pod資源對應的K8s-Service的服務IP
kubectl get svc micro-api

NAME        TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)     AGE
micro-api   ClusterIP   10.110.169.161   <none>        19090/TCP   205d
#查看VirtualService資源定義
kubectl get vs

NAME              GATEWAYS          HOSTS                                   AGE
micro-api-route   [micro-gateway]   [micro-api.default.svc.cluster.local]   7h34m
#查看定義的路由規則資源
kubectl get dr

NAME                    HOST        AGE
micro-api-destination   micro-api   7h27m

通過上面的資源信息查看,這里我們已經可以查到Deployments對應的K8s-Service資源的IP,但如果通過K8s-Service資源來進行測試的話,會發現流量的控制并不精準,并不能達到我們設置的70%流量流向v1,30%的流量流向v2(因為這是隨機流量)

因此,要使用Istio的精準流量控制功能,還需要使用Istio的Ingressgateway。查看Istio的Ingressgateway資源IP的命令如下:

#查看ingress的IP
kubectl get svc -n istio-system | grep ingress

istio-ingressgateway   LoadBalancer   10.98.178.61     <pending>     15021:31310/TCP,80:32113/TCP,443:31647/TCP,31400:30745/TCP,15443:30884/TCP   7h54m

接下來,通過Ingress的IP來訪問“micro-api”服務,命令及效果如下:

# for i in {1..10}; do curl -H "Host:micro-api.default.svc.cluster.local" 10.98.178.61:80/test/test; done

{"code":0,"data":"V3|無依賴測試接口返回->OK!","message":"成功"}
{"code":0,"data":"無依賴測試接口返回->OK!","message":"成功"}
{"code":0,"data":"無依賴測試接口返回->OK!","message":"成功"}
{"code":0,"data":"無依賴測試接口返回->OK!","message":"成功"}
{"code":0,"data":"無依賴測試接口返回->OK!","message":"成功"}
{"code":0,"data":"無依賴測試接口返回->OK!","message":"成功"}
{"code":0,"data":"無依賴測試接口返回->OK!","message":"成功"}
{"code":0,"data":"無依賴測試接口返回->OK!","message":"成功"}
{"code":0,"data":"V3|無依賴測試接口返回->OK!","message":"成功"}
{"code":0,"data":"V3|無依賴測試接口返回->OK!","message":"成功"}

如上所示,流量按照設定的比例(v1:70%;v2:30%)進行了分流。

(7)測試將流量全部切向新版本。

為了更明顯地驗證Istio的流量控制效果,接下來,我們通過變更VirtualService資源的流量設置占比,將流量全部切到新版本。變更后的VirtualService資源的配置文件內容如下:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: micro-api-route
spec:
  #用于定義流量被發送到的目標主機(這里為部署在k8s中的micro-api服務)
  hosts:
    - micro-api.default.svc.cluster.local
  #將VirtualService綁定到Istio網關,通過網關來暴露路由目標
  gateways:
    - micro-gateway
  http:
    - route:
        #設置舊版本(V1)版本的流量占比為70%
        - destination:
            host: micro-api.default.svc.cluster.local
            subset: v1
          #通過權重值來設置流量占比
          weight: 0
        #設置新版本(V2)版本的流量占比為30%
        - destination:
            host: micro-api.default.svc.cluster.local
            subset: v2
          weight: 100

繼續通過Istio網關訪問目標服務,命令如下:

# for i in {1..10}; do curl -H "Host:micro-api.default.svc.cluster.local" 10.98.178.61:80/test/test; done

{"code":0,"data":"V3|無依賴測試接口返回->OK!","message":"成功"}
{"code":0,"data":"V3|無依賴測試接口返回->OK!","message":"成功"}
{"code":0,"data":"V3|無依賴測試接口返回->OK!","message":"成功"}
{"code":0,"data":"V3|無依賴測試接口返回->OK!","message":"成功"}
{"code":0,"data":"V3|無依賴測試接口返回->OK!","message":"成功"}
{"code":0,"data":"V3|無依賴測試接口返回->OK!","message":"成功"}
{"code":0,"data":"V3|無依賴測試接口返回->OK!","message":"成功"}
{"code":0,"data":"V3|無依賴測試接口返回->OK!","message":"成功"}
{"code":0,"data":"V3|無依賴測試接口返回->OK!","message":"成功"}
{"code":0,"data":"V3|無依賴測試接口返回->OK!","message":"成功"}

可以觀察到,此時流量已經全部切換到了新版本服務!

后記

在微服務時代,不同的服務之間相互聯系,關系錯綜復雜,部署升級一個服務,可能造成整個系統的癱瘓,因此,需要選擇合適的部署方式,從而將風險降到最低。金絲雀(灰度)發布只是多種部署方式的一種,還有藍綠部署、滾動部署(如K8s的滾動升級)等,可以根據不同的業務場景選擇不同的發布形式。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,412評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,514評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,373評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,975評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,743評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,199評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,262評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,414評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,951評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,780評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,983評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,527評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,218評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,649評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,889評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,673評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,967評論 2 374

推薦閱讀更多精彩內容

  • 16宿命:用概率思維提高你的勝算 以前的我是風險厭惡者,不喜歡去冒險,但是人生放棄了冒險,也就放棄了無數的可能。 ...
    yichen大刀閱讀 6,076評論 0 4
  • 公元:2019年11月28日19時42分農歷:二零一九年 十一月 初三日 戌時干支:己亥乙亥己巳甲戌當月節氣:立冬...
    石放閱讀 6,901評論 0 2
  • 今天上午陪老媽看病,下午健身房跑步,晚上想想今天還沒有斷舍離,馬上做,衣架和旁邊的的布衣架,一看亂亂,又想想自己是...
    影子3623253閱讀 2,922評論 3 8