Istio流量管理實踐之(3): 基於Istio實現流量對比分析
流量映象
流量映象,也稱為影子流量,流量映象提供一種儘可能低的風險為生產帶來變化的強大功能。映象會將實時流量的副本傳送到映象服務。映象流量發生在主服務的關鍵請求路徑之外。
在非生產或者測試環境中,嘗試訪問一個服務所有可能的測試用例組合是個非常不現實的任務。 在某些情況下,編寫這些用例的所有工作也可能與實際生產所需的用例不匹配。在理想情況下,可以使用實時的生產用例和流量來幫助完善在測試環境中錯過的功能區域。
一旦我們能夠可靠地映象流量,就可以開始做一些有價值的事情,例如通過請求流量對比工具Diffy,可以將引入測試叢集的流量與生產叢集中的預期行為進行比較。例如,我們可能想比較請求結果與預期結果間的偏差,或是API協議中的資料損壞情況,以便更好地相容。
除此之外,需要注意:
- 當流量映象到不同的服務時,會發生在請求的關鍵路徑之外;
- 忽略對任何映象流量的響應; 流量被視為“即發即忘”;
流量對比
此處,插入一個代理就可以負責此類流量的協調,並對其進行有趣的比較。 ofollow,noindex" target="_blank">Diffy 就是一款這樣的代理工具。Diffy啟動一個代理服務(例如監聽埠8880),再根據使用者設定的primary、secondary兩個舊服務地址(primary和secondary程式碼完全相同,目的是為了減少噪音干擾)、candidate新服務地址。
它還能夠檢測結果中的噪音,並通過先呼叫兩個實時服務的例項來忽略它們(例如時間戳,單調遞增計數器等提示),總結來說就是檢測,然後在測試服務中忽略掉這部分。
Diffy還提供了一個不錯的頁面可以用來檢視呼叫結果、對比情況、和基於某些特徵的過濾。它還有一個很好的管理控制檯,可以檢視有關呼叫比較結果的功能指標(metrics)和統計資料(statistics)。
建立用於Istio流量映象的服務
在此任務中,將首先強制所有流量到 v1 版本的服務。然後,將使用規則將一部分流量映象到 v2版本。
首先部署兩個版本的示例服務。
版本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如下:
FROM nginx:latest COPY default.conf /etc/nginx/conf.d/ EXPOSE 80
所需的nginx 配置檔案:
server { listen44134; server_namelocalhost; 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上運行了三類程式碼例項:
- 線上穩定版本:一個執行線上穩定版本程式碼的節點
- 線上穩定版本備份:同樣運行了線上的穩定版本,用於消除噪音
- 測試版本:待上線的測試版本,用於和線上環境程式碼進行對比
在實際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。

與此同時,檢視Diffy的Web介面,可以看到建立的請求也被映象到Diffy Proxy:

Diffy能夠通過一定的方式,清除這類噪音,保證分析結果不被影響。

結論
流量映象提供一種儘可能低的風險為生產帶來變化的強大功能。映象會將實時流量的副本傳送到映象服務,映象流量發生在主服務的關鍵請求路徑之外。一旦我們能夠可靠地映象流量,就可以開始做一些有價值的事情,例如通過請求流量對比工具Diffy,可以將引入測試叢集的流量與生產叢集中的預期行為進行比較。
支援流量映象只是 Istio 的眾多功能之一,它將使基於大型微服務的應用程式的生產部署與管理變得更加簡單。歡迎大家使用阿里雲上的容器服務,快速搭建微服務的開放治理平臺Istio,比較簡單地整合到自己專案的微服務開發中。