3.16

查看英文版

3.16 API 网关与服务网格

Overview and motivation

当你把一个程序拆分成多个服务的那一刻,一个新问题就出现了:谁来负责服务之间的流量,以及从外部进入的流量?你可以用糟糕的方式回答这个问题()把相同的关注点(身份验证、重试、超时、限流、日志记录)手工分散到每个服务中;也可以用更好的方式回答()把这些关注点下沉到一个共享层,让每个服务都能免费继承它们。本章讨论的正是这样两个层。API 网关位于前门,管理来自客户端的流量。服务网格位于你的各个服务之间,管理服务之间流转的流量。它们在不同的地方解决相关但不同的问题,混淆二者是一个常见且代价高昂的错误。

业界用指南针的比喻来命名这两个流量方向。南北向流量是跨越系统边界的流量:移动应用、浏览器或合作伙伴发起的调用。东西向流量是停留在系统内部的流量:服务 A 调用服务 B、服务 B 再调用服务 C 以满足一个请求。API 网关是南北向流量的专家,服务网格是东西向流量的专家。保持这一区分的清晰是本章最有用的一个理念,因为它告诉你哪个工具负责哪项策略,并防止你把同一份工作做两遍。

对大型团队而言,这些层就是让你把一项策略只实施一次、而不是实施一百次的方式。当身份验证、传输加密和限流都存在于一个共享层中时,一项安全修复只需在部署该层的当天就能推送到每一个服务,而不必等待上百个积压事项逐一处理。在企业和政府场景中,这种集中化往往正是重点所在:审计人员希望有一个单一、可证明的地方来检查访问权限并加密流量,而共享网关或服务网格恰好为他们提供了这样一个策略执行点。本章建立在第 3.2 章的架构风格、第 3.3 章的分布式系统现实,以及第 3.13 章的网络基础之上,并将它们转化为关于谁来处理你的流量的具体指导。

Key principles

  • 将南北向(网关)与东西向(网格)分开;让各自负责自己的方向。
  • 将横切关注点下沉到共享层,做到只编写一次,而不是每个服务各写一遍。
  • 只有当服务数量使得逐服务接线的成本更高时,才采用服务网格。
  • 为每个关注点定义唯一的策略执行点;绝不让网关和网格同时做同一件事。
  • 保持服务精简:平台处理传输,服务处理业务逻辑。
  • 优先采用基于身份的零信任网络,而不是基于网络位置的信任。
  • 清醒地权衡引入服务网格所带来的运维复杂度,并度量它是否值得。

Recommendations

理解 API 网关的作用

API 网关是位于你的服务前方的单一入口,负责中介来自外部世界的每一个请求。往简单说,它是一个智能反向代理(接收客户端请求并将其转发到正确后端的服务器),但网关之所以名副其实,是因为它做的远不止转发。它根据路径、主机或请求头,将每个请求路由到正确的服务。它对调用者进行身份验证(核实其身份)并对请求进行授权(检查其被允许做什么),这样网关后面的服务就可以信任:一个请求已经通过了前门的检查。它执行限流(在一段时间内限制每个客户端的请求数)和配额管理(在更长的时间窗口内限制总使用量),这样一个吵闹或滥用的客户端就不会耗尽其他客户端的资源。

网关还会对流量进行重塑。请求转换会重写请求头、在协议之间转换,或者把旧客户端的格式适配为新服务所期望的格式。API 组合让网关将单个入站请求分发给多个服务,并把它们的响应拼接成一个,这样客户端只需发起一次调用,而不是六次。版本管理支持让你可以并行运行 API 的 v1 和 v2 版本,并把每个客户端路由到它所期望的版本,从而为演进留出空间而不会破坏任何人。把这些关注点集中在边缘,既能让你的服务专注于业务逻辑,也让你拥有一个统一的地方来观察、保护和限流所有进入的流量。网关所面向的 API 设计是第 2.3 章的主题,它所执行的身份检查则借鉴自第 4.7 章。

针对差异化客户端使用前端专属后端模式

单一的通用 API 往往同时服务于 Web 应用、移动应用和合作伙伴集成,结果是对每一方的服务都略显不足。移动客户端希望负载小、往返次数少,因为带宽和电量都很稀缺;Web 客户端可以承受更啰嗦、更丰富的响应;合作伙伴则希望有一份稳定、永不出人意料的契约。前端专属后端(BFF)模式通过为每一类客户端提供一个针对其需求量身定制的轻量网关来化解这种张力,该网关位于其背后共享服务的前方。

BFF 是一种服务对象更窄的网关。移动端 BFF 会组合并裁剪响应,使应用只需一次高效调用;Web 端 BFF 暴露更完整的数据形态;合作伙伴 BFF 则维护一份变化缓慢、经过谨慎版本管理的契约。每个团队都可以独立演进自己的 BFF,而无需等待其他团队,这往往才是真正的收益所在,因为它解耦了各个客户端团队。代价是活动部件更多,且各个 BFF 之间存在一定的逻辑重复,因此应把这种模式留给客户端需求确实存在分歧的场景。当所有客户端都想要相同的东西时,一个网关会更简单、更好。

理解服务网格的作用

服务网格管理你的各个服务之间的东西向流量,而且无需服务修改自身代码即可做到这一点。经典的网格通过部署 sidecar(边车)代理来实现()这是一个与每个服务实例一起运行、拦截其所有网络流量的小型代理进程。你的服务以为自己在直接与另一个服务通信;实际上它是在与本地的 sidecar 通信,由 sidecar 来处理真正的网络调用。由于现在每个请求都要流经一个由平台控制的代理,网格就可以在每一种语言编写的每一个服务上统一执行行为,而无需维护一个共享库来保持同步。

它具体执行哪些内容?第一,双向 TLS(mTLS),即每条连接的双方都出示证书并加密流量,使服务间调用默认就是经过身份验证且私密的。第二,流量管理:网格可以将一小部分流量转移到新版本以进行金丝雀发布,可以按请求头拆分流量以用于测试,也可以将流量镜像到影子服务。第三,弹性:重试、超时和熔断(第 2.20 章的模式)都在平台层实施,由策略配置,而不是硬编码进每个服务。第四,可观测性:由于每个请求都经过代理,网格能为所有服务间流量发出一致的指标、日志和分布式追踪,为第 9.2 章的可观测性实践提供数据。服务的作者不需要编写这些内容中的任何一项,却能获得全部收益。

了解 sidecar 模式及无 sidecar 的替代方案

sidecar 模型很优雅,但并非没有代价。每个服务实例现在都要额外运行一个消耗内存和 CPU 的代理容器,并且每次调用都会多出两跳网络(进入本地 sidecar 和离开远端 sidecar),带来一点额外延迟。在只有少数几个服务的规模下,这种开销可以忽略不计;而在成千上万个 pod 的规模下,它会成为计算账单和延迟预算中实实在在的一项支出。这种代价推动了一波无 sidecar(或称无代理)方案的兴起。

有两个方向值得关注。一个方向是把网格功能从每个 pod 一个的 sidecar 迁移到每个节点一个的代理,让同一台机器上的多个服务共享一个代理,而不是各自运行自己的代理;这以牺牲一定隔离性为代价,换来开销的大幅下降。另一个方向是无代理方案,它通过一个轻量库或运行时把网格逻辑直接嵌入服务本身,彻底消除了额外的跳数,代价是引入了按语言区分的依赖。更新的发展是利用 eBPF(一种可在 Linux 内核中运行沙盒化程序的技术)把部分网格功能下沉到操作系统内核中,从而以比用户态代理更低的开销执行策略并采集遥测数据。你今天不需要押注某一个最终胜出的方案。但你确实需要知道:sidecar 税是真实存在的,替代方案确实存在,而你的平台选择不应让你在未来无法采用这些替代方案。这些模式建立在第 8.3 章的容器编排基础之上。

判断服务网格何时值得其复杂度

服务网格功能强大,但运维起来确实复杂。它增加了一个需要运维的控制平面、需要升级的代理、需要轮换的证书,以及在请求丢失时需要排查的新的一层。当你拥有足够多的服务,以至于按服务、按语言手工接线这些关注点的成本高于运维网格本身时,这种复杂度就值得付出。大致的信号是规模和多语言多样性:数十或数百个用多种语言编写的服务,此时若要用共享库来实现 mTLS 和重试,保持一致性将是一场噩梦。在这种规模下,网格能够以统一性和可证明的安全性收回自己的成本。

当你只有少数几个服务、使用单一语言,或团队规模较小时,网格并不值得其复杂度。对于一个规模适中的系统,一个好的库或框架就能以远低于完整网格的运维负担为你提供 mTLS、重试和指标,而一个普通网关加上合理的客户端库往往就能满足你所需要的一切。在规模尚未提出要求之前,仅仅因为流行就采用网格,是许多团队花上一年时间运维一套解决自己并不存在的问题的基础设施的常见方式。从网关起步,在代码或库中加入弹性模式,等到服务和语言的数量使逐服务方案的成本更高时,再引入网格。这与云和分布式系统各章(第 3.11 章和第 3.3 章)反复强调的”是否值得其复杂度”这一纪律是同一回事。

避免网关与网格重叠处理同一件事

网关与网格之间存在重叠,而这种重叠正是团队伤害自己的地方。两者都可以做重试、都可以强制执行超时、都可以检查身份、都可以采集遥测数据。如果网关将一个请求重试三次,而网格又在每一次内部跳转中各自重试三次,那么客户端的一次重试就可能爆炸成几十次后端调用,把一个小小的波动变成一场重试风暴。如果两层都强制执行超时,而内层超时长于外层超时,外层会放弃等待,而内层仍在继续工作,白白浪费在一个不会有人读取的响应上。

解决办法是制定并达成一份清晰的、书面的分工。把每个关注点恰好分配给一层。网关负责南北向的关注点:终端用户身份验证、面向外部的限流与配额、请求转换,以及面向客户端的 API 组合。网格负责东西向的关注点:服务间 mTLS、内部重试与熔断,以及服务版本之间的流量转移。对于既可以放在这层、也可以放在那层的关注点,选定一个所有者,让另一层直接放行。配置重试预算和超时层级,确保外层超时始终长于它所等待的内层工作。目标是让每个请求的每个关注点都恰好由一个地方处理,不会有请求被意外地重试、验证或记录两次。

将网关和网格视为零信任的策略执行点

运维这些层最深层的理由在于安全架构。零信任的原则是:不因请求来自何处就信任它;每个请求都必须证明自己的身份和授权,即使是在你自己的网络内部也不例外。旧模型信任任何已经身处边界之内的事物,这意味着一旦某个服务被攻破,攻击者就能自由游走。零信任用每一跳都基于身份的信任取代了基于网络位置的信任,而网关和网格正是检查这种身份的天然执行点。

网关是外部身份的策略执行点:它在任何流量到达你的服务之前先验证终端用户或合作伙伴。网格是工作负载身份的策略执行点:每个服务都获得一个加密身份,mTLS 在每次调用中都证明这一身份,而策略决定哪些服务可以与哪些服务通信。二者结合起来为你提供纵深防御()请求在边缘被检查一次,在服务之间又被检查一次,因此一个服务被攻破并不会让攻击者在其余服务间自由移动。在多集群、多区域部署中,网格可以把这种身份体系扩展到集群边界之外,使一个集群中的服务在向另一个集群中的服务进行身份验证时,享有与本地相同的 mTLS 保障,从而即便部署范围不断扩大,也能获得一致的零信任网络。这里的身份基础直接对接第 4.7 章。

Trade-offs: pros and cons

方案优点缺点
API 网关身份验证、限流、组合、版本管理集中于一处需要扩展并保持高可用的单一瓶颈点
前端专属后端(BFF)每个客户端都获得量身定制、可独立演进的 API需要运行更多网关;逻辑在各个 BFF 间重复
服务网格(sidecar)无需修改代码即可获得统一的 mTLS、重试、可观测性代理开销、延迟,以及需要运维的控制平面
无 sidecar / 无代理网格开销和延迟低于每 pod 一个的 sidecar成熟度较低;隔离性较弱或存在按语言区分的依赖
基于库的弹性方案运行简单;无需额外基础设施按语言重复实现;规模化后难以保持一致
跨集群网格处处保持一致的零信任身份显著的运维和网络复杂度

核心张力在于统一性与运维成本之间的权衡。共享层为你带来一致性、可证明的安全性,以及只需编写一次的策略,但它同时也是一个你必须运维、扩展、保护和调试的真实系统,并且会插入到每一个请求的路径之中。应根据规模和需求来化解这种张力。只要你有外部客户端,网关几乎总能立刻收回成本,因为它所集中处理的关注点是你无法回避的。网格的回报来得较晚,要等到服务和语言的数量使逐服务接线成为更昂贵的路径时才会显现。低于这个门槛时,库加上一个普通网关就能以极小的成本带来大部分收益;高于这个门槛时,网格的统一性就物有所值。两个方向上的错误都是出于流行而非需求去采用:过早引入网格是浪费一年时间做无谓的打磨(yak-shaving),而过晚才引入网格则意味着上百个不一致的、手工实现的 mTLS。

Questions to discuss with your team

  1. 对于每一个横切关注点(身份验证、重试、超时、限流、加密、遥测),究竟由哪一层单独负责,团队中的每个人是否都能不假思索地说出负责方? 这正是防止重复处理的关键问题,而大多数团队从未明确回答过它,这意味着答案会因服务和作者不同而不同。带上一份具体的关注点清单列在左侧,把你的各层(客户端库、网关、网格、单个服务)列在顶部,一起填写这张表格。同一个关注点在两个格子里都被打勾的地方,就是潜伏的重试风暴和超时反转。空白的行则是完全没有人处理的关注点。最终交付物应是一张所有人一致同意、发布在每个团队都能看到的地方的表格,明确说明每个关注点恰好由哪一层负责,其余层则直接放行。这张表格比任何数量的网关或网格配置都更有价值,因为它正是让两层不再互相打架的关键所在。

  2. 我们是否真的拥有足够多的服务和语言多样性来证明引入服务网格的合理性,还是即将买下一个控制平面来解决一个我们并不存在的问题? 网格是一项严肃的运维承诺,而对许多团队而言,诚实的答案是:今天一个好的库加上一个网关会服务得更好。带上你的服务实际数量、它们所使用的语言种数,以及对当下重复的网络逻辑究竟带来多大伤害的诚实评估。然后再带上另一面:谁来运维这个网格、升级它的代理、轮换它的证书,以及在它误路由请求时被呼叫。如果逐服务接线的痛苦小于运维网格的成本,你就有了答案()那就是再等等。如果你正被淹没在数十个多语言服务中互不一致的 mTLS 和重试代码里,那么网格就值得引入。关键在于依据证据来决定,而不是依据某场会议演讲把网格描绘成什么样子。

  3. 我们今天的零信任边界在哪里?如果某个内部服务被攻破,会发生什么? 许多系统仍然信任网络内部的任何事物,这意味着一旦某个服务被攻破,攻击者就能横向移动并触及其他一切,而团队往往只有在发生事故时才发现这一点。诚实地梳理一遍爆炸半径:如果攻击者控制了你的某个服务,它能调用什么、能读取什么、又有什么能阻止它?带上你当前关于服务间调用如何验证身份和加密的答案,并具体说明哪些调用受 mTLS 保护,哪些只是基于处于同一网络的明文信任。随之而来的行动是为每一跳制定一份基于身份访问的周密计划,由网关检查外部身份、由网格或同等机制检查工作负载身份,从而使一次入侵被遏制在局部,而不是酿成灾难。即便你还没准备好运行完整的网格,明确指出信任边界真正所在的位置,也是第一步诚实的行动。

  4. 如果我们的 API 网关层此刻失效,系统会有多大比例陷入瘫痪?我们是否真的测试过这种故障,还是仅仅假设它不会发生? 网关集中了如此多的功能,以至于它一旦宕机,其背后的一切都会随之下线,而大型团队恰恰因为它平时悄无声息地正常运转,就往往在冗余投入上不足。权衡这两种相互竞争的取向:单一、简单的网关易于理解、运行成本低,而横向扩展的多可用区网关层成本更高,还带来了自身的故障切换配置和复杂度。带上真实的数字进行讨论,包括今天运行着多少个实例、跨越多少个可用区、故障切换需要多长时间,以及你上一次特意打掉网关来做演练日是什么时候。对于承担正常运行时间承诺或法定服务水平的企业和政府平台,还要加上一次中断所对应的合同或监管处罚,因为一个没有冗余的前门,是你在不知不觉中代表其背后每一个服务、每一位公民所承担的可用性风险。

  5. 我们是否已经度量出网格实际收取的 sidecar 税,是否有采用无 sidecar 和 eBPF 替代方案的计划,还是在盲目地为其买单? 在大规模场景下,每个 pod 的代理在计算和延迟上的开销不再是隐形的,而会成为预算中实实在在的一项,然而许多团队运行着成千上万个 sidecar,却从未度量过其成本。这里的张力在于:一边是 sidecar 模型的成熟度和隔离性,另一边是每节点代理、无代理库或内核 eBPF 方案更低的开销()后者更新,但会牺牲一定的隔离性,或增加按语言区分的依赖。带上实测数据:整个舰队中代理消耗的内存和 CPU、每跳增加的尾部延迟,以及网格在你计算账单中所占的比例。对于在数千个 pod 上运行网格的大型企业,或受预算审查的政府平台而言,这是监督部门迟早会追问的一项支出决策,因此了解这项税负、以及你的平台选择是否为更廉价的替代方案保留了空间,是基本的尽职调查。

  6. 每一类客户端是否真的都需要自己专属的前端专属后端,还是我们即将为那些实际上想要相同东西的客户端,在多个网关间重复逻辑? BFF 模式解耦了各个客户端团队,让每个团队都能演进出量身定制的契约,但每新增一个 BFF,就意味着多了一个需要运行、保护并保持同步的网关,而它们之间重复的逻辑会悄悄变成一项维护税。相互竞争的考量是:团队自主性和针对特定客户端的效率,对比众多近乎相同的网关所带来的运维成本和逐渐漂移。带上证据说明客户端的需求究竟存在多大差异:负载大小、往返次数、版本发布节奏,以及在使用单一共享网关的情况下,一个客户端的变更多久会阻塞另一个客户端一次。在拥有众多客户端团队的大型组织中,或是同时服务公共 Web 应用、移动应用和合作伙伴集成的政府平台中,诚实的问题是:这种差异是否足以证明这种扩张的合理性,因为为所有想要相同形态的客户端各建一个 BFF,是你将为之付出数年维护代价的野蛮生长。

Sector lens

创业公司。 上线一个 API 网关,跳过服务网格。在只有少数几个服务和一个小团队的情况下,单一网关就能处理身份验证、限流和组合,而一个共享客户端库能以远低于控制平面的成本为你提供 mTLS 和重试。你最稀缺的资源是工程注意力,因此一个你运维不了的网格是负担,而不是护城河。让网关保持足够的冗余,以扛住单个节点宕机,只有当服务数量和语言多样性真正提出要求时,才重新考虑网格。

小型企业。 你没有平台团队去运维网格,因此要依赖托管平台或网关产品开箱即用的能力:托管式 TLS、内置限流,以及一个托管网关,而不是你自己打补丁维护的网关。把这个选择框定为”买”还是”造”,然后选择”买”,因为托管 API 网关的成本低于自行运维所需的工程师工时。把内部服务间加密当作平台提供的一项功能,而不是你需要配备人手的项目。

企业。 在拥有数百个服务、横跨众多团队和语言的情况下,网格值得其复杂度,而真正的工作在于治理:一份书面的分工,确保网关和网格永远不会重复处理同一个关注点;统一的 mTLS 和遥测;以及一个负责代理升级和证书轮换的平台团队。审计人员希望有一个单一、可证明的访问与加密执行点,因此要标准化接口,让策略成为团队继承而来的东西,而不是各自重新实现的东西。把 sidecar 税作为预算中真实的一项来管理,并为无 sidecar 方案保留空间,以免日后被锁死在无法采用更廉价方案的境地。

政府。 采购规则、透明度和公共问责制塑造着架构。把网关和网格视为监管机构所期望的零信任骨干:每一位公民和合作伙伴都在前门经过身份验证,每一次内部调用都通过工作负载身份进行验证和加密,并且每个执行点都留有审计轨迹,证明访问在何处被检查、流量在何处被加密。优先选择开放标准和可移植配置,而不是采购规则可能禁止的专有锁定,并在适当的情况下公开平台在数据跨越每个边界时如何保护公民数据。

Examples

创业公司。 一家十二人的创业公司在单个 API 网关背后运行着八个服务。网关处理所有南北向工作:通过令牌检查验证用户身份、按套餐执行限流以防止免费用户拖垮系统,并把几个啰嗦的端点组合成对移动端友好的单次调用。对于东西向流量,他们有意跳过了服务网格,因为用两种语言编写的八个服务并不能证明引入控制平面的合理性。取而代之的是,他们通过一个共享客户端库以及所用平台内置的证书管理来获得 mTLS 和重试能力,并用一个轻量级代理来采集追踪数据。后来当他们新增一个对负载要求更苛刻的专用移动客户端时,就在现有 Web 网关之外引入了一个移动端前端专属后端。他们每年都会重新审视网格这一问题,并始终正确地判断:他们尚未跨过让网格值得引入的那道门槛。

企业。 一家跨国银行在众多团队和语言之间运行着数百个服务,在这样的规模下,服务网格值得其复杂度。每个服务默认都获得工作负载身份和 mTLS,因此所有内部流量都经过身份验证和加密,而无需任何团队编写加密代码,这既满足了安全组织的要求,也满足了希望拥有单一可证明执行点的审计人员的要求。网格按策略统一执行重试、超时和熔断,并为金丝雀发布逐步转移流量,使一次糟糕的部署只会影响百分之一的用户,而不是一次性影响所有用户。一层 API 网关面向外部和合作伙伴流量,负责身份验证、配额和版本管理,并有一条明确的书面规则:内部重试只存在于网格中,外部限流只存在于网关中,从而使这两层永远不会重复处理同一个请求。来自每个代理的一致遥测数据汇入中央可观测性平台,使一位值班工程师就能跨数十个服务跳转追踪一个请求。

政府。 某国家税务机构对面向公民的申报平台进行现代化改造,把网关和网格视为监管机构所要求的零信任架构的骨干。API 网关是受控的前门:每一位公民和每一个合作伙伴都在此经过身份验证和授权,外部限流在申报截止日激增期间保护系统,旧客户端格式则在边缘被转换,使遗留集成得以继续工作。在它背后,服务网格为每个内部服务赋予加密身份,并在每次调用中强制执行 mTLS,因此没有服务仅仅因为身处网络内部就被信任,访问策略明确列出了哪些服务可以调用哪些服务。由于该平台出于弹性考虑分布在多个数据中心,网格将同样的身份和加密保障扩展到各个集群之间,从而在全国范围内提供一致的零信任网络。每个执行点都会产生审计轨迹,使该机构能够向监督机构准确证明访问在何处被检查、流量在何处被加密。

Business case: motivations, ROI, and TCO

网关的回报显而易见,通常也相当可观。你不必让每个服务都重新实现身份验证、限流和请求日志,而是在边缘统一构建一次,让每个服务都继承它们。一项安全修复或一条新的限流策略只需一次部署即可上线,而不是一百次,这缩短了修复漏洞所需的时间,也降低了每次审计的成本,因为只有一个地方需要检查。组合和版本管理功能减少了客户端的往返次数,让你能够在不破坏调用者的情况下演进 API,从而既降低了延迟成本,也降低了团队之间的协调税。对几乎任何拥有外部客户端的系统而言,网关都能很快收回自身的成本。

服务网格的商业论证更为微妙,因为它的总拥有成本是真实且持续存在的。你要为代理的计算和延迟付费,要为运维控制平面的工程师付费,还要为调试一个新层所需的学习曲线付费。当替代方案(按服务、按语言各自实现 mTLS、重试和遥测)会带来更大的成本,且更糟的是会以制造安全漏洞和中断的方式变得不一致时,这份成本就是合理的。在高规模场景下,网格能把上百个脆弱的手工实现方案,转化为一个统一、可证明的方案,其投资回报体现为更少的安全事件、通过流量转移实现的更快速安全部署,以及显著改善的可观测性。低于该规模时,诚实的计算往往更倾向于库加网关,而有纪律的做法是继续等待。要向领导层论证这一点,应把网关与他们已经在追踪的指标(漏洞修复时间、审计成本、API 协调开销)联系起来,把网格与安全事件遏制、部署安全性,以及它所取代的多语言重复实现成本联系起来。

Anti-patterns and pitfalls

  • 过早引入网格: 在只有少数几个服务时就采用完整的服务网格,买下一个控制平面来解决一个你尚不存在的问题。
  • 重复重试: 网关和网格都在重试,导致一次客户端调用成倍增长为后端的重试风暴,放大了中断的影响。
  • 超时反转: 内层超时长于外层超时,导致调用方放弃等待,而被调用方仍在继续生成一个不会有人读取的响应。
  • 网关变成单体: 把业务逻辑塞进网关,直到它变成一个每个团队都必须协调才能修改的共享瓶颈。
  • 单点故障: 只运行一个没有冗余的网关实例,导致前门一旦失效,就会拖垮其背后的每一个服务。
  • 信任网络本身: 把边界内的任何事物都当作安全的,导致一个被攻破的服务就能横向移动、触及一切。
  • 职责重叠: 没有书面的分工,导致同一个关注点被两层意外地同时处理,且没有人知道哪一层才是权威来源。
  • BFF 泛滥: 在客户端实际想要相同东西的情况下,仍为每个客户端各建一个前端专属后端,使网关数量成倍增加、逻辑重复,却没有任何收益。
  • 忽视 sidecar 税: 部署成千上万个 sidecar 却从不度量其计算和延迟开销,事后却疑惑预算都花到哪里去了。

Maturity model

  • 第 1 级,启动: 服务之间直接通信,没有共享层。身份验证、重试和超时都按服务手工编码,且不一致。内部流量往往是明文的,仅因处于网络之内就被信任,也没有单一的地方来执行策略或观察流量。决策是被动的,随着问题浮现而逐个服务地做出。
  • 第 2 级,发展: API 网关面向外部流量,集中处理身份验证、限流和路由,但东西向关注点由共享库处理,且采用情况参差不齐。部分服务拥有 mTLS,许多服务则没有。团队认识到南北向与东西向的区别,但职责划分仍是非正式的,各团队之间的做法也各不相同。
  • 第 3 级,标准化: 南北向和东西向关注点被清晰分离,并在整个组织内应用一份书面分工。网关负责外部身份、配额和组合;网格或一个一致的库层负责内部 mTLS、重试和遥测。重叠问题已被解决,没有关注点被重复处理,内部流量默认经过加密和身份检查,标准被记录在案并得到强制执行,而不是留给各团队自行决定。
  • 第 4 级,管理: 流量层被度量并依据基线加以控制。你会追踪每跳的计算开销和尾部延迟形式的 sidecar 税、网关和网格相对于错误预算的可用性、重试放大和超时反转事件、以内部调用百分比表示的 mTLS 覆盖率,以及在整个舰队推送一次策略变更所需的时间。指标决定决策:代理升级、新的重试预算或金丝雀规则都要依据相对基线的数据来判断,而不是凭直觉,任何偏离标准的情况都会触发修复。
  • 第 5 级,编排: 网关和网格是成熟零信任架构的策略执行点,身份在跨集群、跨区域的每一跳都会被检查。流量转移驱动着安全的渐进式交付,可观测性统一而丰富,组织会持续评估并在划算之处采用无 sidecar、无代理和 eBPF 方案。流量层与安全、交付和容量规划深度集成,并随着规模、语言和风险状况的变化而不断调整。

Ideas for discussion

  1. 如果你的单一 API 网关此刻宕机,将有多少服务变得不可访问?你让前门具备冗余的计划是什么?
  2. 你的系统中,哪个关注点目前同时被网关和某个服务(或某个库)处理?你要如何证明它没有被重复处理?
  3. 在服务和语言达到多少数量时,你的团队才会一致认为网格终于值得其复杂度?你们距离这条界线还有多远?
  4. 如果明天有攻击者攻破了某个内部服务,它能触及哪些其他服务?什么样的身份检查能阻止它?
  5. 无 sidecar 或基于 eBPF 的网格方案对你的平台而言是否已经足够成熟?你会度量什么来做出判断?
  6. 你那些存在差异的客户端是否真的各自都需要专属的前端专属后端,还是你即将重复本可以共享的逻辑?

Key takeaways

  • 将南北向流量(由 API 网关处理)与东西向流量(由服务网格处理)分开;各自负责自己的方向,混淆二者会导致重复处理。
  • 网关集中处理路由、身份验证、授权、限流、配额、请求转换、组合和版本管理,使服务保持精简,策略集中于一处。
  • 服务网格在不修改服务代码的前提下为你提供 mTLS、流量转移、平台级重试与熔断,以及统一的可观测性,经典实现方式是通过 sidecar 代理。
  • 只有当服务和语言的数量使逐服务接线成为更昂贵的路径时,才采用网格;低于这一门槛,库加网关更胜一筹,而 sidecar 税确实值得留意。
  • 把网关和网格视为零信任架构的策略执行点,把每个横切关注点恰好分配给一层,并在跨集群的每一跳都检查身份。

References and further reading

  • Sam Newman, Building Microservices: Designing Fine-Grained Systems
  • Chris Richardson, Microservices Patterns: With Examples in Java
  • Lee Calcote and Zack Butcher, Istio: Up and Running
  • Ken Owens、Alois Reitbauer 等;CNCF 云原生全景图及服务网格文档
  • Evan Gilman and Doug Barth, Zero Trust Networks: Building Secure Systems in Untrusted Networks
  • Scott Rose, Oliver Borchert, Stu Mitchell, and Sean Connelly, Zero Trust Architecture, NIST Special Publication 800-207
  • Susan Fowler, Production-Ready Microservices