开发者学堂课程【2022云原生编程挑战赛培训课程:【视频】服务网格赛题解析】学习笔记,与课程紧密连接,让用户快速学习知识。
课程地址:https://developer.aliyun.com/learning/course/1049/detail/15335
【视频】服务网格赛题解析
内容介绍:
一、赛题背景
二、赛题解析
三、总结
一、赛题背景
介绍2022年云原生编程挑战赛服务网格赛题的解析,题目是针对Sidecar模式下的服务网格数据面应用服务访问QPS和延时的优化,服务网格的概念也比较新,先从赛题的背景开始聊起。
1.从容器化到服务网格
传统的服务部署方式:
很难控制应用程序环境一致性
首先想象一个服务部署的场景,比如要部署一个HTTP服务,一般在传统的服务部署方式,会有一物理或虚拟机,机器肯定会有一个系统内核,在系统内核之上,是应用程序所依赖的各种各种环境变量,软件依赖以及数据依赖等些依赖项,最上层是编写的一个应用程序,它通过监听一个端口对外去提供服务,所谓的提供服务就是能够响应比如HTTP的请求,在传统的服务部署方式下,有一个很大的问题,很难控制应用程序环境的一致性,比如想要扩容应用程序,想要在三台机器上部署,很难保证三台机器上的环境变量,软件依赖,这些依赖信息都是和第一台机器完全一样的。
2.容器化:
容器作为云原生应用的交付物,既解决了环境一致性的问题,又可以更细粒度的限制应用资源。
为了解决样的一问题,云原生使用了容器作为云原生应用的交付物,可以参见上面示意图,可以简单理解成容器,就是一个应用程序,以及它的各种环境依赖全部打包在一起,即使到了另外一台机器上,也可以做到直接警报、开箱即用,作为云原生应用的交付容器,既能够解决环境一致性问题,又能够以,更细腻的限制应用的资源。
Kubernetes 作为一种容器编排调度工具,解决了分布式应用程序的部署和调度问题。
刚才提到的是容器化的部署,容器化的部署只是云原生的第一步,在实际的应用场景中,肯定不可能只是在一台机器上部署一个容器,实际上更多的场景是会有多个物理或者虚机组成的样的一个集群,在集群上,会有各种各样的服务,会部署很多的容器,这时就需要一个编排系统将不同的容器合理的部署到不同的机器上,并且让他们对外提供一致的服务。Kubernetes是样的一种容器编排调度的工具,它够解决分布式应用程序的部署和调度问题。Kubernetes的使用方式,实际上就像上图所示,Kubernetes会有一些自己的资源,比如deployment、service等等,用户通过编写资源的YAML文件,并且把它用到Kubernetes集群中,Kubernetes就自动化的帮助用户将容器调度和部署到对应的机器上,并且对外提供服务。
3.流量管理:
微服务架构下,Kubernetes集群中往往存在大量的服务,服务之间存在着复杂的相互调用关系
刚才提到的主要是部署方面的问题,接下来回到服务网格所关注的正题上。服务网格所关注的主要问题实际上并不是服务的部署,而是服务之间的流量的管理,在微服务的架构下,经常是Kubernetes集群中存在着大量的服务,而服务之间会存在复杂的调用关系,如上图所示,这些调用关系形成了一条条很长的调用链,在运维这些微服务的过程中,会存在很多管理这样的流量需求,需要很多经济化的流量管理能力,比如想要一个熔断,熔断请求超过一定量之后,就要拒绝请求,拒绝请求对应用程序进行保护,另外可能还会做灰度发布,比如服务有V1和V2两个版本,想要将一部分的请求路由到V2的版本上,而剩下的请求还是路由到V1的版本上,对服务进行灰度这样的发布和测试,另外还有重定向等。总之,在微服务的场景下,会需要很多样流量治理的能力,在服务网格技术出现之前,实际上是通过在应用程序中集成SDK,用编程的方式来管理应用程序的出像和出向和入向。
流量管理:
在服务网格技术出现之前,可以通过在应用程序中集成SDK、编程的方式来管理应用程序的出向/入向流量。
使用SDK管理流量的缺点:
·有编程语言限制,无法支持集群中不同语言写成的服务
·SDK升级时,需要修改代码、重新上线,增加运维负担
如图所示,SDK是耳熟能详的spring cloud,有可能是Netflix OSS等等,通过用户代码直接去调用SDK,在代码里面对应用程序的流量进行管理,使用SDK管理流量缺点在于,它是有编程语言的限制,因为SDK是针对具体语言的,比如如果集群中部署的有Java的应用、有Java的服务,也有note JS等什么语言的都有时,SDK肯定无法支持所有的多语言服务,无法支持及不同语言写成服务的一个场景。另外,SDK升级时,由于流量治理逻辑都是在代码里面,需要开发人员修改代码进行重新上线,无疑是增加运维的负担。
这时就出现了服务网格的技术,帮助进行流量的管理。服务网格的技术,能够使得流量管理变得对应用程序透明,也就是应用程序不需要在自身逻辑中集成流量管理这样的一个逻辑。这部分功能,实际上从应用程序中就转移到一个平台层,从而使服务网格成为一个云原生的基础设施。服务网格的代表就是开源项目Istio,服务网格Istio它的核心模式是使用sidecar记入iptables技术实现流量的拦截,sidecar就是某服务网格如上图所示,会为每一个用户的业务容器,在它旁边注入一个多余的代理容器,由于每容器旁边都带着一个,就像黄金模特的编车一样,所以称为代理容器sidecar,sidecar通过iptables技术实现对用户应用程序所有流量的拦截,从而可以处理所有应用的出入口流量,来实现流量管理观测和加密等一些流量管理的能力。
4.服务网格流量管理基本概念
服务网格Istio为每个业务容器注入了一个Sidecar代理,该Sidecar代理是一个Envoy应用的容器﹔并通过设定iptables规则拦截所有业务容器的入向和出向流量。
这样,每个Pod中就多出了一个Envoy容器( Sidecar ) ,用于提供服务网格的非侵入式流量管理能力。
Pod : 一个Pod是一个或一组一起部署的容器,是可以在Kubernetes集群中创建和管理的最小部署单位
原先Kubernetes集群中只有业务容器一个,在接入了服务网格平台之后,相当于服务网格会在每pod中给业务容器都注入一个代理容器,来提供流量管理的能力,所以称为sidecar
上图是一个总架构图,通过为每个应用注入Sidecar,服务网格能够感知和控制集群中每个服务的入向和出向流量,进而能够通过配置不同的规则,在Sidecar层面实现流量转发负载均衡、流量灰度、流量加密等能力
对于来自集群外的请求,Istio则会单独部署一个Envoy容器在集群中、作为网关使用,进而实现对所有的流量都具有拦截和治理的能力。
5.网格性能与资源占用优化
服务网格通过将SDK中实现的流量管理功能,全部抽离到sidecar容器之中,能够极大的降低容器化应用的运维成本,但是,目前引入网格技术也会带来性能损失和资源占用增加的缺点
性能损失
服务网格中的sidecar是使用iptables技术对出入口请求进行拦截,iptables技术固有会造成大量性能损失,对一些性能要求高的场景有明显的影响。sidecar拦截无疑导致了调用链度的增长,可以想象集群中如果没有服网格的sidecar,原先要调用图中pod1的业务容器提供的服务,集群直接调用就可以,服务网格向集群中注入了网关以及sidecar,如果再调用pod1的服务,会发现首先发送到网关,网关发送到sidecar,sidecar发送到业务容器,增加了很多调用链,拦截也是导致网格性能的原因之一。sidecar的主要功能是实现流量的管理,需要sidecar处理和转发所有的请求,处理和转发请求需要消耗时间和CPU以及内存资源,以上的三点原因是造成服务网格性能损失的主要原因
服务网格有一个控制面的,由于服务网格中每个业务容器都有一sidecar,在配置一些流量管理的规则时,需要有一个组件集中的对这些sidecar或者网关中的Envoy进行配置和管理,服务网格为什么有控制面的原因,控制面相当于是一个所有sidecar集群的控制中心,控制面默认时会去监听和处理Kubernetes集群中所有空间中的所有资源,如何资源发生变化时,要及时的将变化以配置的形式推送给所有的sidecar,所有的行为全部都是全量的,因此对于有一定规模的集群,在服务发现配置推送的方面,控制面也会对服务网格的性能造成一定的影响。
如果服务网格是有增强集群中应用程序安全的能力,在默认情况下,如果什么都不配置,sidecar之间默认是会使用mTLS的加密技术实现加密通讯,使用MmTLS实现的加密隧道,无疑增加了微服务到微服务延时时间,因为加密的过程同样也是需要消耗一定的资源以及使用一定的时间。
资源占用
原先Kubernetes集群中,比如图中环境,只有两个pod,每pod有一个容器,相当于集群中总共只有两个容器,在服务网格注入sidecar以及网关之后,多了三个Envoy容器,每个Envoy容器都会消耗以集群中一定的CPU和内存的资源,是不可避免的,因为它是一容器,避免要消耗资源来维持自身的运行,当集群中服务规模持续增长时,Envoy容器的规模会达到非常恐怖的地步,时资源消耗也会造成不可忽视的影响,引出了我们赛题的关注点,核心是进行网格性能和资源占用的双重优化。
赛题目标:
在赛题中,会模拟一个资源有限的kubernetes集群的环境,希望能够通过很多的优化手段,让参赛者能够在资源有限的环境下尽可能的去提高网格应用访问的性能表现,具体是两个评分指标,第一点是要提高应用的性能表现,包括调用网格中服务的QPS有多高,以及平均延时有多短,第二点是在维持较高的应用性能表现的情况下,还要尽可能的降低sidecar代理所占用的资源,具体包括集群中的CPU和内存的资源,为参赛者提供了三比较行之有效的优化方向,包括为每个sidecar代理动态的分配CPU和内存资源,对网格本身的应用优化进行配置,以及开启基于Multi-Buffer特性的TLS优化手段,为参赛者提供出来,让参赛者在些优化方向上进行发力,从而达成优化的效果。
二、赛题解析
1.概览
赛题方为参赛者们提供了三个化的角度作为优化入手点,三个角度彼此之间是比较独立,鼓励参赛者可以去积极的选择不同的角度来实现不同的优化手段,达到最大的优化效果。具体来包括三个优化方向,第一是来分配sidecar的资源上限,第二是sidecar的配置调优,第三是平台特性调优。
分配 sidecar 的资源上限
其主旨就在于动态的分配每个sidecar使用的CPU和内存的上限,方向其实比较好理解,如上图,是服务网格架构的比较简易的示意图,在服务网格中,每一个pod的旁边都会有sidecar容器,容器会消耗一定的CPU和内存的资源,服务网格实际上是有一个能力的,可以选择为每个服务的sidecar分配资源的上限,使用的资源是无法超过分配的上限,比如给pod1的sidecar分配了最大使用0.5核的CPU以及256兆的内存,而pod2的sidecar最大可以使用1核的CPU和128兆的内存,sidecar使用的资源总量就会通过kubernetes机制,它无法超过设置的资源上限,可以比较直观的想象到,sidecar请求的处理性能实际上是会对网格中应用的性能造成比较大的影响,尤其是在sidecar处理性能比较低时,就算业务容器处理性能再强大,处理再快,但如果sidecar转发性比较弱,可能一秒发一条,性能再强也没有用。所以当sidecar性能比较弱时,实际上服务网格中服务的性能会比较明显的受制于sidecar本身的性能。
而sidecar性能,又是和为其分配的CPU和内存资源量的上限是成正比的,也就是为其分配的CPU和内存越多,理论上sidecar处理和转发请求就越快。但是,不能盲目的为sidecar分配很多的资源,因为盲目为sidecar分配大量的资源,会迅速的将集群内的资源总量耗尽,最终导致pod无法创建。集群实际上是基于一系列的虚拟或者是物理机器,集群中的机器是有一资源总量的上限的,一共就这么多CPU、核数以及内存,分完就没有了,如果分完还想创建时,pod可能就创建不出来,导致整集群的服务发生崩溃。希望基于上述前提设计一个合理为每个sidecar代理分配资源的算法,是完成本赛题的重要手段之一。
具体参赛者程序的输入输出,评测实际上是分多轮进行的,在每一轮,都会告知参赛者程序,当前集群中剩余还可分配给sidecar的CPU和内存资源的总量是多少,以及sidecar产生的访问日志和每个容器的CPU和内存资源平均用量的指标,这些数据提供给参赛者的程序作为数据上的反馈,参赛者的程序是需要实现一个算法,来计算并返回分配给每个服务的sidecar的CPU和内存资源的上限,评测环境中会使用一个资源相对比较有限的Kubernetes集群,在上面安装好服务网格的环境来作为评测环境,同时评测是分为多轮进行,参赛者的程序,只需要关注于资源分配算法本身即可,具体的资源分配方式以及运行压力测试等,都是由赛题方接管。
解题思路
第一点,希望能够让大家提出一个有效的去建模sidecar资源量和性能关系的方法,sidecar分配的资源越多,sidecar的性能就会越高,但是在为sidecar分配的资源上限达到一定程度的时候,性能优化会呈现边际效应,现在服务网格内部的结构,实际上是用sidecar去拦截所有发往业务容器的出向和入向的流量,会出现短板效应,在sidecar性能非常低的情况下,业务容器属于是有劲不出,但是反过来,如果是业务容器是业务性能瓶颈的话,给sidecar分配了很多资源,反而业务容器变成了性能瓶颈,给sidecar分配再多的资源,转化的再快,也没有用,业务逻辑处理不过来,所以会呈现边际效应。资源总量是存在上限的,要去寻找性价比最高的分配方式,给大家提供的数据就能派上用场,给大家提供了丰富可观测数据,包括在测试中产生的每个容器实际的性能指标,以及使用的资源到底有多少的指标,另外还会有访问日志提供给大家。参赛者需要根据提供的些信息来实现有效的算法,给每个服务的sidecar去分配CPU和内存,最终的目标希望算法既能有效的提高网格内应用的性能,又不会浪费太多的资源,实现样的决策策略。
第二点,做一个调整资源分配的结算算法,因为赛题的评测是分为多进行的,它的实际上的流程图是如上图所示,开始评测之后,在集群中对服务进行压力测试,即向服务集群中的服务频繁的发送请求,压力测试完事之后,会收集段时间内sidecar所产生的访问日志以及各容器的资源使用指标,向参赛者的程序反馈可观测数据的指标,获取到些数据之后,参赛者程序需要给出一个输出,即用算法来给出一角色策略,为每一个服务的sidecar分配具体的资源,获取到参赛者程序输出之后,根据参赛者程序输出具体的为每一个sidecar实际的分配资源的上线,再根据参赛者程序的实际分配,分配好之后再次运行压力测试,整评测过程就是这样的一个循环。鼓励参赛者根据收到反馈,来构建用尽可能少的人数,达到最大优化的目标的迭代算法。因为参赛者程序一开始不知道程序评测环境中服务是什么样的,每个服务处理请求的性能是多大都是不知道的,参赛者程序需要在一轮一轮的迭代中,尽快的利用每一次的反馈数据来迭代自身,从而让自己策略迅速的达到最优,这是需要关注的点。
最后一点提醒大家有效的利用所有的输入数据,服务网格会向参赛者程序反馈很多可观测的数据,主要是包括容器的CPU和内存使用的指标,会为参赛者程序反馈实际的压力测试的过程中,每个容器使用的CPU有多少,内存有多少,以及访问日志。
如图所示是访问日志,实际上是sidecar容器所产生的日志,由于我sidecar容器拦截了所有的请求,所以在测试中产生的每条请求都能够产生对应的访问日志,是一个非常宝贵的数据来源。访问日志每一条都是一个json的字符串,其中包含了很多丰富的信息,图中访问示例,字符串中包含了请求的目的地,请求延时,请求的大小,请求来源等等,还有很多其他的请求协议之类的,可以去翻一下访问日志的文档,以及赛题对访问日志的说明,充分的利用好数据。
sidecar 配置调优
实际上希望使用sidecar和Envoy filter两种资源直接修改sidecar代理的配置内容。需要先了解一些相关的背景,每个sidecar代理,都是一个Envoy的应用,Envoy有一个能力,可以通过xDS协议和服务网格的控制面进行交互,动态变成自身的配置,
上图代理的配置,sidecar内部的Envoy代理的配置,实际上是这些配置具体的控制了sidecar容器对流量的管理行为,所以配置是非常重要的。
控制面实际上起到了一翻译官的职能,服务网格为用户提供了virtual service、DestinationRule等一些自定义的资源,用户可以用YAML写成的一些规则和配置。控制面收到配置之后,会对它进行翻译将它转译成具体的代理的配置,因为代理的配置非常的反人类,所以控制面起到了翻译官的职能将Envoy的自定义资源写成YAML,翻译成具体的Envoy代理配置,再将Envoy代理的配置向所有的sidecar代理容器进行推送,就是配置推送。
本赛题中优化方向实际上是服务网格中有sidecar和Envoy Filter两种自定义资源。希望大家能够积极的利用两种自定义资源发挥创造性,提供一些能够降低服务网格资源消耗和性能提高的有效的配置。
两种自定义资源,首先是sidecar资源,sidecar资源不是之前一直在谈的sidecar容器,sidecar资源实际上是服务网格的一种提供的一种自定义资源,也就是yaml配置文件。type是sidecar,它的作用是能够声明每个服务的sidecar代理的出入项流量的属性,通过编写sidecar资源,网格能够根据sidecar资源配置中描述的流量属性,对服务信息以及相关配置进行选择性的下发,在默认的情况下,实际上服务网格会监控Kubernetea集群中的所有服务,并将所有服务的信息都写入到每个sidecar的配置当中。
这里其实存在一问题,每个sidecar实际上不需要了解集群中所有的服务都在哪,只需要了解调用的服务在哪和被调用服务在哪,这时候下发全量的配置,实际上是比较冗余的,而且会造成一问题,sidecar容器把些配置缓存在自己的内存里面的时候,就会消耗一定的内存资源,因此编写sidecar就能够精简,通过精简些配置,就能够具有一个减少sidecar代理内存占用以及优化配置推送过程中的资源占用的样的一功效。上图实例,当前命名空间default下的所有的服务,它的流量都只可能发往本命名空间,也是default命名空间以及istio-system命名空间下的服务,Default命名空间下服务的配置内容就不会再包含两个命名空间以外的服务的一些内容,从而能够精简一定数量的配置。鼓励积极的考虑利用sidecar资源尽可能的减少,精简sidecar容器内部的一些配置内容。
另外一点Envoy filter,Envoy filter实际上是一个自由度非常高的机制,它的核心是编写原始的Envoy应用配置后来直接合并到现有的Envoy应用的配置之中,可发挥的空间就非常大,实际上是要求开发者们直接将原始的Envoy配置写入到sidecar代理之中,可以起到很多的作用,如上图,它可以修改一些HTTP协议过滤器的参数,删除具体sidecar中的某些配置,还可以增加一些日志的字段等,上图Envoy filter内容,实际上就起到了增加访问日志字段的作用,可想象的空间是非常大的,不仅可以利用Envoy filter想办法优化一些转发过程中些资源的消耗,以及增加性能,也可以为参赛者的程序提供一些更加丰富的信息,为自己在其他优化方向上的一些决策做一些帮助比如增加日志字段的功能,可以为参赛者程序提供更加丰富的信息。
注意事项
本赛题测评环境中使用的服务网格是阿里云服务网格ASM,它是兼容社区服务网格Istio1.12版本的实现,对应的是Envoy v3的API,务必注意保证版本兼容性,需要提供能够兼容1.12版本istio的sidecar或者Envoy filter的YAML才可以。建议根据Istio官方网站指引自行安装1.12版本的Istio,或者在自己的环境下先行测试。如果提供的sidecar或者Envoy filter内容有问题,可能会导致sidecar运行不起来,sidecar容器崩溃,会得到惨见的零分,浪费你宝贵的提交机会,所以建议大家还是先行测试一下,在自己的测试环境中
平台特性调优
实际上是使用英特尔Multi-Buffer技术来优化sidecar代理的加密通信速度。背景实际上是TLS已经是一个网络通信的基石,在服务网络中默认开启了TLS通信,也就是sidecar和sidecar之间通信都是加密的。在微服务的场景下,无论Envoy是作为网关还是微服务的代理,都需要处理大量的样的TLS的请求,尤其是在握手的阶段执行非对称加解密操作时,需要消耗大量的CPU资源。那英特尔Multi-Buffer技术,本质是使用AVX-512的指令来同时处理多独立的缓冲区,可以在一执行周期内,同时执行多个加解密的操作,加解密的执行效率就会得到成倍的提升。在评测环境中所使用的阿里云服务网格ASM中,已经集成封装的开启英特尔Multi-Buffer加解密技术能力,而且使用的机器均为支持AVX-512指令的Intel Ice Lake机型。
参赛者需要做的事情其实比较简单,只需要传达给测试程序开启特性,就可以立即对服务网络进行优化,所鼓励大家积极利用的。
三、总结
本次赛题解析主要是两方面,第一点是从赛题背景上从服务部署聊到Kubernetes,又从流量管理到服务网的基本概念,以及服务网的优缺点,引出了赛题主旨,实际上做sidecar代理的性能和资源占用的优化。在赛题解析方面,准备了三种不同的优化方向,包括分配sidecar资源上线、sidecar配置调优以及平台特定调优三种具体的方向,三种方向是比较独立的,可以积极尝试所有的优化方向,具体的评测方式和输入输出的格式不给赘述,可以参见赛题说明以及赛题示例参赛者程序,在赛题中提供的 service_match_optimizer 参考程序。