背景
最近因工作原因开始了解 Service Mesh 与 Envoy, 为系统性梳理所学内容, 因此沉淀了此文档, 但由于所知有限, 如文档中有描述不当之处, 希望不吝赐教.
提到 Envoy 就不得不提 Service Mesh, 说到 Service Mesh 就一定要谈及微服务了, 那么我们就先放下 Envoy, 简单了解下微服务, Service Mesh 以及 Envoy 在 Service Mesh 中处于一个什么样的角色.
过去几年间, 架构领域最火的方向非微服务莫属, 那么微服务架构到底为我们带来了什么样的好处呢? 下面通过一张图说明架构的演进, 如下:
伴随着业务规模的变大, 微服务的好处显而易见, 例如它本身所具备的可扩展性, 易维护性, 故障和资源隔离性等诸多特性使得产品的生产研发效率大大提高, 同时, 基于微服务架构设计, 研发人员可以构建出原生对于 "云" 具备超高友好度的系统, 让产品的持续集成与发布变得更为便捷.
然而没有所谓的银弹, 微服务带来很多好处的同时也引入了很多问题. 在云原生模型里, 一个应用可以由数百个服务组成, 每个服务可能有数千个实例, 每个实例的状态可能持续的发生变化, 此时, 服务间的通信不仅异常复杂, 而且都是运行时的行为, 管理好服务间通信对于保证端到端的性能与可靠性来说无疑成为重中之重. 在 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 主流程
任何软件架构设计, 其核心都是围绕数据展开的, 基本上如何定义数据结构就决定了其流程的走向, 剩下的不外乎加上一些设计手法, 抽离出变与不变的部分, 不变的部分最终会转化为程序的主流程, 基本固化, 变的部分尽量保证拥有良好的扩展性, 易维护性, 最终会转化为主流程中各个抽象的流程节点.
对于 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 架构
聊完了基本流程后, 本节会试着分析其架构设计, 希望从其架构设计中获得一些益处.
先卖个关子, 在本节开始之前我们不妨先思考一个有趣的问题: 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 https://www.envoyproxy.cn/Introduction/Architectureoverview/Dynamicconfiguration.html ),EDS(Endpoint Discovery Service https://www.envoyproxy.cn/Introduction/Architectureoverview/Dynamicconfiguration.html ),CDS(Cluster Discovery Service https://www.envoyproxy.cn/Introduction/Architectureoverview/Dynamicconfiguration.html ),RDS(Route Discovery Service https://www.envoyproxy.cn/Introduction/Architectureoverview/Dynamicconfiguration.html ),LDS(Listener Discovery Service https://www.envoyproxy.cn/Introduction/Architectureoverview/Dynamicconfiguration.html ).XDS 模块功能是向 Istio 的 Pilot 获取动态配置信息, 拉取配置方式分为 V1 与 V2 版本, V1 采用 HTTP,V2 采用 gRPC.
Envoy 还支持热重启, 即重启时可以做到无缝衔接, 其基本实现原理是:
将统计信息与锁放到共享内存中.
新老进程采用基本的 RPC 协议使用 Unix Domain Socket 通讯.
新进程启动并完成所有初始化工作后, 向老进程请求监听套接字的副本.
新进程接管套接字后, 通知老进程关闭套接字.
通知老进程终止自己.
Envoy 同样也支持 Lua 编写的 Filter, 不过与 Nginx 一样, 都是工作在 HTTP 层, 具体实现原理都一样, 不做赘述了.
到此为止我们看完了上面的架构图, 如果你对其内部实现也有兴趣的话, 可以看看下面的内部实现类图:
其内部实现为了灵活性, 做了很多抽象封装, 但基本上可以拆分为几个大的功能模块, 具体如上图, 不再赘述.
Envoy 性能谈
软件的世界从来就不存在什么银弹, 虽然 ServiceMesh 优势很明显, 甚至被尊称为服务间的通讯层, 但不可否认的是 ServiceMesh 的到来确实对应用的性能带来了损耗, 可以从两个方面看待此问题:
数据面板中 Sidecar 的加入增加了业务请求的链路长度, 必然会带来性能的损耗, 由此延伸可知请求转发性能的高低必然会成为各个 Sidecar 能否最终胜出的关键点之一.
控制面板采用的是集中式管理, 统一负责请求的合法性校验, 流控, 遥测数据的收集与统计, 而这要求 Sidecar 每转发一个请求, 都需要与控制面板通讯, 例如对应到 Istio 的架构中, 这部分工作是由 Mixer 组件负责, 那么可想而知这里必然会成为性能瓶颈之一, 针对这个问题 Istio 官方给出了解决方案, 即将 Mixer 的大部分工作下放到 Sidecar 中, 对应到 Envoy 中就是新增一个 MixerFilter 来承担请求校验, 流控, 数据收集与统计工作, MixerFilter 需要定时与 Istio 通讯以批量上报数据与拉取最新配置数据. 这种方式在 Istio 之前微博的 Motan, 华为 Mesher, 唯品会的 OSP 中已经这么做了.
本节主要谈论 Envoy 在性能方面的努力及社区在性能方面呼声较高的一些内容.
Envoy 作为 Sidecar 其提供的核心功能可以简单总结为以下三点:
对业务透明的请求拦截.
对拦截请求基于一定规则做校验, 认证, 统计, 流量调度, 路由等.
将请求转发出去, 在 ServiceMesh 中所有的流量出入都要经过 Sidecar, 即由 Sidecar 承担起所有的网络通讯职责, 由此可知请求转出后的下一个接收方也必然是 Sidecar, 那么 Sidecar 之间通讯协议的高效与否对 ServiceMesh 整体性能也会产生较大影响.
从上述三点中我们试着分析下性能优化的关键点, 其中第 1,3 点是与业务基本无关的, 属于通用型功能, 而第 2 点的性能是与业务复杂度呈现相关性的, 比如请求校验规则的多与少, 遥测数据的采集精细度, 数据统计的维度多样性等, 因此最有可能提升 Sidecar 性能的点就是对请求的拦截与 Sidecar 之间通讯协议的高效性.
针对请求的拦截, 目前常规的做法是使用 iptables, 在部署 Sidecar 时配置好 iptables 的拦截规则, 当请求来临后 iptables 会从规则表中从上至下顺序查找匹配规则, 如果没遇到匹配的规则, 就一条一条往下执行, 如果遇到匹配的规则, 那就执行本规则并根据本规则的动作(accept, reject, log 等), 决定下一步执行的情况. 为了更直观的展示 iptables 的执行过程, 请看下图:
了解 iptables 的基本流程后, 不难发现其性能瓶颈主要是两点:
在规则配置较多时, 由于其本身顺序执行的特性, 性能会下滑严重.
每个 request 的处理都要经过内核态 --->用户态 --->内核态的过程, 这其中会带来数据从内核态拷贝到用户态的, 再拷贝到内核态的性能消耗, 单次请求来看这种消耗很少, 但是作为流量进出的守门人, 可想而知每秒进出的请求量必然是一个很高的数字, 其累积的消耗也必然很高, 再进一步分析由于网络中大量数据包的到来, 会产生频繁的硬件中断, 上下文切换, 甚至是一个数据包在多个 CPU 核之间切换处理, 这些因素叠加起来会对性能造成更大的损耗.
既然知道了 iptables 的缺陷, 那么优化手段不外乎从这两点下手, 而 Linux 社区与 Envoy 社区也正在计划对此做优化, 具体如下:
Linux 内核社区最近发布了 bpfilter, 一个使用 Linux BPF 提供的高性能网络过滤内核模块, 计划用来替代 netfilter 作为 iptables 的内核底层实现, 实现 Linux 用户向 BPF 过渡的换心手术.
Envoy 社区目前正在推动官方重构其架构, 目的是为了支持自定义的 network socket 实现, 当然最终目的是为了添加 VPP(Vector Packet Processing),Cilium 扩展支持, 无论使用 VPP 或 Cilium 都可以实现数据包在纯用户态或者内核态的处理, 避免内存的来回拷贝, 上下文切换, 且可以绕过 Linux 协议栈, 以提高报文转发效率, 进而达到提升请求拦截效率的目的.
为什么规避 Linux 正常协议处理过程中内核态与用户态的转换如此重要呢? 就以对我们最直观的内存拷贝为例, 正常情况下, 一个网络数据包从网卡到应用程序需要经过如下的过程: 数据从网卡通过 DMA 等方式传到内核开辟的缓冲区, 然后从内核空间拷贝到用户态空间, 在 Linux 内核协议栈中, 这个耗时操作甚至占到了数据包整个处理流程的 57.1%. 为了更直观的对内存拷贝消耗有所了解, 画了一张简图, 如下:
简说 DPDK
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 http://baike.baidu.com/subview/300881/11169495.htm 技术, Linux http://baike.baidu.com/view/1634.htm 系统中一般的驱动设备都是运行在内核 http://baike.baidu.com/view/1366.htm 空间, 在用户空间用的程序调用即可, UIO 则是将驱动 http://baike.baidu.com/subview/43111/5071586.htm 的很少一部分运行在内核空间, 绝大多数功能在用户空间实现, 通过 UIO 能够拦截中断, 并重设中断回调行为, 从而绕过内核协议栈后续的处理流程.
那么 UIO 是如何拦截中断的呢? 我们先看看作为一个设备驱动的两个主要职责:
存取设备的内存. UIO 核心实现了 mmap 可以处理物理内存, 逻辑内存, 虚拟内存. UIO 驱动的编写是就不需要再考虑这些繁琐的细节.
处理设备产生的中断. 设备中断的应答是必须在内核空间的, 因此 UIO 只把非常小的一部分代码逻辑放在内核, 剩余逻辑全部留给用户空间进程处理.
UIO 的实现机制其实是对用户空间暴露文件接口, 比如当注册一个 UIO 设备 uioX, 就会出现文件 /dev/uioX, 对该文件的读写就是对设备内存的读写. 除此之外, 对设备的控制还可以通过 /sys/class/uio 下的各个文件的读写来完成. UIO 架构及流程图如下, 不再赘述.
说完了 DPDK, 那么 Cilium 又是如何提高报文转发效率呢? 既然 Cilium https://github.com/cilium/cilium 是基于 eBPF 和 XDP 实现的, 而 XDP 归根结底也是利用 eBPF 为 Linux 内核提供高性能, 可编程的网络数据路径框架, 既然核心是 eBPF, 那么我们先了解下 eBPF 是什么.
简说 eBPF 与 XDP
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 真的那么完美吗? 答案一定是否定的, 其缺点有二:
XDP 不提供缓存队列(qdisc),TX 设备太慢时直接丢包, 因而不要在 RX 比 TX 快的设备上使用 XDP.
XDP 程序是专用的, 不具备网络协议栈的通用性.
聊了那么多关于 eBPF 与 XDP 的内容, 其在业界存在最佳实践吗? 是的, 目前 facebook 开源的 katran 项目, 使用的正是这两项技术, 据称其从 IPVS 转到 eBPF 后, 使其性能提高了 10 倍. Linux 社区中有人用 XDP 编写的一个简单的入口防火墙就可以轻松实现每秒处理 1100 万个数据包的性能.
简说 QUIC 协议
说完了如何高效的转发请求, 接下来我们聊聊 Sidecar 之间如何高效的通讯.
提到通讯那就一定要提及通讯协议了, 作为我们耳熟能详的两大基本通讯协议 TCP 与 UDP 的优缺点这里就不再赘述了, 那么我们是否能整合 TCP 与 UDP 两者的优点呢, 这样既保证了 TCP 的可靠与安全性, 又兼具 UDP 的速度与效率, 不可否认的是往往正是出于人们对美好事物的向往, 才持续不断的推动我们前进的脚本. QUIC 在这种期许下诞生, 旨在创建几乎等同于 TCP 的独立连接, 但有着低延迟, 并对类似 SPDY 的多路复用流协议有更好的支持.
QUIC 协议本身就内置 TLS 栈, 实现自己的传输加密层 https://docs.google.com/document/d/1g5nIXAIkN_Y-7XJW5K45IblHd_L2f5LTaDUDwvZ5L6g/edit , 而没有使用现有的 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:
Golang 实现.
Sidecar 实现模式: agent + nginx,agent 负责监听 Istio 的配置变化(例如路由规则, 集群信息, 服务发现等), 并将其转换为 nginx 的配置, 然后通过重启 nginx 应用配置.
与 istio,k8s 强绑定, 使用 k8s 的 Initializer 机制实现 sidecar 的自动注入.
目前尚未在生产环境验证.
虽然 nginx 也可以使用 ngixscript/lua 进行扩展, 但大多局限于 http 处理上, 对于 L3L4 的过滤上就无能无力了.
部署图如下:
NginMesh 给人的感觉更多的像是做了一个 Istio 的桥接器, 只负责把 Istio 的配置信息翻译成 Nginx 所知的, 通过重启 Nginx 的方式应用配置. 给我的感觉仅仅是为了搭上 ServiceMesh 的顺风车而临时推出的一个方案.
Linkerd:
Scala 语言开发, 运行在 Java 虚拟机上,"Service Mesh" 概念的缔造者, 经生产环境验证可靠的.
构建基于 Netty,Finagle 上, 工作于 RPC 层.
插入式的服务发现, 例如 File-based,Zookeeper,k8s.
由于工作在 RPC 层, 可根据实时观测到的 RPC 延迟, 要处理请求队列大小决定如何分发请求, 优于传统启发式负责均衡算法, 例如 LRU,TCP 活动请求等.
提供多种负载均衡算法如: Power of Two Choices (P2C): Least Loaded,Power of Two Choices: Peak EWMA,Aperture: Least Loaded,Heap: Least Loaded 以及 Round-Robin.
数据流程图如下:
作为 "Service Mesh" 概念的缔造者, 布道者, 最终却在 Service Mesh 的大潮中, 被由 Google,IBM,Lft 联手打造的 Istio + Envoy 打败, 不得不感叹巨头的强大与初创公司的弱小与艰辛, 由衷的希望看到 Conduit 的崛起, 逆杀 Istio. 话说这是不是典型的弱者心态啊, 哈哈.
Conduit:
脱胎于 Linkerd, 针对 Linkerd 部署模型太重的问题, 其秉承的设计目标是成为最快, 最轻, 最简单的 Service Mesh, 使用 Rust 构建数据平面, 使用 Go 构建控制平面. 与 Linkerd 同出 Buoyant 公司.
不同于 Envoy,Linkerd 是数据平面, Istio 是控制平面, Conduit 中既包括数据平面, 也包括控制平面, 控制平面提供 API, 用户通过 Conduit CLI 与 web UI 使用.
只能运行在 K8s 上.
目前发布了 0.3 版本, 仍然处于初期开发阶段.
对经过 Conduit proxy 的流量, 产生一系列的监控指标, 指标格式是 Prometheus 的, 内容存放在 proxy 监控端口的 metrics 路径下面, Prometheus 可以直接抓取指标内容, 做聚合展示.
利用 Rust 本身的语言安全特性来保证自身的安全性.
诚邀您关注阿里中间件微信公众号!
我们懂您的胃口: 定期分享最前沿技术干货!
我们懂您的喜好: 大量精品大会, 沙龙, 比赛为您量身定做!
我们懂您的情调: 海量惊喜奖品随时放送!
来源: https://yq.aliyun.com/articles/606655