使用服務網格介面和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和郵件列表上都有一個蓬勃發展的社區。來一起玩吧!