应用程序故障注入是混沌工程的一种形式,在微服务应用程序中人为地增加某些服务的错误率,以查看它对整个系统的影响。传统上,需要在服务代码中添加某种类型的故障注入库,才能对应用程序进行故障注入,值得庆幸的是,服务网格提供了一种无需修改或重构服务的应用程序故障注入方法。
结构良好的微服务应用程序有一个特点,它可以优雅地容忍单点服务故障。当这些故障以服务崩溃的形式出现时,Kubernetes 在修复服务崩溃故障方面做的非常好,它可以通过创建新的 Pods 替换崩溃的 Pods 的方式来修复这些故障。然而,故障也可能更加微妙,如导致服务返回的错误率升高。对于这种类型的故障,Kubernetes 就不能自动修复了,仍然会导致部分功能丧失。
使用流量分割 SMI API 注入错误
使用服务网格接口(Service Mesh Interface,SMI)的流量分割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
复制代码
现在,我们创建一个错误服务。在此,我使用配置成返回 HTTP 500 状态代码的 NGINX,创建一个名为 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
复制代码
部署 error-injector.yaml 文件 :
> kubectl apply -f error-injector.yaml
复制代码
现在,我们创建一个流量分割资源,它会将 10%的流量从“books”服务重定向到“error-injector”错误服务。该资源文件命名为 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
复制代码
部署 error-split.yaml 文件:
> kubectl apply -f error-split.yaml
复制代码
现在,我们可以看到从 webapp 到 books 的调用错误率为 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 是一个由云原生计算基金会(Cloud Native Computing Foundation,CNCF)托管的社区项目。Linkerd 托管在GitHub上,在Slack,Twitter和mailing lists上社区也很活跃,感兴趣的开发者可以下载试用。
原文链接:
https://linkerd.io/2019/07/18/failure-injection-using-the-service-mesh-interface-and-linkerd/index.html
评论