Think Deep,Work Lean

一次微服务分流问题分析复盘

Posted on By zack

背景

使用Istio在做微服务分流的时候,可以根据Header或是PATH来匹配七层流量,并分流。

目标

使用bookinfo示例,对reviews做灰度发布,通过匹配Http Header实现分流;

过程

  1. 在k8s集群节点上,通过Curl命令,指定Header,直接访问reviews服务时,没有实现分流;

  2. 通过进入productpage pod的网络空间,指定Header能实现分流;

  3. 在相同的Namespace下,创建一个pod,在该Pod中,指定Header访问服务,未实现分流。

  4. 给这个Pod 注入Sidecar,能实现分流。

  5. 在不同的Namespace下,注入Sidecar的Pod中访问,能实现分流。

问题

  1. 为什么直接访问,指定Header无法实现分流?

  2. 为什么使用未注入Sidecar的容器访问,无法实现分流?

原因:

上述两种情况,是servicemesh外的流量,需要经过ingress或是gateway来访问。

解决方法

使用Nginx Ingress网关来,给网关注入Sidecar。

在使用自定义Header来实现分流功能时,需要Match Request Host,因此在使用使用Nginx Ingress做网关时,需要Rewrite Host Header,指定Upstream的Host。

给Ingress设置Annotation nginx.ingress.kubernetes.io/service-upstream: reviews.test.svc.cluster.local

环境

通过KubeSphere界面创建灰度发布,通过Match Header来实现分流。

本质上是操作VirtualService

 k get virtualservices.networking.istio.io reviews  -oyaml
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  creationTimestamp: "2021-02-02T02:59:17Z"
  generation: 29
  labels:
    app: reviews
    app.kubernetes.io/name: bookinfo
    app.kubernetes.io/version: v1
  name: reviews
  namespace: dsds
spec:
  hosts:
  - reviews
  http:
  - match:
    - headers:
        end-user:
          exact: zack
    route:
    - destination:
        host: reviews
        port:
          number: 9080
        subset: v2
      weight: 100
  - route:
    - destination:
        host: reviews
        port:
          number: 9080
        subset: v1
      weight: 100

FAQ

  1. 如何使用curl时,指定Header
for i in {1..10000}; do curl -H 'end-user: zack' -H 'Host: reviews.dsds.svc.cluster.local' -I 10.233.45.153:9080;sleep 0.1;done
  1. 在做灰度发布的时候,是不是一定要让流量先经过Service?能不能直接把流量打到Pod上面?因为每个Pod有了Sidecar后,相当于是经过了一层envoy proxy,那么是不是可以自动路由到其他的Pod?

不行。必须先经过Service,VirtualService、DestinationRule的规则才能生效。

  1. 为什么非要经过网关解决?

其实istio 有 servicemesh cluster概念,注入了sidecar的容器都称之为在servicemesh cluster内,网络访问符合virtualService/DestinationRule定义的规则。因此,把网关注入Sidecar后,然后流量从网关进入转发到对应的服务上,就能解决。

上面的解释有点解释不通,比如经过Ingress Gateway作网关的时候,istio-ingressgateway就未注入Sidecar。

另外经过测试,如果不经过网关,直接访问服务,会发现virtulservice的设置并没有生效。

设置流量比例,本质是通过vitualservice的实现流量分发,此时并不依赖HTTP。说明跟http header没有太大关系,之前不经过网关直接访问服务实现Http 匹配Header不生效,一直以为是Virtualservice的Host不匹配Request的Header头部的原因,其实方向错了。因为直接访问服务的时候整个Virtualservcie压根就没有生效。

问题:为什么不经过网关直接访问服务时,Virtualservice就是不生效,而是使用了Service Ipvs那一套呢

猜想:如果从网关或是从注入过sidecar的pod内来访问时,会经过virtualservice规则的过滤;如果是直接访问服务时,会优先使用service ipvs的转发规则。 换言之:这里有两套转发规则:envoy及ipvs,它们生效的先后顺序决定最终结果。这里需要继续深入调研。

这个问题参考下面这个问题解决。

virtualservice destinationrule里面定义的规则,是不是就是修改注入sidecar中envoy的过滤规则呢?

其实并不完全对,想象一下这个场景。如果上面的问题成立,那么在做版本流量分流时,此时两版本的pod都会有sidecar,如果规则生效,意味着一个pod需要把流量发到另一个Pod才可行。

可实际上并不是这样的。可以这样理解,virtualservice的规则是路由规则,是在这两个版本pod的前面的一个sidecar envoy中生效的。这样,它就知道如何把流量准确路由到不同的版本中。而Destinationrule可以理解成设置这个pod中的sidecar envoy的规则。

通过这个模型,就理解了上一个问题了,因为经过Ingress Gateway时,这个网关注入了sidecar后,可以配置上游的Virtualservice规则。

另外 istio-ingressgateway 本身就是个envoy,类似于一个单独的sidecar,因此原理一样。

是不是必须负载注入sidecar,才可以使用virtualservice?

不一定,有这个场景也是生效的:client —> istio-ingressgateway —-> serviceA (未注入sidecar)。此时的virtualservice规则也是生效的,因为它是配置在istio-ingressgateway上的(即负载的前面的一个转发节点)。