经过之前的对Istio的学习,大致明白了istio的机制,而要体会istio对流量的神奇魔法,自然离不开 virtualservice 与 destinationrule这两个对象了.
首先,再次说明下两个istio与kubernetes不同的地方:
- 在kuberntes中, 服务与服务之间是通过clusterIP(kube-proxy)进行通信的, 而在istio中,对于网格间的两个服务,得益于xDS机制,通过CDS可直接得到EDS, 因此服务与服务之间不再经过clusterIP,而是直接到达对方的一个端点上(PortIP + Port)
- 在istio中,对于网格间的服务需要访问网格外的服务,仍然通过 ClusterIP 进行通信,但有一点需要注意:这里并没有
kube-proxy
的参与!Envoy 自己实现了一套流量转发机制,当你访问 ClusterIP 时,Envoy 就把流量转发到具体的 Pod 上去,不需要借助 kube-proxy 的iptables
或ipvs
规则 - 对于入口流量管理,您可能会问: 为什么不直接使用 Kubernetes Ingress API ? 原因是 Ingress API 无法表达 Istio 的路由需求。 Ingress 试图在不同的 HTTP 代理之间取一个公共的交集,因此只能支持最基本的 HTTP 路由,最终导致需要将代理的其他高级功能放入到注解(annotation)中,而注解的方式在多个代理之间是不兼容的,无法移植。Istio Gateway 通过将L4-L6配置与L7配置分离的方式克服了 Ingress 的这些缺点。 Gateway 只用于配置L4-L6功能(例如,对外公开的端口,TLS 配置),所有主流的L7代理均以统一的方式实现了这些功能。 然后,通过在 Gateway 上绑定 VirtualService 的方式,可以使用标准的 Istio 规则来控制进入 Gateway 的 HTTP 和 TCP 流量
要理解virtualservice与DestinationRule,先划个重点: 在Istio所提供的基本连接和发现基础上,通过virtualservice,能够将请求路由到Istio网格中的特定服务。每个virtualservice由一组DestinationRule组成,这些DestinationRule使Istio能够将virtualservice的每个给定请求匹配到网格内特定的目标服务(或目标服务子集)
通俗来讲就是,virtualservice定义了服务请求满足什么条件应该转发到哪里,而DestinationRule则定义了这些请求具体的路由规则,比如请求目标服务的什么版本, 负载均衡策略,连接限制等.
以下所有的操作都是基于这个demo
VirtualService
一个简单的virtualservice的yaml文件格式如下.
1 | apiVersion: networking.istio.io/v1alpha3 |
VirtualService 映射的就是 Envoy 中的 Http Route Table
需要注意上面hosts字段对应的为kubernetes的service name, 不过这里进行了省略, 在实际使用时会被扩展为FQDN形式的域名.
DestinationRule
对于上面新建的virtualservice,如果没有对应的destinationrule, 则请求service-b会被返回404(BlackHoleCluster), 因为现在还没有v1版本的service-b,路由不可达
1 | apiVersion: networking.istio.io/v1alpha3 |
DestinationRule映射到 Envoy 的配置文件中就是 Cluster
这样,virtualservice创建的路由即可达了, subset机制类似kubernetes中给对应的pod打label
上面虽然定义了subset存在v2的版本, 但是在virtualservice中并没有指定v2,因此100%的流量转向v1.
当然virtualservice与destinatiorule结合支持非常多的属性, 比如流量分配、熔断机制、 错误注入等机制.
总结: DestinationRule 经常和 VirtualService 结合使用,VirtualService 用到的服务子集 subset 在 DestinationRule 上也有定义。同时,在 VirtualService 上定义了一些规则,在 DestinationRule 上也定义了一些规则。那么,DestinationRule 和 VirtualService 都是用于流量治理的,为什么有些定义在 VirtualService 上,有些定义在 DestinationRule 上呢?
VirtualService 是一个虚拟 Service,描述的是满足什么条件的流量被哪个后端处理。可以对于这样一个 Restful 服务,每个路由规则都对应其中 Resource 中的资源匹配表达式。只是在 VirtualService 中,这个匹配条件不仅仅是路径方法的匹配,还是更开放的 Match 条件。而 DestinationRule 描述的是这个请求到达某个后端后怎么去处理,即所谓目标的规则,类似以上 Restful 服务到达的目的后端。
理解了这两个对象的定义,就不难理解其规则上的设计原理,从而理解负载均衡和熔断等策略为什么被定义在 DestinationRule 上。DestinationRule 定义了满足路由规则的流量到达后端后的访问策略。在 istio 中可以配置目标服务的负载均衡策略、连接池大小、异常实例驱逐规则等功能
实操
服务模型如下:
其中, service-b为3个实例, 分为blue,与green两个版本, github地址在这里
流量分配
service-a访问service-b时, 20%的流量转到版本v-blue, 80%的流量转到版本v-green
注意: subset指定的字段需要在pod上存在对应的label, istio不会自动给pod打上标签. 如果所有的pod上都没有该标签, 则路由不可达
1 | apiVersion: networking.istio.io/v1alpha3 |
kubernetes apply -f servicea-to-serviceb-20-80.yaml
生成的pod如下, 可以看到,总共6个pod, 其中有3个pod有label为version=1.5.2,这3个pod是无法响应请求的,
只有verion=v-blue或者v-green的可接受请求.
从kiali上看效果可以看到, 流量的分配大致维持在2/8的比例:
超时重试
请求service-b的v-green版本时,出现5xx、connect-failure时进行重试,最大重试5次, 重试超时时间3s,整个请求超时7s
并不是所有的http code值都能够进行重试, 不能说指定200进行重试, 这个是做不到的, 重试的更多参数可参考这里
1 | apiVersion: networking.istio.io/v1alpha3 |
由于使用的这个例子已经打好的镜像, 不想再重新折腾了, 就不演示模拟5xx的场景了.
错误注入
从 service-a访问 service-b的version: v-green的所有流量请求延迟5秒钟,并把流量的100%返回400错误
1 | apiVersion: networking.istio.io/v1alpha3 |
效果:
从请求时间可以看到, 请求被延迟5s之后直接返回了错误fault filter abort,故障注入起到了效果.
需要注意的是: fault字段的故障注入是有先后顺序的, 如果把http错误跟时延顺序对调一下, 则效果又将不同.
请求熔断
熔断机制是作用在DestinationRule上的,这个很好理解, DistinationRule控制着流量转发的具体对象. 一个典型的例子如下:
对于service-b的v-green版本的请求, 只允许最大一个连接
部署以下规则kubectl apply -f servicea-to-serviceb-circuit-breaker.yaml
1 | apiVersion: networking.istio.io/v1alpha3 |
关于trafficPolicy的更多参数说明, 可参考这里
为了测试效果,需要安装一个测试工具, fortio,这个工具在istio官方的httpbin目录下也存在, 可直接部署
1 | kubectl apply -f samples/httpbin/sample-client/fortio-deploy.yaml |
效果:
先测试一个连接的情况:
kubectl exec -it fortio-deploy-7cb865f87f-vkkhz -c fortio /usr/bin/fortio -- load -c 1 -qps 0 -n 2 -loglevel Warning http://10.244.0.97:8088/api/v1/greeting
一个连接的情况,发送的两个请求都正常返回
测试三个连接的情况:
kubectl exec -it fortio-deploy-7cb865f87f-vkkhz -c fortio /usr/bin/fortio -- load -c 3 -qps 0 -n 30 -loglevel Warning http://10.244.0.97:8088/api/v1/greeting
上图中显示code -1为100%, service-a有如下红框标出的报错, 说明限制的连接数起到了熔断的作用.
service-a前三条有正常日志,是因为service-a调用的链路里除了调用service-b,还调用了service-c, service-c这条跟没有限制.
当然还有一些如HTTP Redirect,HTTP Rewrite等操作都能在Istio实现, 但一般来讲都会选择在入口处实现, 具体的可参考这里
错误
在使用kiali的过程中, 或多或少会遇到istio的warning提示, 官方的kiali的错误说明列表
More than one Virtual Service for same host
出这个warning提示不影响istio的功能,原因在于存在多个virtualservice作用在service-b这个host上, 虽然Istio将合并配置,但istio建议不要在多个虚拟服务定义中定义相同的部分
这里是因为存在另一个virtualservice,host一列中定义了”*“, 自然包含了service-b这个host,因此重合了
More than one Gateway for the same host port combination
这个warning提示原因同上, 也因存在多个gateway作用于同一个hosts.
通过上面两个提示可以看出, 只要存在一个gateway或者virtualservice,使用了 hosts为”*“的定义,就能出现这个报错.
mTLS settings of a non-local Destination Rule are overridden
原因在于不存在mtls配置
可在spec指定即可.
1 | spec: |
ingressgateway 使用 NodePort暴露服务.
如果在测试的时候使用的nodeport的方式暴露服务(不是loadbalance), 在gateway/virtualservice需要自定义域名的时候, 比如官方的bookinfo的例子,如果想通过bookinfo.local:30732/productpage访问 productpage服务, hosts字段必须为”*“, 写bookinfo.local是行不通的,参考官方说明
这是因为当使用浏览器访问bookinfo.local:30732/productpage时(/etc/hosts中指定了域名解析),通过浏览器的debug可以看到请求中的host为Host: bookinfo.local:30732, 到达gateway时发现host中的域名(没有端口)跟请求中的域名比不对,导致404,同时, gateway中不支持域名:端口的形式,会报错, 因此, 这种情况下只能使用”*“
当然,在生产环境中不太可能使用Nodeport的形式暴露服务.同时也会有DNS解析服务, 因此也不太可能有上述问题.我当时测试时没想通耽误了一些时间,因此记录一下.
只定义gateway, 没有virtualservice
最后, virtualservice还可以与gateway结合使用, 通过gateway向外暴露istio服务网格内的服务, 但当只定义了gateway而没有virtualservice时,请求会被转发到blackhole, 返回404, Istio-Gsteway
1 | $ istioctl proxy-config route $INGRESS_POD -o json -n istio-system |
参考文章:
- https://mp.weixin.qq.com/s?__biz=MzU1MzY4NzQ1OA==&mid=2247483799&idx=1&sn=482cf48661eafcdb072450286cd8bc97&chksm=fbee415acc99c84c7a7ef3f2da726815203097f62bb8409102c619a1a1c6e7e3ea85c0154e76&scene=21#wechat_redirect
- https://jimmysong.io/posts/envoy-sidecar-injection-in-istio-service-mesh-deep-dive/
- https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/
- https://jimmysong.io/posts/istio-traffic-management-basic-concepts/
- https://zhaohuabing.com/post/2018-09-25-istio-traffic-management-impl-intro/
- https://kiali.io/documentation/validations/#_more_than_one_gateway_for_the_same_host_port_combination
- https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/router_filter#x-envoy-retry-on
- https://octopus.com/blog/istio/istio-virtualservice