概述

在之前的文章中,我们已经完成了 Istio 的环境搭建并部署了一个 Bookinfo 的示例程序。
在接下来的内容中,我们将会基于 Bookinfo 示例程序来演示 Istio 中最核心的功能 - 动态路由

核心概念

在开始动态路由的实战讲解之前,我们需要先来了解一下动态路由中涉及到的 Istio 中的一些核心概念。
image.png
如上图所示,在 Istio 的服务调用时,涉及到两个核心的对象,分别是虚拟服务和目标规则。

VirtualService(虚拟服务)

VirtualService 的核心作用在于定义路由访问的规则,例如对于某种特征的请求应该发送到哪里。

DestinationRule(目标规则)

DestinationRule 常常用于配合 VirtualService 来使用:
一方面,DestinationRule可以通过 Subset 的方式将 K8s Service 中的 Pod 进行分组,并配置VirtualService规则实现细粒度的调度;
另一方面,DestinationRule 还可以用于定义到达指定目标的请求应该如何处理,例如重试策略、熔断策略等。

实战

在本文在,我们将会演示 Istio 中的动态路由功能应该如何使用,具体来说如何将请求路由到服务的不同版本中。
具体来说,希望你通过本文的学习可以掌握:

  • 动态路由的配置;
  • VirtualService 和 DestinationRule 的配置方法。

示例项目架构图

image.png
与 QuickStart 中的示例项目一样,我们还是以Bookinfo 项目为例来演示相关的功能使用。
上图是 Bookinfo 项目的实例架构图。

StepByStep

Step1: 前情回顾

在之前 QuickStart 的示例中,其中一个微服务 reviews 的三个不同版本已经部署并同时运行。
这导致在浏览器中访问 Bookinfo 应用程序的 /productpage 并刷新几次,
有时书评的输出包含星级评分,有时则不包含。
这是因为没有明确的默认服务版本可路由,Istio 将以循环方式将请求路由到所有可用版本。

Step2: 配置路由策略

在下面的实践中,让我们会将所有流量都路由到 reviews 的 v1 版本,具体来看一下如何来实现吧!
首先,执行如下的命令来创建相关的 VirtualService 和 DestinationRule:

  1. # 创建相关的 VirtualService
  2. kubectl apply -f samples/bookinfo/networking/virtual-service-all-v1.yaml -n istio-demo
  3. # virtualservice.networking.istio.io/productpage created
  4. # virtualservice.networking.istio.io/reviews created
  5. # virtualservice.networking.istio.io/ratings created
  6. # virtualservice.networking.istio.io/details created
  7. # 创建相关的 DestinationRule
  8. kubectl apply -f samples/bookinfo/networking/destination-rule-all.yaml -n istio-demo
  9. # destinationrule.networking.istio.io/productpage created
  10. # destinationrule.networking.istio.io/reviews created
  11. # destinationrule.networking.istio.io/ratings created
  12. # destinationrule.networking.istio.io/details created

可以看到,我们创建了四个服务对应的 virtualservice 和 destinationrule,下面我们先来体验一下相关的功能吧!
再次打开 homepage 页面,你会发现无论你怎么刷新页面,始终都无法看到对应的星标了!
image.png
这说明我们的路由配置已经生效了,那么它们具体是如何生效的呢?请跟随我们的内容一起来掌握VirtualService和 DestinationRule的使用原理吧。

原理说明

资源配置项

下面,我们首先来了解一下 VirtualService和 DestinationRule 提供了哪些配置项以及它们的关系吧:
image.png
可以看到,对于 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 配置:

  1. apiVersion: networking.istio.io/v1alpha3
  2. kind: VirtualService
  3. metadata:
  4. name: reviews
  5. spec:
  6. hosts:
  7. - reviews
  8. http:
  9. - route:
  10. - destination:
  11. host: reviews
  12. subset: v1

reviews 的配置看起来还是比较清晰的。
这个 VirtualService 生效于所有调用 reviews 服务的流量,并将该流量全部转发给 reviews DestinationRule 中定义的 v1 版本的 subset 中。
那我们来看一下 DestinationRule 的配置又是如何设置的呢?

  1. apiVersion: networking.istio.io/v1alpha3
  2. kind: DestinationRule
  3. metadata:
  4. name: reviews
  5. spec:
  6. host: reviews
  7. subsets:
  8. - name: v1
  9. labels:
  10. version: v1
  11. - name: v2
  12. labels:
  13. version: v2
  14. - name: v3
  15. labels:
  16. version: v3

可以看到,在 DestinationRule 的配置中,我们将 reviews 的服务又切分为了3个subset。而切分的规则是根据Pod上的 labels 中的 version 来进行切分的。例如,我们将所有包含 label 中 version 为 v1 的 Pod 划分到 subset v1中。
在 QuickStart 中,我们其实没有详细查看对应 deployment 中的配置信息,现在我们再来看看当初的reviews的多个不同的部署对象是否都增加了对应的不同的version的label。

  1. apiVersion: apps/v1
  2. kind: Deployment
  3. metadata:
  4. name: reviews-v1
  5. labels:
  6. app: reviews
  7. version: v1
  8. spec:
  9. replicas: 1
  10. selector:
  11. matchLabels:
  12. app: reviews
  13. version: v1
  14. template:
  15. metadata:
  16. labels:
  17. app: reviews
  18. version: v1
  19. spec:
  20. ...
  21. ---
  22. apiVersion: apps/v1
  23. kind: Deployment
  24. metadata:
  25. name: reviews-v2
  26. labels:
  27. app: reviews
  28. version: v2
  29. spec:
  30. replicas: 1
  31. selector:
  32. matchLabels:
  33. app: reviews
  34. version: v2
  35. template:
  36. metadata:
  37. labels:
  38. app: reviews
  39. version: v2
  40. ....

可以看到,在 deployment 的配置中,对于对应的 template 的 metadata 下,我们的确针对不同的版本设置了不同的 labels 的 version 信息。

到此为止,我们应该就了解了整个过程的完整流程了!

  1. 首先,在创建Deployment时,我们针对不同版本的Deployment设置了不同的 labels 中的 version 信息;
  2. 然后,我们创建一个对应的 DestinationRule,这个DestinationRule的目的是通过 version 信息将 Pod 分组到不同的 subset 中。
  3. 最后,在 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 :

  1. apiVersion: networking.istio.io/v1alpha3
  2. kind: VirtualService
  3. metadata:
  4. name: reviews
  5. spec:
  6. hosts:
  7. - reviews
  8. http:
  9. - match:
  10. - headers:
  11. end-user:
  12. regex: ^.*$
  13. route:
  14. - destination:
  15. host: reviews
  16. subset: v2
  17. - route:
  18. - destination:
  19. host: reviews
  20. subset: v1

可以看到,我们在 http 中,增加了一个带有 match 条件的 route 路由块。
具体规则为:

  1. 当 headers 中 end-user 字段存在时,发送到 reviews v2 版本。
  2. 否则,发送到 reviews v1 版本。

下面,我们来实验一下看看吧:

  1. kubectl apply -f samples/bookinfo/networking/virtual-service-reviews-test-v3.yaml -n istio-demo
  2. # virtualservice.networking.istio.io/reviews configured

配置完成后,我们来访问看看吧:
没有登录时,依然是 V1 版本,没有评分:
image.png
登录任意用户后,已经访问到了 V2 版本,显示出来了黑色的评分:
image.png