使用服務網格介面和Linkerd進行故障注入
- 2019 年 12 月 4 日
- 筆記
作者:Alex Leong
應用程式故障注入(failure injection)是混沌工程(chaos engineering)的形式之一,我們在其中人為地增加微服務應用程式中某些服務的錯誤率,以查看這對整個系統有什麼影響。傳統上,你需要在服務程式碼中添加某種類型的故障注入庫,以便進行應用程式故障注入。值得慶幸的是,服務網格為我們提供了一種注入應用程式故障的方法,而無需修改或重新構建我們的服務。
結構良好的微服務應用程式的一個特點,是它能夠優雅地容忍單個服務的失敗。當這些故障以服務崩潰的形式出現時,Kubernetes通過創建新的pod來替換已經崩潰的pod,在治癒這些故障方面做得非常好。然而,失敗也可能更加微妙,導致服務返回更高的錯誤率。這些類型的故障不能由Kubernetes自動修復,但仍然會導致功能損失。
使用流量分割SMI API注入故障
通過使用服務網格介面(Service Mesh Interface)的流量分割API(Traffic Split API),我們可以很容易地注入應用程式故障。這允許我們以一種與實現無關、跨服務網格工作的方式進行故障注入。
為此,我們首先部署一個只返回錯誤的新服務。這可以像配置為返回HTTP 500響應的NGINX服務一樣簡單,也可以是更複雜的服務,返回專門設計的錯誤,以執行你希望測試的某些條件。然後,我們創建一個流量分割資源,該資源指示服務網格將目標服務流量的百分比發送到錯誤服務。例如,通過將服務流量的10%發送給錯誤服務,我們向該服務注入了一個人工的10%故障率。
讓我們來看一個使用Linkerd作為服務網格實現的實例。
例子
我們將首先安裝Linkerd CLI,並將其部署到我們的Kubernetes集群:
> curl https://run.linkerd.io/install | sh > export PATH=$PATH:$HOME/.linkerd2/bin > linkerd install | kubectl apply -f - > linkerd check
現在我們將安裝booksapp示例應用程式:
> linkerd inject https://run.linkerd.io/booksapp.yml | kubectl apply -f -
此應用程式中的一個服務配置了錯誤率。這個演示的重點,是表明我們可以在應用程式中不需要任何支援就可以注入故障,所以讓我們刪除配置的故障率:
> kubectl edit deploy/authors # Find and remove these lines: # - name: FAILURE_RATE # value: "0.5"
我們現在應該看到應用程式是健康的:
> linkerd stat deploy NAME MESHED SUCCESS RPS LATENCY_P50 LATENCY_P95 LATENCY_P99 TCP_CONN authors 1/1 100.00% 6.6rps 3ms 58ms 92ms 6 books 1/1 100.00% 8.0rps 4ms 81ms 119ms 6 traffic 1/1 - - - - - - webapp 3/3 100.00% 7.7rps 24ms 91ms 117ms 9
現在我們可以創建錯誤服務了。在這裡,我將使用NGINX配置為只響應HTTP狀態碼500。創建一個名為error-injector.yaml的文件:
apiVersion: apps/v1 kind: Deployment metadata: name: error-injector labels: app: error-injector spec: selector: matchLabels: app: error-injector replicas: 1 template: metadata: labels: app: error-injector spec: containers: - name: nginx image: nginx:alpine ports: - containerPort: 80 name: nginx protocol: TCP volumeMounts: - name: nginx-config mountPath: /etc/nginx/nginx.conf subPath: nginx.conf volumes: - name: nginx-config configMap: name: error-injector-config --- apiVersion: v1 kind: Service metadata: labels: app: error-injector name: error-injector spec: clusterIP: None ports: - name: service port: 7002 protocol: TCP targetPort: nginx selector: app: error-injector type: ClusterIP --- apiVersion: v1 data: nginx.conf: |2 events { worker_connections 1024; } http { server { location / { return 500; } } } kind: ConfigMap metadata: name: error-injector-config
和部署它:
> kubectl apply -f error-injector.yaml
現在我們可以創建一個流量分割資源,它將把10%的圖書服務定向到錯誤服務。創建一個名為error-split.yaml的文件:
apiVersion: split.smi-spec.io/v1alpha1 kind: TrafficSplit metadata: name: error-split spec: service: books backends: - service: books weight: 900m - service: error-injector weight: 100m
和部署它:
> kubectl apply -f error-split.yaml
我們現在可以看到從webapp調用到書籍10%的故障率:
> linkerd routes deploy/webapp --to service/books ROUTE SERVICE SUCCESS RPS LATENCY_P50 LATENCY_P95 LATENCY_P99 [DEFAULT] books 90.66% 6.6rps 5ms 80ms 96ms
我們還可以看到應用程式如何優雅地處理這些故障:
> kubectl port-forward deploy/webapp 7000 & > open http://localhost:7000
看起來其實不太好!如果刷新頁面幾次,有時會看到內部伺服器錯誤頁面。

我們學習了一些有價值的東西,關於我們的應用程式如何面對服務錯誤。讓我們恢復我們的應用程式,只需刪除流量分割資源:
> kubectl delete trafficsplit/error-split
總結
在本文中,通過使用SMI API(由Linkerd提供)將一部分流量動態重定向到一個簡單的「總是失敗」目的地,我們演示了在服務級別進行故障注入的快速而簡單的方法。這種方法的美妙之處在於,我們能夠完全通過SMI API來完成它,而不需要更改任何應用程式程式碼。
當然,故障注入是一個廣泛的主題,還有許多更複雜的方法來注入故障,包括某些路由故障、只匹配特定條件的請求故障或在整個應用程式拓撲中傳播單個「毒丸」請求。這些類型的故障注入將需要比這篇文章所涵蓋的更多的設定。
Linkerd是一個社區項目,由CNCF(Cloud Native Computing Foundation,雲原生計算基金會)託管。如果你有功能需求、問題或評論,我們歡迎你加入我們快速增長的社區!Linkerd程式碼託管在GitHub上,我們在Slack、Twitter和郵件列表上都有一個蓬勃發展的社區。來一起玩吧!