摘要: 提到Envoy就不得不提Service Mesh,说到Service Mesh就必定要谈及微服务了,那么咱们就先放下Envoy,简单了解下微服务、Service Mesh以及Envoy在Service Mesh中处于一个什么样的角色。html
最近因工做缘由开始了解Service Mesh与Envoy,为系统性梳理所学内容,所以沉淀了此文档,但因为所知有限,如文档中有描述不当之处,但愿不吝赐教。nginx
提到Envoy就不得不提Service Mesh,说到Service Mesh就必定要谈及微服务了,那么咱们就先放下Envoy,简单了解下微服务、Service Mesh以及Envoy在Service Mesh中处于一个什么样的角色。git
过去几年间,架构领域最火的方向非微服务莫属,那么微服务架构到底为咱们带来了什么样的好处呢?下面经过一张图说明架构的演进,以下:
github
伴随着业务规模的变大,微服务的好处显而易见,例如它自己所具有的可扩展性、易维护性、故障和资源隔离性等诸多特性使得产品的生产研发效率大大提升,同时,基于微服务架构设计,研发人员能够构建出原生对于“云”具有超高友好度的系统,让产品的持续集成与发布变得更为便捷。算法
然而没有所谓的银弹,微服务带来不少好处的同时也引入了不少问题。在云原生模型里,一个应用能够由数百个服务组成,每一个服务可能有数千个实例,每一个实例的状态可能持续的发生变化,此时,服务间的通讯不只异常复杂,并且都是运行时的行为,管理好服务间通讯对于保证端到端的性能与可靠性来讲无疑成为重中之重。在Service Mesh没有出现以前,微服务框架之间的通信大多采用SDK方案,但该方式短板也很是明显,例如对业务有侵入性、没法作到SDK升级对业务透明等。编程
基于以上种种复杂缘由催生了服务间通信层的出现,这个层即不该该与应用程序的代码耦合,又能捕获到底层环境的动态变化并做出适当的调整,避免业务出现单点故障;同时也可让开发者只关注自身业务,将应用云化后带来的诸多问题以不侵入业务代码的方式提供给开发者。后端
上述所说的这个服务间通信层就是Service Mesh(国内一般翻译为服务网格),它能够提供安全、快速、可靠的服务间通信。若是用一句话来解释什么是Service Mesh,能够将其比做微服务间的TCP/IP层,负责服务之间的调用、限流、熔断和监控等。缓存
读到这里你们必定仍然存在这样的疑惑,Service Mesh究竟是什么呢?这是一个全新的东西吗?它的演进过程是什么样的呢?下面使用一张图来讲明其演进过程,以下:
从上图能够看到最初的Service Mesh始于一个网络代理,在2016年1月业界第一个开源项目Linkerd发布,同年9 月 29 日的 SF Microservices 大会上,“Service Mesh”这个词汇第一次在公开场合被使用,随后Envoy也发布了本身的开源版本,但此时的Service Mesh更多停留在Sidecar层面,并无清晰的Sidecar管理面,所以属于Service Mesh的第一代。此时虽然Service Mesh尚不成熟,但一个初具雏形的服务间通信层已然出现,以下图:
随后Google联合IBM、Lyft发起了Istio项目,从架构层面明确了数据平面、控制平面,并经过集中式的控制平面概念进一步强化了Service Mesh的价值,再加上巨头背书的缘故,所以Service Mesh、Istio概念迅速火爆起来。此时已然进入到了第二代的Service Mesh,控制平面的概念及做用被你们承认并接受,而更重要的一点是至此已经造成了一个完整意义上的SDN服务通信层。此时的Service Mesh架构以下图:
至此Service Mesh的背景信息基本介绍完毕,接下来开始进入正题说说Envoy相关的内容。其在完整的Service Mesh体系中处于一个什么位置呢?继续看图:安全
Envoy是Istio中的Sidecar官方标配,是一个面向服务架构的高性能网络代理,由C++语言实现,拥有强大的定制化能力,经过其提供的Filter机制基本能够对请求转发过程当中超过50%的流程作定制化,在性能方面因为其实现参考了Nginx,也处于主流水平,固然还有不少特性,在这里就不作一一介绍了。性能优化
任何软件架构设计,其核心都是围绕数据展开的,基本上如何定义数据结构就决定了其流程的走向,剩下的不外乎加上一些设计手法,抽离出变与不变的部分,不变的部分最终会转化为程序的主流程,基本固化,变的部分尽可能保证拥有良好的扩展性、易维护性,最终会转化为主流程中各个抽象的流程节点。
对于Envoy也不例外,做为一个网络代理程序,其核心职责就是完成请求的转发,在转发的过程当中人们又但愿能够对其作必定程度的微处理,例如附加一个Header属性等,不然就不必使用代理程序了。那么Envoy是如何运做的呢?它是如何定义其数据结构,并围绕该数据结构设计软件架构、程序流程,又是如何抽象出变得部分,保证高扩展性呢?
带着这些疑问,试想Envoy做为一个高度可定制化的程序,其定制化的载体必然是配置信息,那么咱们下面就试着从Envoy的一份配置来解读其架构设计与程序流程。
在查看其配置前,咱们不妨先脑补一下网络代理程序的流程,好比做为一个代理,首先要能获取请求流量,一般是采用监听端口的方式实现,其次拿到请求数据后须要对其作些微处理,例如附加Header头或校验某个Header字段内容等,这里针对来源数据的层次不一样,就能够分为L3L4L7,而后将请求转发出去,转发这里又能够衍生出若是后端是一个集群,须要从中挑选出一台机器,如何挑选又涉及到负载均衡等。脑补下来大体流程应该就是这个样子,接下来咱们看看Envoy是如何组织其配置信息的。
Envoy配置的简单配置信息以下:
关键字段说明:
Listener: 服务(程序)监听者。就是真正干活的。 Envoy 会暴露一个或者多个listener监听downstream的请求。
Filter: 过滤器。在 Envoy 中指的是一些“可插拔”和可组合的逻辑处理层。是 Envoy 核心逻辑处理单元。
Route_config: 路由规则配置,即请求路由到后端那个集群(cluster)。
Cluster: 服务提供方集群。Envoy 经过服务发现定位集群成员并获取服务。具体请求到哪一个集群成员是由负载均衡策略决定。经过健康检查服务来对集群成员服务状态进行检查。
根据上面咱们脑补的流程,配合上这份配置的话,Envoy大体处理流程以下图:
Envoy内部对请求的处理流程其实跟咱们上面脑补的流程大体相同,即对请求的处理流程基本是不变的,而对于变化的部分,即对请求数据的微处理,所有抽象为Filter,例如对请求的读写是ReadFilter、WriteFilter,对HTTP请求数据的编解码是StreamEncoderFilter、StreamDecoderFilter,对TCP的处理是TcpProxyFilter,其继承自ReadFilter,对HTTP的处理是ConnectionManager,其也是继承自ReadFilter等等,各个Filter最终会组织成一个FilterChain,在收到请求后首先走FilterChain,其次路由到指定集群并作负载均衡获取一个目标地址,而后转发出去。
聊完了基本流程后,本节会试着分析其架构设计,但愿从其架构设计中得到一些益处。
先卖个关子,在本节开始以前咱们不妨先思考一个有趣的问题:Envoy自己采用C++开发的,广泛承认C++程序执行性能会更好,那么延伸下来能够想到Envoy的设计目标彷佛是在追求高性能,那么真是如此吗?
在探究Envoy架构设计以前,咱们先来看看Envoy自身是怎么描述其设计目标的,以下:
Envoy并非很慢(咱们已经花了至关长的时间来优化关键路径)。基于模块化编码,易于测试,而不是性能最优。咱们的观点是,在其余语言或者运行效率低不少的系统中,部署和使用Envoy可以带来很好的运行效率。
很是有意思的表述,Envoy并无把追求极致性能做为目标,那么其架构设计会弱化性能这块吗?
目前业内公认代理程序性能最好的是Nginx,其采用了per thread one eventloop模型,这种架构被业内广泛借鉴,那么Envoy呢?咱们先看看下面的架构图:
看到里面Worker的工做方式是否是很熟悉,会不会有一点点困惑呢?呵呵,没错,Envoy也采用了类Nginx的架构,方式是:多线程 + 非阻塞 + 异步IO(Libevent),虽然Envoy没有把极致性能做为目标,但不等于没有追求,只不过是相对于扩展性而言级别稍微低一点而已。
Envoy的另外一特色是支持配置信息的热更新,其功能由XDS模块完成,XDS是个统称,具体包括ADS(Aggregated Discovery Service)、SDS(Service Discovery Service)、EDS(Endpoint Discovery Service)、CDS(Cluster Discovery Service)、RDS(Route Discovery Service)、LDS(Listener Discovery Service)。XDS模块功能是向Istio的Pilot获取动态配置信息,拉取配置方式分为V1与V2版本,V1采用HTTP,V2采用gRPC。
Envoy还支持热重启,即重启时能够作到无缝衔接,其基本实现原理是:
Envoy一样也支持Lua编写的Filter,不过与Nginx同样,都是工做在HTTP层,具体实现原理都同样,不作赘述了。
到此为止咱们看完了上面的架构图,若是你对其内部实现也有兴趣的话,能够看看下面的内部实现类图:
其内部实现为了灵活性,作了不少抽象封装,但基本上能够拆分为几个大的功能模块,具体如上图,再也不赘述。
软件的世界历来就不存在什么银弹,虽然ServiceMesh优点很明显,甚至被尊称为服务间的通信层,但不能否认的是ServiceMesh的到来确实对应用的性能带来了损耗,能够从两个方面看待此问题:
本节主要谈论Envoy在性能方面的努力及社区在性能方面呼声较高的一些内容。
Envoy做为Sidecar其提供的核心功能能够简单总结为如下三点:
从上述三点中咱们试着分析下性能优化的关键点,其中第一、3点是与业务基本无关的,属于通用型功能,而第2点的性能是与业务复杂度呈现相关性的,好比请求校验规则的多与少、遥测数据的采集精细度、数据统计的维度多样性等,所以最有可能提高Sidecar性能的点就是对请求的拦截与Sidecar之间通信协议的高效性。
针对请求的拦截,目前常规的作法是使用iptables,在部署Sidecar时配置好iptables的拦截规则,当请求来临后iptables会从规则表中从上至下顺序查找匹配规则,若是没遇到匹配的规则,就一条一条往下执行,若是遇到匹配的规则,那就执行本规则并根据本规则的动做(accept, reject, log等),决定下一步执行的状况。为了更直观的展现iptables的执行过程,请看下图:
了解iptables的基本流程后,不难发现其性能瓶颈主要是两点:
既然知道了iptables的缺陷,那么优化手段不外乎从这两点下手,而Linux社区与Envoy社区也正在计划对此作优化,具体以下:
为何规避Linux正常协议处理过程当中内核态与用户态的转换如此重要呢?就以对咱们最直观的内存拷贝为例,正常状况下,一个网络数据包从网卡到应用程序须要通过以下的过程:数据从网卡经过 DMA 等方式传到内核开辟的缓冲区,而后从内核空间拷贝到用户态空间,在 Linux 内核协议栈中,这个耗时操做甚至占到了数据包整个处理流程的 57.1%。为了更直观的对内存拷贝消耗有所了解,画了一张简图,以下:
DPDK全称Intel Data Plane Development Kit,是Intel提供的数据平面开发工具集,为Intel Architecture(IA)处理器架构下用户空间高效的数据包处理提供库函数和驱动的支持,它不一样于Linux系统以通用性设计为目的,而是专一于网络应用中数据包的高性能处理,它将数据包处理、内存管理、处理器调度等任务转移到用户空间完成,而内核仅仅负责部分控制指令的处理。这样就解决了处理数据包时的系统中断、上下文切换、系统调用、系统调度等问题。
VPP是the vector packet processor的简称,是一套基于DPDK的网络帧处理解决方案,是一个可扩展框架,提供开箱即用的交换机/路由器功能。是Linux基金会下开源项目FD.io的一个子项目,由思科贡献的开源版本,目前是FD.io的最核心的项目。
整个DPDK仍是很是复杂的,经过一两篇文章很难说清楚,且本文重点也不在DPDK,所以下面只简单介绍下其基本原理,让咱们大体清楚为何Envoy引入VPP后能够大幅提高请求处理转发效率。
为了说清楚DPDK是如何大幅提高了数据包的处理性能,咱们先看一下普通的数据包在Linux中的收发过程,以下图:
经过上面两张图咱们能够大体清楚数据包的一个完整的收发过程,能够看到整个处理链路仍是比较长的,且须要在内核态与用户态之间作内存拷贝、上下文切换、软硬件中断等。虽然Linux设计初衷是以通用性为目的的,但随着Linux在服务器市场的普遍应用,其原有的网络数据包处理方式已很难跟上人们对高性能网络数据处理能力的诉求。在这种背景下DPDK应运而生,其利用UIO技术,在Driver层直接将数据包导入到用户态进程,绕过了Linux协议栈,接下来由用户进程完成全部后续处理,再经过Driver将数据发送出去。原有内核态与用户态之间的内存拷贝采用mmap将用户内存映射到内核,如此就规避了内存拷贝、上下文切换、系统调用等问题,而后再利用大页内存、CPU亲和性、无锁队列、基于轮询的驱动模式、多核调度充分压榨机器性能,从而实现高效率的数据包处理。说了这么多,接下来咱们看下在DPDK中数据包的收发过程,以下图:
经过对比得知,DPDK拦截中断,不触发后续中断流程,并绕过内核协议栈,经过UIO(Userspace I/O)技术将网卡收到的报文拷贝到应用层处理,报文再也不通过内核协议栈。减小了中断,DPDK的包所有在用户空间使用内存池管理,内核空间与用户空间的内存交互不用进行拷贝,只作控制权转移,减小报文拷贝过程,提升报文的转发效率。
DPDK可以绕过内核协议栈,本质上是得益于 UIO 技术,UIO技术也不是DPDK创立的,是内核提供的一种运行在用户空间的I/O技术,Linux系统中通常的驱动设备都是运行在内核空间,在用户空间用的程序调用便可,UIO则是将驱动的不多一部分运行在内核空间,绝大多数功能在用户空间实现,经过 UIO 可以拦截中断,并重设中断回调行为,从而绕过内核协议栈后续的处理流程。
那么UIO是如何拦截中断的呢?咱们先看看做为一个设备驱动的两个主要职责:
UIO的实现机制实际上是对用户空间暴露文件接口,好比当注册一个 UIO 设备 uioX,就会出现文件 /dev/uioX,对该文件的读写就是对设备内存的读写。除此以外,对设备的控制还能够经过 /sys/class/uio 下的各个文件的读写来完成。UIO架构及流程图以下,再也不赘述。
说完了DPDK,那么Cilium又是如何提升报文转发效率呢?既然Cilium 是基于 eBPF 和 XDP 实现的,而XDP归根结底也是利用eBPF为Linux内核提供高性能、可编程的网络数据路径框架,既然核心是eBPF,那么咱们先了解下eBPF是什么。
eBPF(extended Berkeley Packet Filter)起源于BPF,它提供了内核的数据包过滤机制。Linux 3.15 开始引入 eBPF。其扩充了 BPF 的功能,丰富了指令集。它在内核提供了一个虚拟机,用户态将过滤规则以虚拟机指令的形式传递到内核,由内核根据这些指令来过滤网络数据包。直白地讲就是咱们可让内核按照咱们的规则来对数据包进行处理,包括未进入协议栈以前的处理哦,有没有瞬间以为eBPF很牛逼,既然都这么强大了,有没有什么最佳实践或者应用呢?请看下图:
咱们能够看到XDP自己就是一个eBPF的最佳实践,因为其余内容跟本文档讨论内容无关,再也不展开。做为eBPF是如何工做以提供强大的能力呢?请看下图:
首先是将用户的.c文件编译后自动生成eBPF 字节码文件,也就是一堆的指令集合,其次经过系统调用将字节码注入到内核,而后内核验证合法性,经过校验后使用JIT将其run起来,用户程序与run起来的eBPF程序使用内核提供的标准Maps作数据交换。
与DPDK的内存所有在用户空间来避免内存拷贝、上下文切换、系统调用等不一样,eBPF都是在内核空间执行的。但二者的核心都是经过避免数据包在内核态与用户态之间的往复来提高转发效率。
说完了eBPF,接下来该XDP粉墨登场了。XDP(eXpress Data Path)为Linux内核提供了高性能、可编程的网络数据路径。因为网络包在还未进入网络协议栈以前就处理,它给Linux网络带来了巨大的性能提高(性能比DPDK还要高)。
XDP在Linux内核4.8中引入,在数据包到达协议栈、分配sk_buff以前拦截,不一样于DPDK的是XDP是做为内核功能的一部分,是与内核协同工做的。其基本处理流程以下图:
XDP一样将用户程序编译后生成eBPF字节码文件,注入内核执行包过滤。XDP包过滤是在数据包进入内核协议栈以前,若是判断数据包不需进一步处理可直接在内核态转发数据包,若是判断TX设备来不及处理会直接丢包,若是判断数据包需再处理则转给协议栈。
而为何会有XDP比DPDK更高效的结论呢?也许经过下面这张图你能够本身找到答案。
做为数据报文处理的新贵,其带来的性能优点是不言而喻,但XDP真的那么完美吗?答案必定是否认的,其缺点有二:
聊了那么多关于eBPF与XDP的内容,其在业界存在最佳实践吗?是的,目前facebook开源的katran项目,使用的正是这两项技术,据称其从IPVS转到eBPF后,使其性能提升了10倍。Linux社区中有人用XDP编写的一个简单的入口防火墙就能够轻松实现每秒处理1100万个数据包的性能。
说完了如何高效的转发请求,接下来咱们聊聊Sidecar之间如何高效的通信。
提到通信那就必定要说起通信协议了,做为咱们耳熟能详的两大基本通信协议TCP与UDP的优缺点这里就再也不赘述了,那么咱们是否能整合TCP与UDP二者的优势呢,这样既保证了TCP的可靠与安全性,又兼具UDP的速度与效率,不能否认的是每每正是出于人们对美好事物的向往,才持续不断的推进咱们前进的脚本。QUIC在这种期许下诞生,旨在建立几乎等同于TCP的独立链接,但有着低延迟,并对相似SPDY的多路复用流协议有更好的支持。
QUIC协议自己就内置TLS栈,实现本身的传输加密层,而没有使用现有的TLS 1.2。同时QUIC还包含了部分HTTP/2的实现,所以QUIC的地位看起来是这样的:
QUIC协议的诞生就是为了下降网络延迟,开创性的使用了UDP协议做为底层传输协议,经过多种方式减小了网络延迟。所以带来了性能的极大提高,且具体的提高效果在Google旗下的YouTube已经验证。
既然QUIC协议相比现有其余的协议更具优点 ,那是否也能够将其应用到Envoy中呢?Envoy社区正在推进官方重构其架构的目的之一就是为了QUIC,最终目的是但愿使用QUIC做为Sidecar之间的通信协议。
试想一下若是Envoy应用了上述技术,性能会有怎样的提高呢?这个就留给各位看官自行脑补吧。
读到这里不知各位是否会产生这样的疑问,目前做为ServiceMesh中数据面板的Sidecar有好几个,为何只有Envoy社区在性能方面呼声最高呢?这就牵扯到一个老掉牙的话题了,由于Envoy是C系语言编写的,在应用OS特性时有着先天优点。
上节内容提到目前有与Envoy同类的几个程序,包括Linkerd、Conduit、NginMesh,下面就以我的所知简单描述下各自的特色,仅供诸位参考。
就我的而言,其实挺但愿Conduit的壮大,正如其设计初衷说的那样:轻量化,相比Istio这种重部署模式来说,很是适合小规模业务的快速上手,且Conduit与Linkerd系出同门,足以保证其设计理念的先进性,虽然Buoyant公司宣称Conduit与Linkerd的目标不一样,但细想下来何尝Buoyant公司没有存在一丝不甘,但愿推出一个完整的Service Mesh方案来颠覆Istio一家独大的局面,夺回Service Mesh开创者的殊荣。
下面是各自的特性简述。
NginMesh:
NginMesh给人的感受更多的像是作了一个Istio的桥接器,只负责把Istio的配置信息翻译成Nginx所知的,经过重启Nginx的方式应用配置。给个人感受仅仅是为了搭上ServiceMesh的顺风车而临时推出的一个方案。
Linkerd:
做为“Service Mesh”概念的缔造者、布道者,最终却在Service Mesh的大潮中,被由Google、IBM、Lft联手打造的Istio + Envoy战胜,不得不感叹巨头的强大与初创公司的弱小与艰辛,由衷的但愿看到Conduit的崛起,逆杀Istio。话说这是否是典型的弱者心态啊,哈哈。
Conduit: