Istio虚拟服务 (Virtual Service)

1、概述

虚拟服务(Virtual Service) 和目标规则(Destination Rule) 是 Istio 流量路由功能的关键拼图。虚拟服务让您配置如何在服务网格内将请求路由到服务,这基于 Istio 和平台提供的基本的连通性和服务发现能力。每个虚拟服务包含一组路由规则,Istio 按顺序评估它们,Istio 将每个给定的请求匹配到虚拟服务指定的实际目标地址。您的网格可以有多个虚拟服务,也可以没有,取决于您的使用场景。

2、有了 Kubernetes Service,为什么还需要 Istio Virtual Service

简单来说,基于 Kubernetes Service,只可以实现简单的流量负载均衡,如果想实现基于HTTP Header,负载百分比等等复杂的流量控制就无从下手了,Istio Vistrual Service在原本 Kubernetes Service 的功能之上,提供了更加丰富的路由控制。

3、virtualservices.networking.istio.io资源结构

通过kubectl get customresourcedefinitions.apiextensions.k8s.io virtualservices.networking.istio.io -o yaml命令查看virtualservices资源的apiGroups、apiVersions 和 resources 以及资源的 scope信息,可以看到资源scope是Namespaced。

spec:
  conversion:
    strategy: None
  group: networking.istio.io
  names:
    categories:
    - istio-io
    - networking-istio-io
    kind: VirtualService
    listKind: VirtualServiceList
    plural: virtualservices
    shortNames:
    - vs
    singular: virtualservice
  scope: Namespaced
  .....

4、Virtual Service示例

有两个Deployment(nginx 及 httpd),通过Service关联到一起,通过访问Service只能做到简单的负载均衡,通过实验发现 nginx 和 httpd 流量各自在 50% 左右。

Deployment和Service配置文件如下:

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: nginx
  name: nginx-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  strategy:
    rollingUpdate:
      maxSurge: 25%
      maxUnavailable: 25%
    type: RollingUpdate
  template:
    metadata:
      labels:
        app: nginx
        server: web
    spec:
      containers:
        - image: 'nginx:latest'
          name: nginx-deployment
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: httpd
  name: httpd-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: httpd
  strategy:
    rollingUpdate:
      maxSurge: 25%
      maxUnavailable: 25%
    type: RollingUpdate
  template:
    metadata:
      labels:
        app: httpd
        server: web
    spec:
      containers:
        - image: 'httpd:latest' 
          name: httpd-deployment 
---
apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: nginx
  type: ClusterIP
---
apiVersion: v1
kind: Service
metadata:
  name: httpd-service
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: httpd
  type: ClusterIP
---
apiVersion: v1
kind: Service
metadata:
  name: web-service
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    server: web
  type: ClusterIP

如果想实现更加细颗粒度的流量管控,通过引入Istio Vistrual Service 非常简单的就实现复杂的流量管理。VirtualService 根据 destination 进行调度,并且设置相关的负载百分比实现精准的控制。

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: web-vs   #name可以随便取值
spec:
  hosts:
  - web-service   #hosts值必须是kubernetes里面的服务名
  http:
  - route:
    - destination:
        host: nginx-service #k8s服务名
      weight: 80
    - destination:
        host: httpd-service
      weight: 20

通过客户端测试以上的实验,客户端也必须经过 Istio 注入,因为只有客户端被 Istio 注入才可以接收到来自 Pilot 有关 Virtual Service 和 Destination Rule 的配置信息,才可以保证流量接管生效。

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: client-deployment
  name: client-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: client-deployment
  strategy:
    rollingUpdate:
      maxSurge: 25%
      maxUnavailable: 25%
    type: RollingUpdate
  template:
    metadata:
      labels:
        app: client-deployment
    spec:
      containers:
        - image: 'busybox:latest'
          name: client-deployment
          command: [ "/bin/sh", "-c", "sleep 3600"]

进入客户端容器执行 wget -q -O - web-service 观察执行结果,可以测试出nginx流量在80%左右,httpd 流量在20%左右。

这里解释下 wget -q -O -含义:

-q的含义是:--quiet,安静模式,无信息输出。

-O的含义是把后面网址下载后,改成一个指定的名称,如果后面没有跟着一个名字,而是“-”,则表示将下载后的内容输出到标准输出,也就是输出到屏幕上。

-qO-的含义:把下载的内容输出到标准输出

5、Vistrual Service配置详解

除了权重之外,还有条件匹配,很多场景下,需要针对不同的用户已提供个性化的服务等,例如针对地理位置、是否为VIP等等,那就需要对http流量进行识别匹配,示例如下:

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: web-vs
spec:
  hosts:
  - web-service
  http:
  - match:
    - headers:
        end-user:
          exact: jason
      uri:
        prefix: "/health"
      ignoreUriCase: true
    route:
      - destination:
          host: httpd-service
  - route:
    - destination:
        host: nginx-service

5.1 hosts字段

使用 hosts 字段列举虚拟服务的主机——即用户指定的目标或是路由规则设定的目标。这是客户端向服务发送请求时使用的一个或多个地址。

hosts:
  - web-service   #hosts值如果是域名形式必须是kubernetes里面的服务名

在Kubernetes里面虚拟服务主机名是k8s服务clusterIp或者k8s服务的短名称(Pod DNS配置文件里面的search域会自动拼成"服务名.namespace.svc.cluster.local"形式),隐式或显式地指向一个完全限定域名(FQDN)。您也可以使用通配符(“*”)前缀,让您创建一组匹配所有服务的路由规则。虚拟服务的 hosts 字段实际上不必是 Istio 服务注册的一部分,它只是虚拟的目标地址。这让您可以为没有路由到网格内部的虚拟主机建模。

5.2 路由规则(match 字段、destination字段)

match 字段

在 http 字段包含了虚拟服务的路由规则,用来描述匹配条件和路由行为,它们把 HTTP/1.1、HTTP2 和 gRPC 等流量发送到 hosts 字段指定的目标(您也可以用 tcp 和 tls 片段为 TCP 和未终止的 TLS 流量设置路由规则)。一个路由规则包含了指定的请求要流向哪个目标地址,具有 0 或多个匹配条件,取决于您的使用场景。

示例中的第一个路由规则有一个条件,因此以 match 字段开始。在本例中,您希望此路由应用于来自 ”jason“ 用户的所有请求,所以使用 headersend-user 和 exact 字段选择适当的请求。

- match:
   - headers:
       end-user:
         exact: jason

destination字段

route 部分的 destination 字段指定了符合此条件的流量的实际目标地址。与虚拟服务的 hosts 不同,destination 的 host 必须是存在于 Istio 服务注册中心的实际目标地址(k8s服务名),否则 Envoy 不知道该将请求发送到哪里。可以是一个有代理的服务网格,或者是一个通过服务入口被添加进来的非网格服务。本示例运行在 Kubernetes 环境中,host 名为一个 Kubernetes 服务名: 

   route:
      - destination:
          host: httpd-service

请注意,在该示例和本页其它示例中,为了简单,我们使用 Kubernetes 的短名称设置 destination 的 host。在评估此规则时,Istio 会添加一个基于虚拟服务命名空间的域后缀(服务名.namespace.svc.cluster.local),这个虚拟服务包含要获取主机的完全限定名的路由规则。在我们的示例中使用短名称也意味着您可以复制并在任何喜欢的命名空间中尝试它们。 只有在目标主机和虚拟服务位于相同的 Kubernetes 命名空间时才可以使用这样的短名称。因为使用 Kubernetes 的短名称容易导致配置出错,我们建议您在生产环境中指定完全限定的主机名。destination 片段还指定了 Kubernetes 服务的子集,将符合此规则条件的请求转入其中。您可以在目标规则章节中看到如何定义服务子集。

路由规则优先级

路由规则按从上到下的顺序选择,虚拟服务中定义的第一条规则有最高优先级。本示例中,不满足第一个路由规则的流量均流向一个默认的目标,该目标在第二条规则中指定。因此,第二条规则没有 match 条件,直接将流量导向nginx-service服务。

- route:
    - destination:
        host: nginx-service

在生产环境中建议提供一个默认的“无条件”或基于权重的规则作为每一个虚拟服务的最后一条规则,如案例所示,从而确保流经虚拟服务的流量至少能够匹配一条路由规则。

路由规则的更多内容

正如上面所看到的,路由规则是将特定流量子集路由到指定目标地址的强大工具。您可以在流量端口、header 字段、URI 等内容上设置匹配条件。例如,这个虚拟服务让用户发送请求到两个独立的服务:ratings 和 reviews,就好像它们是 http://bookinfo.com/ 这个更大的虚拟服务的一部分。虚拟服务规则根据请求的 URI 和指向适当服务的请求匹配流量。

apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
  name: bookinfo
spec:
  hosts:
    - bookinfo.com
  http:
  - match:
    - uri:
        prefix: /reviews
    route:
    - destination:
        host: reviews
  - match:
    - uri:
        prefix: /ratings
    route:
    - destination:
        host: ratings
...

  http:
  - match:
      sourceLabels:
        app: reviews
    route:
...

有些匹配条件可以使用精确的值,如前缀或正则。

您可以使用 AND 向同一个 match 块添加多个匹配条件,或者使用 OR 向同一个规则添加多个 match 块。对于任何给定的虚拟服务也可以有多个路由规则。这可以在单个虚拟服务中使路由条件变得随您所愿的复杂或简单。匹配条件字段和备选值的完整列表可以在 HTTPMatchRequest 参考中找到。

另外,使用匹配条件您可以按百分比”权重“分发请求。这在 A/B 测试和金丝雀发布中非常有用:

spec:
  hosts:
  - reviews
  http:
  - route:
    - destination:
        host: reviews
        subset: v1
      weight: 75
    - destination:
        host: reviews
        subset: v2
      weight: 25

您也可以使用路由规则在流量上执行一些操作,例如:

  • 添加或删除 header。
  • 重写 URL。
  • 为调用这一目标地址的请求设置重试策略

想了解如何利用这些操作,查看 HTTPRoute 参考。 

6、总结

在Kubernetes集群里面可以通过Istio Vistrual Service实现基于HTTP Header,负载百分比等等复杂的流量控制,相比于Kubernetes Service,它提供了更加丰富的路由控制。Vistrual Service资源scope是Namespaced级别的,Pod只有被Istio注入才可以接收到来自 Pilot 有关 Virtual Service 和 Destination Rule 的配置信息,才可以保证流量接管生效。Vistrual Service路由规则按从上到下的顺序选择,虚拟服务中定义的第一条规则有最高优先级,在生产环境中建议提供一个默认的“无条件”或基于权重的规则作为每一个虚拟服务的最后一条规则,这样才能确保流经虚拟服务的流量至少能够匹配一条路由规则。

参考:https://istio.io/latest/zh/docs/concepts/traffic-management/

参考:https://zhuanlan.zhihu.com/p/262249783

posted @ 2021-12-21 22:28  人艰不拆_zmc  阅读(3919)  评论(0编辑  收藏  举报