本文发布已超过一年。较早的文章可能包含过时的内容。请检查页面中的信息自发布以来是否已不再准确。
使用 Istio 服务网格进行请求路由和策略管理
编者按:今天的文章是关于 Istio 的三部分系列文章的第二篇。
在上一篇文章中,我们查看了一个由四个独立的微服务组成的简单应用 (Bookinfo)。文章展示了如何在不改变任何应用代码的情况下,使用 Kubernetes 和启用 Istio 的集群来部署应用。文章还概述了如何查看 Istio 提供的运行中服务的 L7 指标。
本文将继续深入探讨 Istio,并使用 Bookinfo 进行示例。具体来说,我们将介绍 Istio 的另外两个功能:请求路由和策略管理。
运行 Bookinfo 应用
和之前一样,我们运行 Bookinfo 应用的 v1 版本。在集群中安装 Istio 后,我们使用以下命令启动bookinfo-v1.yaml 中定义的应用程序:
kubectl apply -f \<(istioctl kube-inject -f bookinfo-v1.yaml)
我们为应用创建了一个 Ingress 资源
cat \<\<EOF | kubectl create -f -
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: bookinfo
annotations:
kubernetes.io/ingress.class: "istio"
spec:
rules:
- http:
paths:
- path: /productpage
backend:
serviceName: productpage
servicePort: 9080
- path: /login
backend:
serviceName: productpage
servicePort: 9080
- path: /logout
backend:
serviceName: productpage
servicePort: 9080
EOF
然后我们检索了 Istio Ingress 控制器的 NodePort 地址
export BOOKINFO\_URL=$(kubectl get po -n istio-system -l istio=ingress -o jsonpath={.items[0].status.hostIP}):$(kubectl get svc -n istio-system istio-ingress -o jsonpath={.spec.ports[0].nodePort})
最后,我们将浏览器指向 http://$BOOKINFO_URL/productpage,以查看运行中的 v1 应用
HTTP 请求路由
现有的容器编排平台,如 Kubernetes、Mesos 和其他微服务框架,允许操作员控制何时将流量发送到特定的 Pod/VM 集合 (例如,通过添加/移除特定标签)。与现有技术不同,Istio 将流量流与基础设施扩缩容解耦。这使得 Istio 能够提供各种流量管理功能,这些功能位于应用代码之外,包括用于 A/B 测试、金丝雀发布、渐进式发布、使用超时、重试、熔断器进行故障恢复,以及用于测试跨服务故障恢复策略兼容性的故障注入的动态 HTTP 请求路由。
为了演示,我们将部署 reviews 服务的 v2 版本,并使用 Istio 使其仅对特定的测试用户可见。我们可以使用这个 YAML 文件创建一个 Kubernetes Deployment,名为 reviews-v2:
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: reviews-v2
spec:
replicas: 1
template:
metadata:
labels:
app: reviews
version: v2
spec:
containers:
- name: reviews
image: istio/examples-bookinfo-reviews-v2:0.2.3
imagePullPolicy: IfNotPresent
ports:
- containerPort: 9080
从 Kubernetes 的角度来看,v2 Deployment 添加了额外的 Pod,这些 Pod 会被 reviews service 的选择器纳入轮询负载均衡算法中。这也是 Istio 的默认行为。
在我们启动 reviews:v2 之前,我们将启动 Bookinfo 应用的最后一个服务,ratings。v2 版本使用 ratings 服务为每个评论提供对应的评分星级。
kubectl apply -f \<(istioctl kube-inject -f bookinfo-ratings.yaml)
如果我们现在启动 reviews:v2,我们会看到浏览器响应在 v1 (没有对应评分的评论) 和 v2 (带有黑色评分星级的评论) 之间交替出现。然而,这并不会发生,因为我们将使用 Istio 的流量管理功能来控制流量。
使用 Istio,新版本不需要根据运行中的 Pod 数量来决定是否可见。版本可见性由指定精确标准的规则来控制。为了演示,我们首先使用 Istio 指定我们将 100% 的 reviews 服务流量发送到 v1 Pod。
立即为服务网格中的每个服务设置一个默认规则是 Istio 的最佳实践。这样做可以避免新版本(可能不稳定)意外可见。然而,为了演示的目的,我们只对 reviews 服务执行此操作:
cat \<\<EOF | istioctl create -f -
apiVersion: config.istio.io/v1alpha2
kind: RouteRule
metadata:
name: reviews-default
spec:
destination:
name: reviews
route:
- labels:
version: v1
weight: 100
EOF
这个命令指示服务网格将 reviews 服务的 100% 流量发送到带有标签“version: v1”的 Pod。有了这条规则,我们可以安全地部署 v2 版本而不会暴露它。
kubectl apply -f \<(istioctl kube-inject -f bookinfo-reviews-v2.yaml)
刷新 Bookinfo 网页,确认没有发生任何变化。
此时,我们有很多选项来决定如何暴露 reviews:v2。例如,如果想进行简单的金丝雀测试,可以使用类似这样的规则将 10% 的流量发送到 v2:
apiVersion: config.istio.io/v1alpha2
kind: RouteRule
metadata:
name: reviews-default
spec:
destination:
name: reviews
route:
- labels:
version: v2
weight: 10
- labels:
version: v1
weight: 90
对于服务版本的早期测试,更好的方法是更具体地限制对其的访问。为了演示,我们将设置一条规则,使 reviews:v2 仅对特定的测试用户可见。我们通过设置第二条优先级更高的规则来做到这一点,这条规则只在请求匹配特定条件时应用:
cat \<\<EOF | istioctl create -f -
apiVersion: config.istio.io/v1alpha2
kind: RouteRule
metadata:
name: reviews-test-v2
spec:
destination:
name: reviews
precedence: 2
match:
request:
headers:
cookie:
regex: "^(.\*?;)?(user=jason)(;.\*)?$"
route:
- labels:
version: v2
weight: 100
EOF
这里我们指定条件是请求头中需要包含值为“tester”的用户 cookie。如果这条规则不匹配,我们将回退到 v1 的默认路由规则。
如果我们使用用户名“tester”(无需密码)登录 Bookinfo UI,我们现在将看到应用的 v2 版本(每个评论包含 1-5 个黑色评分星)。其他所有用户都不受此更改的影响。
v2 版本经过全面测试后,我们可以使用 Istio 继续进行金丝雀测试(使用之前展示的规则),或者我们可以简单地将所有流量从 v1 迁移到 v2,也可以通过一系列权重小于 100 的规则(例如:10、20、30、... 100)以渐进方式迁移。这种流量控制与实现每个版本的 Pod 数量无关。例如,如果我们启用了自动扩缩容并且流量很大,我们很可能会看到 v2 Pod 相应地扩容,同时 v1 Pod 独立地缩容。有关使用自动扩缩容进行版本路由的更多信息,请参阅“使用 Istio 进行金丝雀部署”。
在我们的示例中,我们将使用一个命令将所有流量发送到 v2:
cat \<\<EOF | istioctl replace -f -
apiVersion: config.istio.io/v1alpha2
kind: RouteRule
metadata:
name: reviews-default
spec:
destination:
name: reviews
route:
- labels:
version: v2
weight: 100
EOF
我们还应该移除为测试用户创建的特殊规则,这样它就不会覆盖我们将来决定进行的任何发布。
istioctl delete routerule reviews-test-v2
在 Bookinfo UI 中,我们将看到现在 reviews 服务的 v2 版本已经对所有用户开放。
策略执行
Istio 提供了策略执行功能,例如配额、前置条件检查和访问控制。我们可以通过一个示例来演示 Istio 开放且可扩展的策略框架:限速。
让我们假设 Bookinfo 的 ratings 服务是一个外部付费服务——例如,Rotten Tomatoes®——具有每秒 1 个请求 (req/sec) 的免费配额。为了确保应用程序不超过此限制,我们将指定一个 Istio 策略,在达到限制后切断请求。为此,我们将使用 Istio 的内置策略之一。
要设置 1 req/sec 的配额,我们首先配置一个带有速率限制的 memquota 处理程序:
cat \<\<EOF | istioctl create -f -
apiVersion: "config.istio.io/v1alpha2"
kind: memquota
metadata:
name: handler
namespace: default
spec:
quotas:
- name: requestcount.quota.default
maxAmount: 5000
validDuration: 1s
overrides:
- dimensions:
destination: ratings
maxAmount: 1
validDuration: 1s
EOF
然后我们创建一个 quota 实例,将传入的属性映射到配额维度,并创建一个使用该实例和 memquota 处理程序的 rule:
cat \<\<EOF | istioctl create -f -
apiVersion: "config.istio.io/v1alpha2"
kind: quota
metadata:
name: requestcount
namespace: default
spec:
dimensions:
source: source.labels["app"] | source.service | "unknown"
sourceVersion: source.labels["version"] | "unknown"
destination: destination.labels["app"] | destination.service | "unknown"
destinationVersion: destination.labels["version"] | "unknown"
---
apiVersion: "config.istio.io/v1alpha2"
kind: rule
metadata:
name: quota
namespace: default
spec:
actions:
- handler: handler.memquota
instances:
- requestcount.quota
EOF
为了看到限速效果,我们将对应用产生一些负载:
wrk -t1 -c1 -d20s http://$BOOKINFO\_URL/productpage
在网页浏览器中,我们会注意到当负载生成器运行时(即,每秒生成超过 1 个请求),浏览器流量被切断了。页面上不再是每个评论旁边的黑色星级,而是显示一条消息,指示评分当前不可用。
停止负载生成器意味着不再超出限制:当我们刷新页面时,黑色星级会重新出现。
总结
我们向您展示了如何在无需重启任何服务的情况下,将 HTTP 请求路由和策略注入等高级功能引入配置了 Istio 的服务网格中。这使您可以专注于开发和部署,而无需担心服务网格的持续管理;服务范围的策略可以随时添加。
在本系列的下一篇也是最后一篇文章中,我们将重点介绍 Istio 的安全和认证能力。我们将讨论如何在服务网格中保护所有服务间的通信,即使面对能够访问网络的内部人员,也无需对应用代码或部署进行任何更改。