作为业内首个全托管Istio兼容的阿里云服务网格产品ASM,一开始从架构上就保持了与社区、业界趋势的一致性,控制平面的组件托管在阿里云侧,与数据面侧的用户集群独立。ASM产品是基于社区Istio定制实现的,在托管的控制面侧提供了用于支撑精细化的流量管理和安全管理的组件能力。通过托管模式,解耦了Istio组件与所管理的K8s集群的生命周期管理,使得架构更加灵活,提升了系统的可伸缩性。从2022年4月1日起,阿里云服务网格ASM正式推出商业化版本, 提供了更丰富的能力、更大的规模支持及更完善的技术保障,更好地满足客户的不同需求场景,详情可进入阿里云官方网站 - 搜索服务网格ASM。
Introduction
2023年,提起服务网格,相信对于了解一些云原生相关概念的业者来说,倒也不算是一个闻所未闻的概念了。
服务网格是一个通过“Sidecar”模式进行服务治理简化的平台,能够以无侵入的方式提供包括服务调用之间的流量路由与拆分管理、服务间通信的认证安全以及网格可观测性等能力,从而极大地减轻开发与运维的工作负担。
整个服务网格可以划分为包括核心组件Istiod的“控制面”以及包括了每个服务的Sidecar的“数据面”。对于服务网格来说,其流量治理的核心能力正是来源于数据面为每个服务容器所注入的Sidecar代理;在拦截到服务容器的出入站流量后,Sidecar代理便能根据控制面为其下发的配置信息,实现丰富的自定义流量控制行为(包括版本路由、熔断、故障注入等等)。
为确保Sidecar代行Kubernetes流量控制之职能,为Sidecar代理提供丰富、准确、且及时的配置信息,几乎可以说是必不可少的,这也是当前Sidecar模式下服务网格的主流实现方式。具体来说,这些配置信息包括监听器(listener)、路由(route)、服务发现(cluster)、证书秘钥(secret)等组成部分。而控制面组件的主要任务,就是将网格中的VirtualService、DestinationRule等资源“翻译”成envoy的具体配置,并将配置发送给数据面的每个envoy,我们简称这个过程为“配置推送”。
为什么要优化你的服务网格
正如上文所述,服务网格在提供流量治理、安全、可观测等强大的应用拓展能力的同时,却也在大规模集群的实际应用之中面临一些挑战,主要可以总结为以下两点:
1、随服务规模而上涨的内存占用
在服务网格的Sidecar模式下,要让网格代理业务容器的流量,就势必得为业务容器额外注入Sidecar容器。在服务网格完全代理集群中每个服务的流量的情况下,集群中Sidecar容器与业务容器的比例将会接近 1 : 1(实际上,集群中的工作负载全部注入Sidecar也是一种比较常见的情况,istio社区中的例子也都是以工作负载全部注入Sidecar为前提的)。相比没有使用服务网格的集群,加入服务网格后的情况相当于每个服务都需要更多的CPU以及内存资源。
除去Sidecar容器的规模问题之外,单个Sidecar容器的内存占用问题更加需要考虑。实际上,单个Sidecar容器的内存占用是随着集群的规模而不断增长的。这主要是由于每个Sidecar都拥有集群中所有服务的监听、路由、端点等信息,以便对集群中访问任意服务的流量都能够实现代理。
依据上述两种情况,我们能够得出:
-
集群中Sidecar容器的数量随着集群中服务端点的数量增长而呈现正比线性增长
-
每个Sidecar容器保存的服务相关配置信息大小随着集群中服务及服务端点的数量也呈现近似的线性增长
因此,集群中Sidecar容器所占用的资源总量(尤其是内存总量),随着集群中服务端点的增长,将近似以二次幂数量级增长。当服务端点增长至一定数量级时,这些Sidecar容器的资源占用将不可忽视。
2、随服务规模而上涨的配置推送压力
上文中提到,每个Sidecar都拥有集群中所有服务的监听、路由、端点等信息,这里就牵扯出这些配置信息的维护问题。事实上,每个Sidecar中的配置信息都是由服务网格的控制面进行统一维护的。这意味着控制面组件需要时刻监听集群与服务网格中与服务访问相关的任何变更,并向所有Sidecar进行配置推送、更新其中的配置,以保证Sidecar的代理行为一直行之有效。例如:
-
集群中某个服务的端点信息发生了变化(如进行了服务扩缩容)。此时,控制面需要将新的端点信息通过EDS协议向所有Sidecar代理进行同步。
-
用户修改了服务网格中的虚拟服务(VirtualService)资源、意图修改访问某个服务的路由规则。此时,控制面需要将新的路由配置通过RDS协议向所有Sidecar代理进行同步。
注意到在上述两个例子中,无论是何种变更引起的配置推送,控制面都需要向所有的Sidecar代理进行同步(而Sidecar代理的数量是随着服务规模的增长而不断增长的!)。当前,随着服务网格技术的发展与成熟,在向每个具体的Sidecar代理进行推送时,控制面已经不需要全量推送覆盖Sidecar中的配置,而只需要将增量部分进行推送即可,然而在大规模集群中,每次变更仍然会为控制面带来不小的推送压力。在一次大规模的变更中(如,一次性部署大量服务),很可能由于服务网格控制面的压力而发生以下可能的现象(包括但不限于):
-
由于控制面必须不断向所有Sidecar代理同步配置信息,流量路由等配置可能生效甚慢,因为配置推送需要一定时间才能完成。
-
控制面组件在面临过大的配置推送压力时,可能出现OOM等问题,导致组件重启,部分服务的Sidecar无法正常启动、进而导致服务部署失败。
正因为有着上述的一系列问题,在服务网格的实际应用中,配置推送的优化才更显重要。有关配置推送的更多细节,可以继续参考你的Sleep服务会梦到服务网格外的bookinfo吗,这篇文章同样针对配置推送优化问题进行了分析。
Overview
在你的Sleep服务会梦到服务网格外的bookinfo吗中,我们提到可以使用选择性服务发现的能力来限制服务网格的服务发现范围,从而在一定程度上控制服务网格所管理的服务规模,减少Sidecar中存储的配置信息以及控制面的推送频率。
然而,使用选择性服务发现的前提是:用户仅需要服务网格管理集群中的一部分服务。在这个前提下,用户才可以以命名空间为单位,手动为服务网格划定服务发现范围。如果用户确实需要服务网格管理集群中的所有服务,则选择性服务发现将无法为配置推送的优化提供任何帮助。
使用Sidecar资源进行配置推送优化
Sidecar资源并不是至今为止我们一直在讨论的“Sidecar代理”,而是一种kubernetes自定义资源(Custom Resource,CR)。服务网格通过VirtualService、DestinationRule、Gateway等自定义资源来帮助用户实现网格功能的配置,而Sidecar资源也是这些服务网格自定义资源的其中之一。
在社区服务网格istio的官方文档中,对Sidecar资源的简述如下:
Sidecar describes the configuration of the sidecar proxy that mediates inbound and outbound communication to the workload instance it is attached to. By default, Istio will program all sidecar proxies in the mesh with the necessary configuration required to reach every workload instance in the mesh, as well as accept traffic on all the ports associated with the workload. The Sidecar configuration provides a way to fine tune the set of ports, protocols that the proxy will accept when forwarding traffic to and from the workload. In addition, it is possible to restrict the set of services that the proxy can reach when forwarding outbound traffic from workload instances.
简单来说,Sidecar资源可以针对每个Sidecar代理,定制其在拦截流量时的具体行为配置:
-
针对入站流量,可以自定义Sidecar代理监听的端口与接受的协议等配置
-
针对出站流量,主要功能是可以限制Sidecar代理在转发出站流量时可以访问的服务
例如,我们可以应用类似以下的Sidecar资源:
apiVersion: networking.istio.io/v1beta1
kind: Sidecar
metadata:
name: ratings
spec:
workloadSelector:
labels:
app: ratings
egress:
- hosts:
- "default/reviews.default.svc.cluster.local"
- "istio-system/*"
这个Sidecar资源通过工作负载标签选择器(workloadSelector)划定了其生效的Sidecar代理,并限制这些代理在转发出站流量时,仅保留针对istio-system下所有服务以及default命名空间下的reviews服务的转发配置。
为什么Sidecar资源可以用于网格配置推送优化呢?我们回到刚才论述的背景信息中来:
每个Sidecar都拥有集群中所有服务的监听、路由、端点等信息
进一步思考,为什么Sidecar必须保存所有服务相关的配置信息呢?其实,主要原因就在于服务网格无法确定服务之间的依赖关系。在不知晓服务之间依赖关系的情况下,只能假定网格中的每个工作负载都有可能访问集群中的任何一个服务,从而导致了Sidecar必须保留全量的服务相关配置。
反过来想,只要知晓了服务之间的依赖关系,是否就可以在很大程度上精简掉Sidecar代理中必须保留的配置信息呢?毕竟,虽然集群中的服务众多,但具体到每个服务上,单个服务所依赖的其他服务的数量往往是有限的,理论上来说,Sidecar代理只需要保留依赖的这些服务的相关信息就好了。
Sidecar资源则刚好可以帮助实现上述构想。我们刚才提到,Sidecar资源可以帮助限制Sidecar代理在转发出站流量时可以访问的服务。实际上,Sidecar资源在限制工作负载的服务访问范围的同时,也精简了对应Sidecar代理中无关服务的配置信息。在应用了Sidecar资源后,由于Sidecar代理中仅保存了工作负载需要访问的服务相关信息,单个Sidecar代理内部的配置信息就被大大简化了,内存占用也将随之降低。
同时,控制面也将选择性地对Sidecar代理进行配置推送,从而使得配置推送所需的频率也大大降低。例如,在使用Sidecar资源声明工作负载a仅依赖服务b的情况下,若服务c的端点信息发生变化,则控制面将无需将相关信息推送到工作负责a的Sidecar代理,因为a对c“不感兴趣”。通过这种方法,服务网格能够大大减少无意义的配置推送行为,进而对推送延迟、控制面组件OOM等问题进行有效的优化。
在如此的背景之下,针对每个工作负载定制Sidecar资源、将整个服务网格的配置推送调整至最优,也就正是题中应有之义了。
手动使用Sidecar资源优化所面临的挑战
Sidecar资源虽然能够大幅精简Sidecar代理中的配置信息,但手动配置Sidecar资源仍然面临着许多挑战:
首先,每个服务所依赖的其他服务都各不相同,而这个信息是与用户应用的拓扑结构相关的;用户若选择手动应用Sidecar资源,必须清楚地了解服务之间完整的依赖关系,并针对每个服务的Sidecar代理进行单独定制,这一行为需要花费很大的代价。
此外,集群中服务之间的依赖关系并不能保证一直不变,在服务依赖关系产生变化的同时,用户必须及时更新Sidecar资源中定义的依赖信息,否则可能导致部分服务网格提供的功能不可用,这无疑也提高了Sidecar资源的使用门槛。
在如何使用基于访问日志分析自动推荐的Sidecar资源中,阿里云服务网格ASM提供了基于访问日志分析自动推荐Sidecar资源的能力,以帮助客户快速针对应用中的不同服务定制Sidecar资源,然而当服务依赖关系发生变化时,用户仍然需要时刻注意这一变化,并及时进行Sidecar资源的更新。
利用ASM自适应配置推送优化自动生成Sidecar资源
ASM通过自适应配置推送优化来克服这一问题,该能力能够时刻监听集群中服务的增减、以及服务之间的访问信息,并自适应地为选中的服务生成优化的Sidecar配置,在服务间依赖关系发生改变时,通过该能力生成的Sidecar资源也将自动地发生变更,从而能够高效地利用好Sidecar资源这一“优化利器”。
自适应配置推送优化主要通过控制面中的控制器、访问日志分析服务以及数据面中部署的出口网关组成:
整套自适应配置推送优化的系统基本流程如下:
-
控制器将监听集群中的服务以及服务端点,当新服务创建 / 老服务被删除时,如果用户通过注解选中了该服务,则将对应 创建 / 删除 作用于该服务工作负载的Sidecar资源与EnvoyFilter资源,其中:
-
Sidecar资源最初将仅包含系统命名空间(kube-system、istio-system等)中的服务,工作负载最初将不能直接访问其依赖的其它服务。
-
最初,工作负载虽不能直接访问其依赖的其它服务,但可以将所有请求交给出口网关代理,由出口网关负责将请求转发至目的地。EnvoyFilter是服务网格的另一种自定义资源,负责为工作负载访问其它服务时加入一条默认路由项,将所有请求导向出口网关。
-
由于上述的Sidecar资源与EnvoyFilter的共同作用,在初始状态下,所有服务间的相互访问请求都会经过出口网关。此时,出口网关可以生成请求的访问日志,并将访问日志上报给访问日志分析服务。
-
访问日志分析服务将分析访问日志中包含的服务依赖信息,并将该信息同步给控制器。
-
控制器在得到新的依赖关系(服务A依赖服务B)后,将更新其下发的Sidecar资源与EnvoyFilter资源:
-
Sidecar资源得到更新,记录的依赖服务将增加服务B,此时,服务网格将会为服务A的Sidecar代理下发服务B的端点/路由/监听等信息。
-
EnvoyFilter资源得到更新,不再默认将访问 服务B 的请求导向出口网关。
-
此时,针对 服务A 访问 服务B 的自适应配置推送优化已经完成,依据访问日志分析得出的服务依赖关系,作用于服务A的Sidecar资源自动得到了更新,使整个服务网格的配置推送达到最优状态。由于出口网关仅在服务依赖关系分析结果得出之前负责代理请求,因此出口网关实际并不会面临过大的压力。
-
如果服务间的依赖关系产生了变化,例如图中:服务A 新依赖了 服务C,则依然重复以上流程,最终作用于服务A的Sidecar资源中也将自适应地添加依赖 服务C。
注意:由于出口网关代理集群中的请求时,需要根据7层协议中的信息确定请求的转发目的地,因此,自适应配置推送优化目前仅适用于使用HTTP协议互相访问的服务。