流量治理是一个非常宽泛的话题,例如:
- 动态修改服务间访问的负载均衡策略,比如根据某个请求特征做会话保持;
- 同一个服务有两个版本在线,将一部分流量切到某个版本上;
- 对服务进行保护,例如限制并发连接数、限制请求数、隔离故障服务实例等;
- 动态修改服务中的内容,或者模拟一个服务运行故障等。
在Istio中实现这些服务治理功能时无须修改任何应用的代码。较之微服务的SDK方式,Istio以一种更 轻便、透明的方式向用户提供了这些功能。用户可以用自己喜欢的任意语言和框架进行开发,专注于自己的业务,完全不用嵌入任何治理逻辑。只要应用运行在Istio的基础设施上,就可以使用这些治理能力。一句话总结 Istio 流量治理的目标:以基础设施的方式提供给用户非侵入的流量治理能力,用户只需关注自己的业务逻辑开发,无须关注服务访问管理。
一句话总结 Istio 流量治理的目标:以基础设施的方式提供给用户非侵入使得流量治理能力,用户只需要管住自己的业务逻辑开发,无需关注服务访问管理。
Istio 流量治理的概要流程,如图所示:
在控制面会经过如下流程:
- 管理员通过命令行或者API创建流量规则;
- Pilot将流量规则转换为Envoy的标准格式;
- Pilot将规则下发给Envoy。
在数据面会经过如下流程:
- Envoy拦截Pod上本地容器的Inbound流量和Outbound流量;
- 在流量经过Envoy时执行对应的流量规则,对流量进行治理。
因为 Istio 流量治理功能很多,读者可以根据具体场景选择治理规则。
一、负载均衡
负载均衡从严格意义上讲不应该算治理能力,因为它只做了服务间互访的基础工作,在服务调用方使用一个服务名发起访问的时候能找到一个合适的后端,把流量导过去。
传统的负载均衡一般是在服务端提供的,例如用浏览器或者手机访问一个 We b 网站时,一般在网站入口处有一个负载均衡器来做请求的汇聚和转发。服务的虚拟 IP 和后端实例一般是通过静态配置文件维护的,负载均衡器通过健康检查保证客户端的请求被路由到健康的后端实例上,图示如下:
在微服务场景下,负载均衡一般和服务发现配合使用,每个服务都有多个对等的服务实例,需要有一种机制将请求的服务名解析到服务实例地址上。服务发现负责从服务名中解析一组服务实例的列表,负载均衡负责从中选择一个实例。
如图 3-3 所示为服务发现和负载均衡的工作流程。不管是 SDK 的微服务架构,还是Istio这样的Service Mesh架构,服务发现和负载均衡的工作流程都是类似的,如下所述。
- 服务注册。各服务将服务名和服务实例的对应信息注册到服务注册中心。
- 服务发现。在客户端发起服务访问时,以同步或者异步的方式从服务注册中心获取服务对应的实例列表。
- 负载均衡。根据配置的负载均衡算法从实例列表中选择一个服务实例。
Istio 的负载均衡正是其中的一个具体应用。在 Istio 中,Pilot 负责维护服务发现数据。如图 3-4 所示为 Istio 负载均衡的流程,Pilot 将服务发现数据通过 Envoy 的标准接口下发给数据面 Envoy,Envoy 则根据配置的负载均衡策略选择一个实例转发请求。Istio 当前支持的主要负载均衡算法包括:轮询、随机和最小连接数算法。
在Kubernetes上支持Service的重要组件Kube-proxy,实际上也是运行在工作节点的一个网络代理和负载均衡器,它实现了Service模型,默认通过轮询等方式把Service访问转发到后端实例Pod上,图示如下:
二、服务熔断
熔断器在生活中一般指可以自动操作的电气开关,用来保护电路不会因为电流过载或者短路而受损,典型的动作是在检测到故障后马上中断电流。
“熔断器”这个概念延伸到计算机世界中指的是故障检测和处理逻辑,防止临时故障或意外导致系统整体不可用,最典型的应用场景是防止网络和服务调用故障级联发生,限制故障的影响范围,防止故障蔓延导致系统整体性能下降或雪崩。
如图所示为级联故障示例,可以看出在 4 个服务间有调用关系,如果后端服务recommendation由于各种原因导致不可用,则前端服务 forecast和 frontend都会受影响。在这个过程中,若单个服务的故障蔓延到其他服务,就会影响整个系统的运行,所以需要让故障服务快速失败,让调用方服务forecast和frontend知道后端服务recommendation出现问题,并立即进行故障处理。这时,非常小概率发生的事情对整个系统的影响都足够大。
在Hystrix官方曾经有这样一个推算:如果一个应用包含30个依赖的服务,每个服务都可以保证99.99%可靠性地正常运行,则从整个应用角度看,可以得到99.9930=99.7%的正常运行时间,即有0.3%的失败率,在10亿次请求中就会有3 000 000多种失败,每个月就会有两个小时以上的宕机。即使其他服务都是运行良好的,只要其中一个服务有这样0.001%的故障几率,对整个系统就都会产生严重的影响。
关于熔断的设计,Martin Fowler 有一个经典的文章(https://martinfowler.com/bliki/CircuitBreaker.html),其中描述的熔断主要应用于微服务场景下的分布式调用中:在远程调用时,请求在超时前一直挂起,会导致请求链路上的级联故障和资源耗尽;熔断器封装了被保护的逻辑,监控调用是否失败,当连续调用失败的数量超过阈值时,熔断器就会跳闸,在跳闸后的一定时间段内,所有调用远程服务的尝试都将立即返回失败;同时,熔断器设置了一个计时器,当计时到期时,允许有限数量的测试请求通过;如果这些请求成功,则熔断器恢复正常操作;如果这些请求失败,则维持断路状态。Martin把这个简单的模型通过一个状态机来表达,我们简单理解下,图示如下:
三个点表示熔断器的状态,下面分别进行解释。
- 熔断关闭:熔断器处于关闭状态,服务可以访问。熔断器维护了访问失败的计数器,若服务访问失败则加一。
- 熔断开启:熔断器处于开启状态,服务不可访问,若有服务访问则立即出错。
- 熔断半开启:熔断器处于半开启状态,允许对服务尝试请求,若服务访问成功则说明故障已经得到解决,否则说明故障依然存在。
图上状态机上的几条边表示几种状态流转,如表3-1所示。
Martin这个状态机成为后面很多系统实现的设计指导,包括最有名的Hystrix,当然,Istio的异常点检测也是按照类似语义工作的。