概述
在之前的文章中,我们已经完成了 Istio 的环境搭建并部署了一个 Bookinfo 的示例程序。
在接下来的内容中,我们将会基于 Bookinfo 示例程序来演示 Istio 中最核心的功能 - 动态路由。
核心概念
在开始动态路由的实战讲解之前,我们需要先来了解一下动态路由中涉及到的 Istio 中的一些核心概念。
如上图所示,在 Istio 的服务调用时,涉及到两个核心的对象,分别是虚拟服务和目标规则。
VirtualService(虚拟服务)
VirtualService 的核心作用在于定义路由访问的规则,例如对于某种特征的请求应该发送到哪里。
DestinationRule(目标规则)
DestinationRule 常常用于配合 VirtualService 来使用:
一方面,DestinationRule可以通过 Subset 的方式将 K8s Service 中的 Pod 进行分组,并配置VirtualService规则实现细粒度的调度;
另一方面,DestinationRule 还可以用于定义到达指定目标的请求应该如何处理,例如重试策略、熔断策略等。
实战
在本文在,我们将会演示 Istio 中的动态路由功能应该如何使用,具体来说如何将请求路由到服务的不同版本中。
具体来说,希望你通过本文的学习可以掌握:
- 动态路由的配置;
- VirtualService 和 DestinationRule 的配置方法。
示例项目架构图
与 QuickStart 中的示例项目一样,我们还是以Bookinfo 项目为例来演示相关的功能使用。
上图是 Bookinfo 项目的实例架构图。
StepByStep
Step1: 前情回顾
在之前 QuickStart 的示例中,其中一个微服务 reviews 的三个不同版本已经部署并同时运行。
这导致在浏览器中访问 Bookinfo 应用程序的 /productpage 并刷新几次,
有时书评的输出包含星级评分,有时则不包含。
这是因为没有明确的默认服务版本可路由,Istio 将以循环方式将请求路由到所有可用版本。
Step2: 配置路由策略
在下面的实践中,让我们会将所有流量都路由到 reviews 的 v1 版本,具体来看一下如何来实现吧!
首先,执行如下的命令来创建相关的 VirtualService 和 DestinationRule:
# 创建相关的 VirtualService
kubectl apply -f samples/bookinfo/networking/virtual-service-all-v1.yaml -n istio-demo
# virtualservice.networking.istio.io/productpage created
# virtualservice.networking.istio.io/reviews created
# virtualservice.networking.istio.io/ratings created
# virtualservice.networking.istio.io/details created
# 创建相关的 DestinationRule
kubectl apply -f samples/bookinfo/networking/destination-rule-all.yaml -n istio-demo
# destinationrule.networking.istio.io/productpage created
# destinationrule.networking.istio.io/reviews created
# destinationrule.networking.istio.io/ratings created
# destinationrule.networking.istio.io/details created
可以看到,我们创建了四个服务对应的 virtualservice 和 destinationrule,下面我们先来体验一下相关的功能吧!
再次打开 homepage 页面,你会发现无论你怎么刷新页面,始终都无法看到对应的星标了!
这说明我们的路由配置已经生效了,那么它们具体是如何生效的呢?请跟随我们的内容一起来掌握VirtualService和 DestinationRule的使用原理吧。
原理说明
资源配置项
下面,我们首先来了解一下 VirtualService和 DestinationRule 提供了哪些配置项以及它们的关系吧:
可以看到,对于 VirtualService 而言,其核心的字段是 http,表示针对 http 类型的请求如何进行处理。
而在 HTTP Route 中:
- match 字段用于定义各种规则,当规则匹配后,会进入对应的 route 地址
- route 字段用于定义具体访问的目标地址,它常常与 DestinationRule 组合使用。DestinationRule用户将服务分为多个 subset,VirtualService将匹配指定规则的服务转发到对应的 subset中。
此外,对于 DestinationRule 的 Subset 中,它还可以配置一些具体的访问策略,例如负载均衡策略等。
配置分析
了解了刚才我们上述的 VirtualService 和 DestinationRule 的基本配置,现在我们来回头看一下我们刚才具体生效了哪些配置,使得它们可以实现流量都路由到 reviews 的 v1 版本。
因为是控制了发往 reviews 模块的流量,因此,我们重点来关注 reviews 对应的 VirtualService 和 DestinationRule 的配置。
reviews VirtualService 配置:
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: reviews
spec:
hosts:
- reviews
http:
- route:
- destination:
host: reviews
subset: v1
reviews 的配置看起来还是比较清晰的。
这个 VirtualService 生效于所有调用 reviews 服务的流量,并将该流量全部转发给 reviews DestinationRule 中定义的 v1 版本的 subset 中。
那我们来看一下 DestinationRule 的配置又是如何设置的呢?
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
name: reviews
spec:
host: reviews
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2
- name: v3
labels:
version: v3
可以看到,在 DestinationRule 的配置中,我们将 reviews 的服务又切分为了3个subset。而切分的规则是根据Pod上的 labels 中的 version 来进行切分的。例如,我们将所有包含 label 中 version 为 v1 的 Pod 划分到 subset v1中。
在 QuickStart 中,我们其实没有详细查看对应 deployment 中的配置信息,现在我们再来看看当初的reviews的多个不同的部署对象是否都增加了对应的不同的version的label。
apiVersion: apps/v1
kind: Deployment
metadata:
name: reviews-v1
labels:
app: reviews
version: v1
spec:
replicas: 1
selector:
matchLabels:
app: reviews
version: v1
template:
metadata:
labels:
app: reviews
version: v1
spec:
...
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: reviews-v2
labels:
app: reviews
version: v2
spec:
replicas: 1
selector:
matchLabels:
app: reviews
version: v2
template:
metadata:
labels:
app: reviews
version: v2
....
可以看到,在 deployment 的配置中,对于对应的 template 的 metadata 下,我们的确针对不同的版本设置了不同的 labels 的 version 信息。
到此为止,我们应该就了解了整个过程的完整流程了!
- 首先,在创建Deployment时,我们针对不同版本的Deployment设置了不同的 labels 中的 version 信息;
- 然后,我们创建一个对应的 DestinationRule,这个DestinationRule的目的是通过 version 信息将 Pod 分组到不同的 subset 中。
- 最后,在 VirtualService 中,我们可以配置各种规则将流量转发到指定的 Subset 中,从而实现了请求的动态路由。
思考与实战
将登录用户路由到 reviews 服务的 v2 版本
在上述的实战中,我们直接将所有的流量全部都路由到了 reviews 服务的 v1 版本。
然而,在更多的场景中,我们其实是希望能够将不同类型的请求转发到不同的版本中的,下面,我们就来尝试将登录用户的请求都转发到 v2 版本,其他用户的请求仍然还是访问 v1 版本。
Ps: 对于登录用户的请求而言,bookinfo 调用链中会在 HTTP header 中添加 end-user: ${username} 的信息。
我们先来简单的分析一下:
- DestinationRule 的作用是根据 labels version 来将 Pod 分组为 subset,而在当前场景中,没有新的 version 出现,因此,DestinationRule 其实是无需变化的。
- VirtualService 是根据流量特征设置规则来将对应的流量转发到指定 route 的。而在当前的目标下,我们其实是希望将 Headers 中包含 end-user Key 的请求转发到 v2 版本的 reviews,其他流量转发到 v1 版本,因此,我们需要对 VirtualService 配置进行修改。
VirtualService 修改后的内容如下,我们命名为 virtual-service-reviews-test-v3.yaml :
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: reviews
spec:
hosts:
- reviews
http:
- match:
- headers:
end-user:
regex: ^.*$
route:
- destination:
host: reviews
subset: v2
- route:
- destination:
host: reviews
subset: v1
可以看到,我们在 http 中,增加了一个带有 match 条件的 route 路由块。
具体规则为:
- 当 headers 中 end-user 字段存在时,发送到 reviews v2 版本。
- 否则,发送到 reviews v1 版本。
下面,我们来实验一下看看吧:
kubectl apply -f samples/bookinfo/networking/virtual-service-reviews-test-v3.yaml -n istio-demo
# virtualservice.networking.istio.io/reviews configured
配置完成后,我们来访问看看吧:
没有登录时,依然是 V1 版本,没有评分:
登录任意用户后,已经访问到了 V2 版本,显示出来了黑色的评分: