Istio流量管理實踐之(3): 基於Istio實現流量對比分析

流量鏡像

流量鏡像,也稱爲影子流量,流量鏡像提供一種儘量低的風險爲生產帶來變化的強大功能。鏡像會將實時流量的副本發送到鏡像服務。鏡像流量發生在主服務的關鍵請求路徑以外。nginx

在非生產或者測試環境中,嘗試訪問一個服務全部可能的測試用例組合是個很是不現實的任務。 在某些狀況下,編寫這些用例的全部工做也可能與實際生產所需的用例不匹配。在理想狀況下,可使用實時的生產用例和流量來幫助完善在測試環境中錯過的功能區域。git

一旦咱們可以可靠地鏡像流量,就能夠開始作一些有價值的事情,例如經過請求流量對比工具Diffy,能夠將引入測試集羣的流量與生產集羣中的預期行爲進行比較。例如,咱們可能想比較請求結果與預期結果間的誤差,或是API協議中的數據損壞狀況,以便更好地兼容。
除此以外,須要注意:github

  • 當流量鏡像到不一樣的服務時,會發生在請求的關鍵路徑以外;
  • 忽略對任何鏡像流量的響應; 流量被視爲「即發即忘」;

流量對比

此處,插入一個代理就能夠負責此類流量的協調,並對其進行有趣的比較。Diffy就是一款這樣的代理工具。Diffy啓動一個代理服務(例如監聽端口8880),再根據用戶設置的primary、secondary兩個舊服務地址(primary和secondary代碼徹底相同,目的是爲了減小噪音干擾)、candidate新服務地址。docker

它還可以檢測結果中的噪音,並經過先調用兩個實時服務的實例來忽略它們(例如時間戳,單調遞增計數器等提示),總結來講就是檢測,而後在測試服務中忽略掉這部分。api

圖片.png

Diffy還提供了一個不錯的頁面能夠用來查看調用結果、對比狀況、和基於某些特徵的過濾。它還有一個很好的管理控制檯,能夠查看有關調用比較結果的功能指標(metrics)和統計數據(statistics)。微信

建立用於Istio流量鏡像的服務

在此任務中,將首先強制全部流量到 v1 版本的服務。而後,將使用規則將一部分流量鏡像到 v2版本。併發

首先部署兩個版本的示例服務。app

版本1的部署使用了Docker鏡像httpbin,提供常見的http請求訪問:負載均衡

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: mirrorservice-sample-v1
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: mirrorservice-sample
        version: v1
    spec:
      containers:
      - image: docker.io/kennethreitz/httpbin
        imagePullPolicy: IfNotPresent
        name: mirrorservice-sample
        command: ["gunicorn", "--access-logfile", "-", "-b", "0.0.0.0:44134", "httpbin:app"]
        ports:
        - containerPort: 44134

版本2的部署使用了自定義的Docker鏡像,對應的Dockerfile以下:curl

FROM nginx:latest
COPY default.conf /etc/nginx/conf.d/
EXPOSE 80

所需的nginx 配置文件:

server {
    listen       44134;
    server_name  localhost;

    location / {
        proxy_pass http://httpbin-diffy.diffy:8880/;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";   
    }
}

版本2的部署做爲Istio的流量鏡像目標,在接收到流量以後會轉發到Diffy的代理中。當前沒有直接將Diffy代理做爲Isito流量鏡像目標,緣由是Diffy代理與Envoy代理目前自己有衝突,沒法正常流量轉發,所以須要此部署中轉一下。

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: mirrorservice-sample-v2
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: mirrorservice-sample
        version: v2
    spec:
      containers:
      - name: mirrorservice-sample
        image: registry.cn-beijing.aliyuncs.com/wangxining/mirrorservice:0.1
        imagePullPolicy: Always
        ports:
        - containerPort: 44134

對應的Kubernetes service:

apiVersion: v1
kind: Service
metadata:
  name: mirrorservice-sample
spec:
  type: ClusterIP
  ports:
  - name: http
    port: 44134
  selector:
    app: mirrorservice-sample

建立流量鏡像的Istio策略

默認狀況下,Kubernetes 在服務的兩個版本之間進行負載均衡。建立以下流量鏡像規則將 100% 的流量發送到 v1, 同時指定流量鏡像到v2。當流量被鏡像時,請求將經過其主機/受權報頭髮送到鏡像服務附上 -shadow 。

apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: mirrorservice-sample
spec:
  host: mirrorservice-sample
  subsets:
  - name: v1
    labels:
      version: v1
  - name: v2
    labels:
      version: v2
---      
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: mirrorservice-sample
spec:
  hosts:
    - mirrorservice-sample
  http:
  - route:
    - destination:
        host: mirrorservice-sample
        subset: v1
      weight: 100
    #- destination:
    #    host: mirrorservice-sample
    #    subset: v2
    #  weight: 0
    mirror:
      host: mirrorservice-sample
      subset: v2

搭建Diffy用於請求流量對比

Diffy能夠做爲代理,截取請求併發送至全部運行的服務實例,經過對比響應結果來發現每次迭代代碼中可能存在的問題。其中,Diffy上運行了三類代碼實例:

  • 線上穩定版本:一個運行線上穩定版本代碼的節點
  • 線上穩定版本備份:一樣運行了線上的穩定版本,用於消除噪音
  • 測試版本:待上線的測試版本,用於和線上環境代碼進行對比
    圖片.png

在實際Diffy測試中,會發現大部分的接口都會有必定差別,緣由是這些響應中存在了噪音,噪音可能包括:

  • server響應中生成的時間戳
  • 隨機生成的數字
  • 系統服務間的有條件競爭

Diffy可以經過必定的方式,清除這類噪音,保證分析結果不被影響。

建立Diffy及示例服務

經過如下YAML建立Diffy服務:

apiVersion: v1
kind: Service
metadata:
  name: httpbin-diffy
  labels:
    app: httpbin-diffy
spec:
  ports:
  - name: http-proxy
    port: 8880
  - name: http-admin
    port: 8881
  - name: http-console
    port: 8888    
  selector:
    app: httpbin-diffy
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  labels:
    app: httpbin-diffy
    version: v2
  name: httpbin-diffy-v2
spec:
  replicas: 1
  selector:
    matchLabels:
      app: httpbin-diffy
      version: v2
  template:
    metadata:
      labels:
        app: httpbin-diffy
        version: v2
    spec:
      containers:
      - image: lordofthejars/diffy:1.0
        imagePullPolicy: IfNotPresent
        livenessProbe:
          exec:
            command:
            - curl
            - localhost:8888
          initialDelaySeconds: 10
          periodSeconds: 60
          timeoutSeconds: 1
        name: httpbin-diffy
        args: ["-candidate=httpbin-candidate:8080", "-master.primary=httpbin-master:8080", "-master.secondary=httpbin-master:8080", "-service.protocol=http", "-serviceName=httpbin", "-proxy.port=:8880", "-admin.port=:8881", "-http.port=:8888", "-rootUrl='localhost:8888'"]
        ports:
        - containerPort: 8888
          name: http-console
          protocol: TCP
        - containerPort: 8880
          name: http-proxy
          protocol: TCP
        - containerPort: 8881
          name: http-admin
          protocol: TCP
        readinessProbe:
          exec:
            command:
            - curl
            - localhost:8888
          initialDelaySeconds: 10
          periodSeconds: 60
          timeoutSeconds: 1
        securityContext:
          privileged: false

經過如下YAML建立示例所用的primary、secondary(當前示例中與primary相同)與candidate服務:

apiVersion: v1
kind: Service
metadata:
  name: httpbin-master
  labels:
    app: httpbin-master
spec:
  ports:
  - name: http
    port: 8080
  selector:
    app: httpbin
    version: v1
---
apiVersion: v1
kind: Service
metadata:
  name: httpbin-candidate
  labels:
    app: httpbin-candidate
spec:
  ports:
  - name: http
    port: 8080
  selector:
    app: httpbin
    version: v2
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: httpbin-v1
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: httpbin
        version: v1
    spec:
      containers:
      - image: docker.io/kennethreitz/httpbin
        imagePullPolicy: IfNotPresent
        name: httpbin
        command: ["gunicorn", "--access-logfile", "-", "-b", "0.0.0.0:8080", "httpbin:app"]
        ports:
        - containerPort: 8080
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: httpbin-v2
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: httpbin
        version: v2
    spec:
      containers:
      - image: docker.io/kennethreitz/httpbin
        imagePullPolicy: IfNotPresent
        name: httpbin
        command: ["gunicorn", "--access-logfile", "-", "-b", "0.0.0.0:8080", "httpbin:app"]
        ports:
        - containerPort: 8080

發送流量進行鏡像驗證

啓動 sleep 服務,這樣就可使用 curl 來提供負載:

cat <<EOF | istioctl kube-inject -f - | kubectl create -f -
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: sleep
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: sleep
    spec:
      containers:
      - name: sleep
        image: tutum/curl
        command: ["/bin/sleep","infinity"]
        imagePullPolicy: IfNotPresent
EOF

進入到SLEEP_POD, 具體POD名稱根據實際賦值。

kubectl exec -it $SLEEP_POD -c sleep sh

發送流量:

curl -v http://mirrorservice-sample:44134/headers

能夠查看 v1的訪問日誌記錄,以下所示建立的請求100%指向了v1。 
圖片.png

與此同時,查看Diffy的Web界面,能夠看到建立的請求也被鏡像到Diffy Proxy:
圖片.png

圖片.png

Diffy可以經過必定的方式,清除這類噪音,保證分析結果不被影響。
圖片.png

結論

流量鏡像提供一種儘量低的風險爲生產帶來變化的強大功能。鏡像會將實時流量的副本發送到鏡像服務,鏡像流量發生在主服務的關鍵請求路徑以外。一旦咱們可以可靠地鏡像流量,就能夠開始作一些有價值的事情,例如經過請求流量對比工具Diffy,能夠將引入測試集羣的流量與生產集羣中的預期行爲進行比較。

支持流量鏡像只是 Istio 的衆多功能之一,它將使基於大型微服務的應用程序的生產部署與管理變得更加簡單。歡迎你們使用阿里雲上的容器服務,快速搭建微服務的開放治理平臺Istio,比較簡單地集成到本身項目的微服務開發中。

 

原文連接 更多技術乾貨 請關注阿里云云棲社區微信號 :yunqiinsight

相關文章
相關標籤/搜索