共计 5865 个字符,预计需要花费 15 分钟才能阅读完成。
作者:Eliza Weisman
局部因为 Linkerd 的性能数字和一流的平安审计报告,最近对 Linkerd2-proxy(Linkerd 应用的底层代理)的趣味激增。作为一名 Linkerd2 维护者,我大部分工夫都在 Linkerd2-proxy 上工作,所以这个主题十分贴近我的心田。在本文中,我将更具体地介绍 Linkerd2-proxy 是什么以及它是如何工作的。
代理能够说是服务网格中最要害的组件。它能够随应用程序的部署而扩大,因而低附加提早和低资源耗费至关重要。它也是解决应用程序所有敏感数据的中央,因而安全性至关重要。如果代理速度慢、臃肿或不平安,那么服务网格也是如此。
当初的 Linkerd2-proxy 就是为了满足这些严格的要求而设计的。事实上,我认为它可能是服务网格用例和世界上最令人兴奋的一些技术的最佳代理。正如 William Morgan 最近所说的,Linkerd2-proxy 是古代网络编程的最先进的状态:
不像 Envoy、NGINX 和 haproxy 这样的通用代理,开源的 Linkerd2-proxy 被设计为只做一件事,并且比任何人做得都好:成为一个服务网格边车(sidecar)代理。
事实上,咱们认为 Linkerd2-proxy 代表了平安、古代网络编程的最新技术。它是齐全异步的,用古代类型平安和内存平安的语言编写。它充分利用了古代 Rust 网络生态系统,与亚马逊的 Firecracker 等我的项目共享根底。它对古代网络协议(如 gRPC)有原生反对,能够基于实时提早实现负载平衡申请,并对零配置应用进行协定检测。它是齐全开源的、通过审计的和大规模宽泛测试的。
所以,如果你想理解最先进的网络编程是什么样子的,请持续浏览!
为什么应用 Rust 呢?
谈到 Linkerd2-proxy,如果不探讨 Rust,那就不残缺。当咱们在 2017 年开始着手 Linkerd2-proxy 的开发时,咱们无意识地决定应用 Rust,只管过后 Rust 的网络生态系统十分十分早。为什么咱们抉择这条冒险的路线,而不是保持应用 Scala,或者一些更“传统”的代理语言,如 C ++ 或 C?
决定应用 Rust 的起因有几个。首先,服务网格代理有一些十分严格的要求:因为它是作为每个 pod 根底上的边车部署的,所以它必须领有尽可能小的内存和 CPU 占用。因为应用程序的大部分或所有网络流量都要通过代理,所以它须要有最小的提早开销,尤其是最坏状况下的尾部提早。兴许最重要的是,因为代理解决应用程序数据(可能包含难以置信的敏感数据,如金融交易或集体衰弱),因而必须保障平安。
让咱们顺次从资源耗费开始。在咱们编写 Linkerd2-proxy 之前,咱们构建了 Linkerd 1.x。Linkerd 的第一个版本有一个用 Scala 编写的代理组件,并利用强壮的 Scala 和 Java 网络生态系统来实现大规模的卓越性能。然而,因为它运行在 Java 虚拟机上,所以它占用了相当大的资源。(JVM 擅长于“伸”,但不善于“缩”,正如 William 在他的 InfoQ 文章中对于决定从新实现 Linkerd 的文章中所写的那样。)只管 Linkerd 社区在调优 JVM 的内存应用以最小化内存占用方面做得很好,但在按 pod 服务网格部署模型中,要求这么做还是太多了。所以咱们晓得咱们须要一种能够编译成原生二进制文件的语言,比方 Rust、Go 和 C ++。
当初,谈一谈提早。咱们从 Linkerd 1 中学到的另一个教训。通知咱们抉择 Rust 的是垃圾收集的影响。在垃圾收集运行时中,GC 必须偶然遍历内存中的对象图,以找到不再应用且能够回收的对象。这个过程须要工夫,而且可能在不可预测的点产生。如果申请是在垃圾收集器通过期间传入的,那么它可能具备显著的提早。这个尖尖的、不可预测的提早配置文件与咱们对服务网格代理的冀望相同。因而,只管咱们喜爱 Go(Linkerd 2.x 管制立体是用它写的),Go,也是一种垃圾收集语言。这就给咱们留下了没有垃圾收集的语言,比方 Rust 和 C ++。
最初,是谈平安。确保服务之间的平安和公有通信是服务网格的次要价值支柱。然而,在数据门路中插入另一个跳也会向攻击者裸露一个新的攻击面。在咱们思考进步应用程序的安全性之前,咱们必须确保咱们没有使它变得更糟。咱们曾经确定,垃圾收集语言不适宜 Linkerd2-proxy 的用例,然而 Scala、Java、Ruby 和 Go 所有依赖垃圾收集一个要害起因是:确保内存平安与手动内存治理的语言,像 C 和 C ++,比看起来要艰难得多。
为什么内存平安如此重要?很简略:绝大多数可利用的安全漏洞——Chromium 和 Windows 中 70% 的重大安全漏洞,以及最近内存中一些最重大的安全漏洞,如 heartbleed——都是由缓冲区溢出和开释后应用等内存安全漏洞造成的。与 C 和 C ++ 不同,Rust 解决了这些问题,但它是在编译时解决的,不会受到垃圾收集的性能影响。换句话说,Rust 让咱们避开了大量潜在的数据立体破绽,否则这些破绽会困扰 Linkerd。
思考到所有这些因素,Rust 是 Linkerd2-proxy 的惟一抉择。它提供了闪电般的性能、可预感的低提早和咱们晓得服务网格代理须要的平安属性。它还为咱们提供了古代语言个性,如模式匹配和富裕表现力的动态类型零碎,以及工具,如内置的测试框架和包管理器,使在其中编程变得十分欢快。
Rust 的生态系统
令人高兴的是,自 2017 年以来,Rust 网络生态系统曾经蓬勃发展——这在很大水平上要归功于 Buoyant 公司在几个要害我的项目上的投资。明天,Linkerd2-proxy 是建设在一些根底的 Rust 网络库上的:
- Tokio:Rust 的异步运行时
- Hyper:疾速、平安、正确的 HTTP 实现
- Rustls:平安的古代 TLS 实现
- Tower:模块化和可组合的网络软件组件库
让咱们一一来看一下。
Tokio 是一个构建疾速、牢靠、轻量级网络应用的平台。它提供了一个与操作系统的非阻塞 I / O 性能、高性能计时器和任务调度集成的事件循环。对于相熟 Node 的读者。能够认为 Tokio 表演的角色相似于 C 库 libuv——事实上,应用 Tokio 是 Node 创建者 Ryan Dahl 抉择在他的下一代 JavaScript 运行时 Deno 应用 Rust 的次要起因之一。自从 Linkerd 在 2016 年首次应用 Tokio 以来,它曾经在 TiKV、微软 Azure 的 iot-edge 和 Facebook 的 Mononoke 等开源我的项目,以及从 AWS 到 Discord 等公司中失去了迅速而宽泛的采纳。
Hyper 是 Rust 当先的异步 HTTP 实现,以其最佳的类内性能和正确性而著称。和 Tokio 一样,Hyper 因大规模应用而久经沙场。
为了应用互相 TLS 来爱护网络通信,Linkerd 代理应用 rustls,这是 TLS 协定的一种实现,构建在 ring 和 webpki 之上,这些库提供了底层的加密性能。一项由 CNCF 资助的独立平安审计发现,这个加密堆栈的品质十分高,来自 Cure53 的审计人员“对所出现的软件印象十分粗浅”。
明天,这些组件形成了 Rust 网络生态系统的外围构建块,毫不夸大地说,大部分开发都是由 Linkerd2-proxy 驱动的。2017 年,当咱们开始 Linkerd2-proxy 的工作时,还没有一个可生产的 HTTP/ 2 或 gRPC 实现,所以咱们率先开发了 h2 库和 tower-gRPC。当初,h2 加强了 Hyper 的 HTTP/ 2 反对,而 tower-gRPC(当初被称为 Tonic)曾经成为 Rust 最风行的 gRPC 库。受 Linkerd 1.x 提供能源的 Scala 库 Finagle 的启发,咱们还推动了 Tower 的开发,这是一个用于以模块化、可组合的形式实现网络服务和中间件的形象层。
申请的生命周期
有了这些构建块,让咱们来讨论一下代理实际上做了什么。作为一个服务网络,Linkerd 的最大益处之一能够总结为“零配置就能工作”:如果你把 Linkerd 增加到一个失常运行的应用程序,它应该持续运行,用户不应该做任何配置。(如果你是从其余服务网我的项目来的 Linkerd,这看起来很神奇。)
Linkerd 是如何实现这一惊人壮举的?当然是应用了 Linkerd2-proxy。因而,让咱们合成通过代理的申请的生命周期。代理在不进行配置的状况下如何智能地解决通信量,同时对网格化的应用程序放弃通明?
第一步是协定检测。要使零配置成为事实,当代理收到申请时,咱们须要确定正在应用的协定。所以咱们做的第一件事是从连贯的客户端读取几个字节,而后问几个问题:
- “这是 HTTP 申请吗?”
- “这是 TLS 客户端 Hello message 吗?”
如果申请是客户端 hello,则查看服务器名称批示(server name indication,SNI)值,该值蕴含客户端心愿终止的服务器的主机名。如果 SNI 值表明 TLS 连贯是为注入的应用程序筹备的,代理将间接转发音讯。透明性的一个重要局部是,如果代理接管到一个音讯,它不能做任何聪慧的事件,它应该间接发送它——在这种状况下,音讯是加密的,而代理没有解密它的密钥,所以咱们没有别的方法。相似地,未知协定中的 TCP 流量将通明地转发到其原始目的地。
另一方面,如果加密连贯是为咱们提供的,作为 Linkerd 的主动互 TLS 个性的一部分呢?网格中的每个代理都有本人独特的加密身份,代理在启动时为其生成要害资料,并且从不来到 pod 边界或写入磁盘。管制立体的身份服务对这些身份进行签名,以批示对代理进行身份验证,以便为注入代理的 pod 的 Kubernetes ServiceAccount 服务流量。如果 SNI 名称与代理的服务帐户匹配,那么咱们对其进行解密,并将其作为服务网格的一部分进行解决。
接下来,如果申请被网格化,代理会做什么?让咱们思考这样一种状况,网格化的客户机向其代理发送出站申请。代理执行咱们下面探讨的协定检测,并确定这是一个 HTTP/1、HTTP/ 2 或 gRPC 申请协定,Linkerd 了解并能够智能路由。因而,当初咱们须要确定申请的去向。Linkerd 依据指标权限(target authority)来路由 HTTP 流量,指标权限是 HTTP/1.1 和 1.0 申请的 Host: header 或申请 URL 的权限局部的值,或者 HTTP/ 2 中的:authority 头字段的值。代理查看申请,并依据应用的协定版本查找指标权限,并执行 DNS 查问以确定该名称的标准模式。
当代理晓得了申请的指标权限,它就通过从 Linkerd 管制立体的指标服务中查找权限来执行服务发现。是否征询管制立体取决于一组搜寻后缀:默认状况下,代理被配置为查问位于默认 Kubernetes 集群本地区.cluster.local 的服务。然而能够为应用自定义域的集群笼罩此性能。指标服务向代理提供组成该权限的 Kubernetes 服务的所有端点的地址,以及特定于链接的元数据和配置重试、超时和其余策略的服务配置文件。所有这些数据都流到代理,所以如果有任何变动——例如。如果一个服务被放大或放大,或者服务概要配置被编辑——管制立体将在产生时将新状态推送到代理。
而后,代理将在管制立体提供的一组端点上对申请进行负载平衡。当申请被转发到目的地时,代理会应用一种名为指数加权挪动均匀(exponentially weighted moving averages,EWMA)的负载平衡算法来计算负载估算。实质上,这意味着代理在一个无限的工夫窗口内放弃一个挪动均匀提早,以便在提早产生时对其做出反馈,并且该负载预计是基于向该端点运行的申请的数量进行加权的。在传统上,负载平衡决策通常是通过抉择预计负载最低的端点来做出的,比方应用有序堆。然而,放弃端点的有序汇合从最小到最大的负载在计算上是低廉的,所以 Linkerd 实现了“两种抉择的能力”(power of two choices,P2C)负载平衡。在这种办法中,咱们通过从两个随机抉择的可用端点中抉择负载较少的端点来做出每个负载平衡决策。只管这仿佛违反直觉,但从数学上来说,这至多在规模上与总是抉择负载最小的正本一样无效,而且它防止了多个负载均衡器都将流量发送到负载最小的正本、导致重载的问题。此外,这种快捷方式效率更高,因而在速度上有很大差异。
当指标端点有本人的 Linkerd 代理时,管制立体将向代理批示它能够发动互相 TLS,以确保连贯是平安和公有的。同样的,当 HTTP/1.x 申请在网格中发送,代理将通明地将它们降级为 HTTP/2,这样多个申请能够在一个连贯上多路复用,并由指标代理降级为 HTTP/1,这样降级对应用程序是不可见的。与 Linkerd 的智能、反对协定的负载平衡相结合,这是网状流量通常比非网状流量具备更低提早的起因之一,只管采纳了额定的网络跳数。
把它们放在一起,代理中的根本逻辑流程看起来如下:
只管 Linkerd2-proxy 提供了很多性能,但咱们尽可能放弃它的简略和最低限度。最重要的是,代理的模块化体系结构意味着大多数个性能够实现为小的、自蕴含的模块,并插入到堆栈的适当地位,放弃整体代码复杂度低。
代理是要害
明天,Linkerd 是惟一一个以数据立体代理为个性的服务网格,它是为服务网格用例显式地从头设计的。通过关注服务网格的独特需要,充分利用 Rust 令人印象粗浅的性能、平安保障和尖端的异步网络栈,咱们置信 Linkerd2-proxy 是 Linkerd 胜利的秘诀。
那么,你是否想参加一个在世界各地的要害零碎中应用的最前沿的开源 Rust 我的项目?好消息,Linkerd 是开源的,所以你能够!在 GitHub 上退出咱们,并查看 Slack 上的 #contributors 频道。咱们心愿你能退出。
Linkerd 实用于所有人
Linkerd 是一个社区我的项目,由 CNCF 托管。Linkerd 致力于凋谢治理。如果你有性能要求、问题,或评论,咱们心愿你退出咱们快速增长的社区!Linkerd 代码托管在 GitHub 上,咱们在 Slack、Twitter 和邮件列表上有一个蓬勃发展的社区。快来退出咱们乐趣满满的我的项目吧!
点击浏览网站原文。
CNCF (Cloud Native Computing Foundation) 成立于 2015 年 12 月,隶属于 Linux Foundation,是非营利性组织。
CNCF(云原生计算基金会)致力于培养和保护一个厂商中立的开源生态系统,来推广云原生技术。咱们通过将最前沿的模式民主化,让这些翻新为公众所用。扫描二维码关注 CNCF 微信公众号。