当一个系统有几十个服务时,同样的网络代码会出现在每一个服务里:重试、超时、服务间 TLS、每次调用的指标、发布时的流量拆分。服务网格把这些代码从服务中移出,放到网络路径上,由一个团队统一为所有服务配置。
本文讲解网格是如何构成的、控制平面和数据平面各自做什么、自动 mTLS 如何工作,以及让网格对许多团队来说并不合适的那些成本。图中展示的是经典的 Sidecar 设计。
数据平面与控制平面
数据平面 是一组代理,位于服务之间每个请求的路径上。在 Sidecar 模式下,每个 Pod 都有自己的代理容器。应用用普通 HTTP 与本地代理通信,线路上的一切都由代理处理:加密、重试、负载均衡、指标。Istio 使用 Envoy 作为 Sidecar;Linkerd 则使用自己专门打造、用 Rust 编写的代理。
控制平面 从不接触请求流量。它监视集群中的服务和端点,把你的策略转换成代理配置,将配置下发到每个代理,并签发代理用来证明自身身份的证书。在 Istio 中,这就是 istiod;参见 Istio 架构页面。Linkerd 的 架构参考 介绍了对应的组件。
自动 mTLS 与工作负载身份
每一对服务之间的双向 TLS,是说服大多数团队采用网格的功能。每个代理都会从控制平面获得一张标识工作负载的短期证书,通常是根据 Kubernetes 命名空间和服务账号生成的 SPIFFE ID,例如 spiffe://cluster.local/ns/shop/sa/orders。两个代理建立连接时,双方都出示证书、都验证对方的证书,连接随之加密。
这带来两个重要的结果:
- 身份取代了网络位置。 策略可以写成“payments 只接受来自 orders 服务账号的调用”,这比“来自这个子网”更有力。
- 轮换是自动的。 证书的有效期只有几个小时,没有人需要为续期提工单。
当所有服务都进入网格后,开启严格模式,让明文连接被拒绝,而不是被悄悄接受。
一个请求穿过网格的过程
跟踪一次从 orders 到 payments 的调用:
- orders 应用向 payments 的服务名发送一个普通 HTTP 请求。流量重定向把它发给本地的 Sidecar,而不是直接发到网络上。
- orders 的 Sidecar 从控制平面下发的配置中找到健康的 payments 端点,用自己的负载均衡算法选出一个,并应用这条路由的超时设置。
- 它打开(或复用)一条到 payments Sidecar 的 mTLS 连接,并出示 orders 的证书。
- payments 的 Sidecar 验证证书,检查授权策略(orders 是否被允许调用 payments?),然后通过 localhost 把请求转发给 payments 应用。
- 两个 Sidecar 都会记录这个请求、它的状态和延迟。如果调用以可重试的状态失败,orders 的 Sidecar 可以在重试预算内重试。
两个应用都不知道发生了这些事。这正是网格的意义,也是它的风险:代码里看不到的行为,必须在别的地方看得到,比如架构图上。
逐步落地网格
分步骤采用。先安装控制平面,再以宽松(permissive)模式一个命名空间一个命名空间地接入服务,让明文流量仍然可用,观察指标,然后把每个命名空间切换到严格 mTLS。授权策略最后再加,从保存敏感数据的服务开始。
流量策略
由于每次调用都经过代理,网格可以在所有地方执行同样的规则:
- 超时和重试, 并配合重试预算,避免故障期间重试成倍放大。
- 熔断和异常检测: 不再向持续失败的实例发送流量。
- 流量拆分, 按权重或请求头拆分,网格中的金丝雀发布就是这样实现的。
- 故障注入, 用于测试:给一定比例的调用加入延迟或错误。
要小心两处同时重试。如果应用重试、Sidecar 也重试,一次失败的调用就会变成很多次调用。为重试指定唯一的负责方,并写下来。
(几乎)免费的可观测性
代理会按来源和目的地上报请求数、错误率和延迟,而无需修改应用。它们还能创建并传播链路追踪的 span,但应用仍然必须把入站请求中的追踪头传递到出站请求上,否则链路会在每个服务处断开。
Sidecar 与 Ambient 模式
Sidecar 有实实在在的成本:每个 Pod 里的代理都要占用 CPU 和内存,给每一跳增加延迟,还会让 Pod 启动和升级更复杂。Istio 的 Ambient 模式 去掉了每个 Pod 的 Sidecar。一个名为 ztunnel 的节点级代理负责 mTLS 和四层策略,可选的 waypoint 代理只为需要的服务提供七层功能。代价是运维模式不同,而且在生产环境中的积累比 Sidecar 少好几年。
架构图
图表内容
- Ingress gateway
- orders pod
- Sidecar proxy
- orders app
- payments pod
- payments app
- inventory pod
- inventory app
- Control plane
- Metrics and traces
- mTLS
- localhost
- config + certificates
这张图的读法如下:
- 左侧: 入口网关接收外部流量,并与 orders Pod 中的 Sidecar 建立 mTLS 连接。
- 中间: 在每个 Pod 内,Sidecar 通过 localhost 把请求转发给应用。orders 的 Sidecar 通过 mTLS 调用 payments 和 inventory 的 Sidecar;应用从不处理证书。
- 底部: 控制平面向代理下发配置和证书(虚线连线)。它不在请求路径上。
- 右侧: Sidecar 上报指标和链路追踪(点线)。Markdown 块展示了 payments 的一条策略:谁可以调用它、超时和重试、异常实例驱逐以及严格 mTLS。
什么时候不该用网格
- 只有少数几个服务,或者是一个单体加几个卫星服务。用一个库或一个 API 网关,就能以很小的成本满足需求。
- 没有团队负责它。网格是一种基础设施,需要升级、调试技能和值班经验。
- 主要目标只是加密。许多云平台和 CNI 插件无需完整的网格就能加密 Pod 之间的流量。
网格架构图上应该画什么
- 两个平面, 清楚地分开,并把控制平面画在请求路径之外。
- TLS 从哪里开始、到哪里结束: 代理到代理之间是 mTLS,localhost 上是普通 HTTP。
- 身份, 至少要标出有访问策略的服务。
- 会改变行为的策略: 超时、重试、熔断。
Kubernetes 架构图 展示了网格所运行的集群,零信任架构图 解释了基于身份的策略为何重要,微服务架构图 则介绍了服务本身。
在 Archboard 中构建
- 使用 Google 或 GitHub 登录,并新建一张图。
- 添加 C4 模型节点包中的一个 容器 表示入口网关。
- 为每个 Pod 添加基础节点包中的一个 框架,在其中放入表示 Sidecar 的 组件 和表示应用的 容器。
- 添加一个表示控制平面的 容器,以及 DevOps 节点包中的 指标。
- 在 Sidecar 之间画出 mTLS 连线,从控制平面画出虚线,再画出表示遥测数据的点线,并添加一个基础节点包中的 Markdown 块,写明某个服务的策略。
这个示例共用 13 个图表节点,在 Free 套餐的限制之内。
常见问题
服务网格和 API 网关有什么区别? API 网关管理从外部进入系统的流量,服务网格管理系统内部服务之间的流量。许多系统两者都会使用。
服务网格会增加延迟吗? 会。每经过一个代理跳都会增加一点延迟,通常每跳远低于几毫秒,另外代理还要占用 CPU 和内存。请针对自己的流量进行测量。
使用网格需要修改应用代码吗? 大多数情况下不需要。例外是链路追踪:应用必须传递追踪头,链路才能在服务之间串起来。
什么是 Ambient 模式? 一种不使用 Sidecar 的 Istio 数据平面:由节点级代理负责 mTLS 和四层,可选的 waypoint 代理在需要的地方提供七层功能。
网格应该由谁负责? 平台团队。服务团队通过策略使用它的功能,但总得有人负责升级、监控和调试它。