15 KiB
专栏上一期我们聊了Service Mesh,并以Linkerd为例介绍了Service Mesh的架构。随着技术发展,现在来看Linkerd可以说是第一代Service Mesh产品,到了今天当我们再谈到Service Mesh时,往往第一个想到的是Istio。为什么我认为Istio可以称得上是Service Mesh的代表产品呢?在我看来主要有以下几个原因:
现在我们一起走进Istio的架构,看看它各部分的实现原理,希望能让你有所收获。
Istio整体架构
如下图所示,Istio的架构可以说由两部分组成,分别是Proxy和Control Plane。
(图片来源:https://istio.io/docs/concepts/what-is-istio/arch.svg)
下面我来详细分解Istio架构,看看每一个组件的作用和工作原理。
Proxy
Istio的Proxy采用的是Envoy,Envoy是跟上一期提到的Linkerd是同一代的产品,既要作为服务消费者端的正向代理,又要作为服务提供者端的反向代理,一般需要具备服务发现、服务注册、负载均衡、限流降级、超时熔断、动态路由、监控上报和日志推送等功能,它主要包含以下几个特性:
Envoy是Istio中最基础的组件,所有其他组件的功能都是通过调用Envoy提供的API,在请求经过Envoy转发时,由Envoy执行相关的控制逻辑来实现的。
Pilot
Pilot的作用是实现流量控制,它通过向Envoy下发各种指令来实现流量控制,它的架构如下图所示。从架构图里可以看出,Pilot主要包含以下几个部分:
(图片来源:https://istio.io/docs/concepts/traffic-management/PilotAdapters.svg)
那么具体来讲,Pilot是如何实现流量管理功能的呢?
1.服务发现和负载均衡
就像下图所描述的那样,服务B也就是服务提供者注册到对应平台的注册中心中去,比如Kubernetes集群中的Pod,启动时会注册到注册中心etcd中。然后服务A也就是服务消费者在调用服务B时,请求会被Proxy拦截,然后Proxy会调用Pilot查询可用的服务提供者节点,再以某种负载均衡算法选择一个节点发起调用。
除此之外,Proxy还会定期检查缓存的服务提供者节点的健康状况,当某个节点连续多次健康检查失败就会被从Proxy从缓存的服务提供者节点列表中剔除。
(图片来源:https://istio.io/docs/concepts/traffic-management/LoadBalancing.svg)
2.请求路由
Pilot可以对服务进行版本和环境的细分,服务B包含两个版本v1.5和v2.0-alpha,其中v1.5是生产环境运行的版本,而v2.0-alpha是灰度环境运行的版本。当需要做A/B测试时,希望灰度服务B的1%流量运行v2.0-alpha版本,就可以通过调用Pilot提供的Rules API,Pilot就会向Proxy下发路由规则,Proxy在转发请求时就按照给定的路由规则,把1%的流量转发给服务B的v2.0-alpha版本,99%的流量转发给服务B的v1.5版本。
(图片来源:https://istio.io/docs/concepts/traffic-management/ServiceModel_Versions.svg)
3.超时重试
缺省状态下,Proxy转发HTTP请求时的超时是15s,可以通过调用Pilot提供的Rules API来修改路由规则,覆盖这个限制。比如下面这段路由规则,表达的意思是ratings服务的超时时间是10s。
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: ratings
spec:
hosts:
- ratings
http:
- route:
- destination:
host: ratings
subset: v1
timeout: 10s
除此之外,还可以通过修改路由规则,来指定某些HTTP请求的超时重试次数,比如下面这段路由规则,表达的意思就是ratings服务的超时重试次数总共是3次,每一次的超时时间是2s。
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: ratings
spec:
hosts:
- ratings
http:
- route:
- destination:
host: ratings
subset: v1
retries:
attempts: 3
perTryTimeout: 2s
4.故障注入
Istio还提供了故障注入的功能,能在不杀死服务节点的情况下,通过修改路由规则,将特定的故障注入到网络中。它的原理是在TCP层制造数据包的延迟或者损坏,从而模拟服务超时和调用失败的场景,以此来观察应用是否健壮。比如下面这段路由规则的意思是对v1版本的ratings服务流量中的10%注入5s的延迟。
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: ratings
spec:
hosts:
- ratings
http:
- fault:
delay:
percent: 10
fixedDelay: 5s
route:
- destination:
host: ratings
subset: v1
而下面这段路由规则意思是对v1版本的ratings服务流量中的10%注入HTTP 400的错误。
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
name: ratings
spec:
hosts:
- ratings
http:
- fault:
abort:
percent: 10
httpStatus: 400
route:
- destination:
host: ratings
subset: v1
Mixer
Mixer的作用是实现策略控制和监控日志收集等功能,实现方式是每一次Proxy转发的请求都要调用Mixer,它的架构请见下图。而且Mixer的实现是可扩展的,通过适配层来适配不同的后端平台,这样的话Istio的其他部分就不需要关心各个基础设施比如日志系统、监控系统的实现细节。
(图片来源:https://istio.io/docs/concepts/policies-and-telemetry/topology-without-cache.svg)
理论上每一次的服务调用Proxy都需要调用Mixer,一方面检查调用的合法性,一方面要上报服务的监控信息和日志信息,所以这就要求Mixer必须是高可用和低延迟的,那么Mixer是如何做到的呢?下图是它的实现原理,从图中你可以看到Mixer实现了两级的缓存结构:
(图片来源:https://istio.io/docs/concepts/policies-and-telemetry/topology-with-cache.svg)
那么Mixer是如何实现策略控制和监控日志收集功能呢?
1.策略控制
Istio支持两类的策略控制,一类是对服务的调用进行速率限制,一类是对服务的调用进行访问控制,它们都是通过在Mixer中配置规则来实现的。具体来讲,速率限制需要配置速率控制的yaml文件,每一次Proxy转发请求前都会先调用Mixer,Mixer就会根据这个yaml文件中的配置,来对调用进行速率限制。比如下面这段配置表达的意思是服务默认访问的速率限制是每秒5000次,除此之外还定义了两个特殊限制,第一个是v3版本的reviews服务请求ratings服务的速率限制是每5秒1次,第二个是其他服务请求ratings服务的速率限制是每10秒5次。
apiVersion: config.istio.io/v1alpha2
kind: memquota
metadata:
name: handler
namespace: istio-system
spec:
quotas:
- name: requestcount.quota.istio-system
maxAmount: 5000
validDuration: 1s
overrides:
- dimensions:
destination: ratings
source: reviews
sourceVersion: v3
maxAmount: 1
validDuration: 5s
- dimensions:
destination: ratings
maxAmount: 5
validDuration: 10s
而访问控制需要配置访问控制的yaml文件,每一次Proxy转发请求前都会先调用Mixer,Mixer就会根据这个yaml文件中的配置,来对调用进行访问控制。比如下面这段配置表达的意思是v3版本的reviews服务调用ratings服务就会被拒绝。
apiVersion: "config.istio.io/v1alpha2"
kind: rule
metadata:
name: denyreviewsv3
spec:
match: destination.labels["app"] == "ratings" && source.labels["app"]=="reviews" && source.labels["version"] == "v3"
actions:
- handler: denyreviewsv3handler.denier
instances: [ denyreviewsv3request.checknothing ]
2.监控和日志收集
跟策略控制的实现原理类似,Mixer的监控、日志收集功能也是通过配置监控yaml文件来实现的,Proxy发起的每一次服务调用都会先调用Mixer,把监控信息发给Mixer,Mixer再根据配置的yaml文件来决定监控信息该发到哪。示例yaml文件可以参考这个链接。
Citadel
Citadel的作用是保证服务之间访问的安全,它的工作原理见下图,可见实际的安全保障并不是Citadel独立完成的,而是需要Proxy、Pilot以及Mixer的配合,具体来讲,
(图片来源:https://istio.io/docs/concepts/security/architecture.svg)
总结
今天我给你详细讲解了Istio的架构及其基本组件Proxy、Pilot、Mixer以及Citadel的工作原理,从Istio的设计和实现原理可以看出,它是采用模块化设计,并且各个模块之间高度解耦,Proxy专注于负责服务之间的通信,Pilot专注于流量控制,Mixer专注于策略控制以及监控日志功能,而Citadel专注于安全。正是这种高度模块化的设计,使得Istio的架构极具扩展性和适配性,如果你想加强流量控制方面的功能,可以在Pilot模块中定制开发自己的代码,而不需要修改其他模块;如果你想增加一种监控系统支持,可以在Mixer模块中添加对这个监控系统的适配器,就能接入Istio。除此之外,虽然Istio由Google和IBM主导,但也没有完全与Kubernetes平台绑定,你也可以在Mesos或者AWS上运行Istio,可见它的适配性极强,这也是Istio的强大之处,以至于它的竞争对手Linkerd也开始支持Istio,作为可选的Proxy组件之一。
思考题
Mixer的一个功能是实现服务调用的日志收集,假如某一个服务调用并发量很高,而每一次调用都经过Proxy代理请求Mixer,再由Mixer调用后端的日志系统的话,整个链路的网络延迟就会对服务调用的性能影响很大,你有什么优化建议吗?
欢迎你在留言区写下自己的思考,与我一起讨论。
扩展阅读: