前两篇把 RPC 框架 的 SDK 治理和 服务发现 的候选集讲透了。团队长到多语言、多团队时会出现新矛盾:每一种语言都重写一遍熔断/重试/mTLS/灰度? Service Mesh 的答案是:把这些横切能力放进与业务容器并肩的 Sidecar 代理,由统一控制面下发策略——业务进程尽量只写业务。
广告体系里常见「Java 竞价核心 + Go/Python 周边数据服务 + 若干遗留 Thrift」:指望每人把 Dubbo Filter 重写一遍不现实。本篇聚焦 Istio + Envoy 这条工业主流路径:架构、流量路径、安全与可观测、以及你必须正视的代价。
本文是微服务与 RPC 系列的第 3 篇。 系列顺序:① RPC 框架(开篇);② 服务注册与发现深挖;③ 服务网格 Service Mesh 深挖(本篇);④ API 网关与分布式追踪。
一句话定位:Service Mesh = 数据面 Sidecar(执行)+ 控制面(决策)——把服务间的路由、安全、观测从业务 SDK 下沉为基础设施;收益是一致性,账单是延迟、资源与运维复杂度。
TL;DR
- 数据面:每个 Pod 旁路一个 Envoy Sidecar(或 Ambient/eBPF 变体),劫持进出流量执行策略。
- 控制面(Istiod):聚合服务发现、下发 xDS(监听/路由/集群/端点)、证书与策略。
- 流量治理:超时、重试、熔断、异常值检测、金丝雀/A-B、故障注入——改 VirtualService/DestinationRule,业务零发版。
- 安全:网格内默认或按策略 mTLS(SPIFFE 身份),AuthZ 按服务身份而不是只靠网络 ACL。
- 可观测:Sidecar 自动出 Metrics/Access Log/Trace span,补齐「没埋点的服务」。
- 代价:每跳 +1 代理 CPU/延迟、内存占用、排障链路变长;小集群/超低延迟路径要慎重。
- 与 SDK 不是宿命对立:同构 Java 热路径可留 Dubbo;多语言与统一安全用 Mesh。
- AdTech 血泪:Istio 自动重试 + 业务 FailOver 双重重试,发布窗口流量放大失控。
Table of contents
Open Table of contents
1. 为什么需要 Mesh
SDK 模式(Dubbo Cluster、各类 Resilience 库)在同构、小团队里非常高效:零额外 hop,调优直观。痛点出在:
- 多语言:Go/Python/Node 要各自实现同一套治理,行为很难一致;
- 升级成本:改熔断参数常要跟着业务发版;
- 安全债:服务间明文、证书各管各的;
- 可观测缺口:没接 SDK 的服务在 Trace 里「消失」。
Mesh 把「服务间怎么说话」收归平台,用依赖韧性同一套思想,但是在代理层落地。它不消灭 RPC——契约、序列化、业务超时预算仍在应用层;它消灭的是「每门语言一份私有治理实现」。
另一条诱因是组织:平台团队想统一灰度、统一 mTLS、统一观测标准,而业务团队只想专注特征与策略。Mesh 是这种分工的物理落点。若组织里没有能对 VirtualService/DestinationRule 负责的平台 owner,上网格等于把配置复杂度均摊给每个业务——失败模式从「SDK 不一致」变成「YAML 不一致」,未必更好。
2. Sidecar + 控制面架构
控制面不转发用户请求;它只把「怎么转」教会数据面。
2.1 流量劫持
经典做法:用 iptables/nftables(或 CNI)把 Pod 的进出 TCP 重定向到 Sidecar 端口;业务以为自己连对端,其实出站先进 Envoy。新一代 Ambient Mesh / eBPF 试图去掉每 Pod 一个 Sidecar 的重税(ztunnel / waypoint),但概念仍是「数据面代理 + 控制面」。
排查「流量怎么被转走」时,要会看:Pod 的 iptables 规则、Envoy 的 listener/cluster、以及应用实际 dial 的地址。很多人第一次上网格时,会把「连不上」误判成业务 Bug,其实是劫持或 mTLS 握手失败。
2.2 xDS:控制面如何遥控 Envoy
Envoy 通过 xDS API 从控制面拉配置:
- LDS/CDS:监听与上游集群;
- EDS:Endpoints(发现结果);
- RDS:路由(路径、权重、匹配 header);
- SDS:证书密钥。
于是上一篇的「服务发现」在 Mesh 里变成:控制面看 K8s Endpoints / 注册中心 → EDS 推给 Sidecar。业务进程甚至可以不知道注册中心的存在。
配置下发有延迟与版本:刚改完 VirtualService,不是全集群瞬间一致。做金丝雀时要给传播窗口,别改完 1 秒就宣布成功。
2.3 Istio 里常见对象
| 对象 | 作用 |
|---|---|
| VirtualService | 路由、超时、重试、故障注入、镜像 |
| DestinationRule | 子集、负载均衡、连接池、outlier detection |
| PeerAuthentication / AuthorizationPolicy | mTLS 模式与访问控制 |
| Gateway | 南北向入口(常与下篇网关职责重叠/协作) |
| Sidecar 资源 | 限制出站范围,减小配置膨胀 |
配置膨胀是真实问题:服务一多,每条规则交叉,Envoy 推送配置变大、内存上涨。要治理 Sidecar 出站范围、合并规则、避免「每个服务全网互通的巨型 CDS」。
3. 网格内的调用路径与治理挂载点
治理挂在代理两侧与链路上;App 只看见「成功或失败」。
3.1 流量治理清单
| 能力 | 典型挂载 | 配置入口(Istio) |
|---|---|---|
| 超时 / 重试 | 出口 Sidecar | VirtualService |
| 熔断 / 连接池 | 出口 | DestinationRule |
| 金丝雀 / A-B | 出口路由权重 | VirtualService |
| 故障注入 | 出口/入口 | VirtualService |
| 限流 | 入口或外部 ratelimit | EnvoyFilter / 扩展 |
| 镜像(shadow) | 出口 | VirtualService |
这对广告灰度极友好:把 5% 流量切到新竞价策略版本,业务二进制不用改匹配规则。镜像流量可把生产请求复制到候选版本做暗流量验证,但要注意写接口禁止盲目镜像(双写预算就炸了)。
Outlier detection(异常值检测)类似「主动踢掉持续 5xx/超时的实例」,和发现层健康检查互补——它更贴近真实请求成败,而不是端口探活。参数要保守:连续失败阈值过低,一次毛刺就会踢光半边容量;过高又等同没开。建议与负载均衡里的慢启动(slow start)一起配:被踢后回池的实例先吃小流量再爬升。
金丝雀还有组织流程:权重从 0→1→5→25→100,每级卡住看错误率与业务 KPI(广告主消耗、填充率)。只看 HTTP 200 不够——竞价可能「技术成功、业务算错」。网格能切流量,但不能替你定义业务成功指标。
3.2 mTLS 与身份
网格为每个工作负载签发身份(SPIFFE URI),Sidecar 之间建立 mTLS。收益:
- 服务间通信默认加密;
- 授权基于服务身份(
bid-service才能调budget-service),比纯 IP ACL 稳; - 证书轮换由控制面做,业务无感。
迁移期常用 PERMISSIVE(明文与 mTLS 并存)再切 STRICT。切 STRICT 前必须确认所有调用路径都进了网格——漏网的脚本、批任务、旁路 NodePort 会突然全挂。
注意:mTLS 管的是东西向;南北向仍多在网关终结公网 TLS(下一篇)。
证书与控制面可用性绑定:控制面长时间不可用、SDS 续不上,会逐渐出现握手失败。网格的 HA 不是「多个 Envoy」就够,Istiod / 证书链路同样要进稳定性预算。对广告这种长连接多的系统,证书轮换窗口还要避开流量高峰。
3.3 可观测下沉
Sidecar 自动产生:RED 指标、访问日志、以及与 Trace 系统对接的 span。对「来不及埋点的老服务」特别有价值;完整的 Trace 传播与采样策略在第 4 篇展开。
网格 metrics 常成为容量与 SLO 的权威来源——但也要防止基数爆炸(别把高基数广告主 ID 打进目标标签)。
3.4 故障注入:把混沌变成配置
VirtualService 的 delay / abort 注入,能在预发验证:
- 下游 timeout 是否按 deadline 失败;
- 熔断是否打开;
- 降级逻辑是否真正返回兜底,而不是空等。
比「等生产偶然抖一下」便宜得多。但注入必须限定在 staging 或带染色 header 的流量——曾经有人把 abort 规则推到生产忘了删,那是另一种 war story。
4. 代价与 SDK 对照
Mesh 不是银弹,是「复杂度搬家」——从业务仓库搬到平台团队。
4.1 真实账单
- 延迟:每跳多一次用户态代理(通常几百微秒到数毫秒级,视配置与连接复用而定);
- 资源:每个 Pod 多一份 CPU/内存;节点密度下降;
- 排障:问题在 App、Sidecar、还是控制面?要统一看 Envoy stats、Istio 配置与业务日志;
- 版本兼容:数据面与控制面、CRD 与行为要对齐滚动;
- 启动顺序:Sidecar 未就绪时业务已发包 → 启动失败;需要 holdApplicationUntilProxyStarts 一类机制。
超低延迟交易/竞价热路径,往往保留短链路 SDK;Mesh 优先覆盖管理面、多语言外围、强制 mTLS 的区域。也能「网格内通信,但对某几个端口绕过劫持」做精确例外——例外清单要严管,否则安全模型被掏空。
4.2 何时上、何时不上
值得上:多语言、强合规(必须 mTLS)、渴望统一灰度与观测、平台团队能扛运维。
慎上:几十个服务以内的同构栈、极致延迟、还没有成熟的平台 SRE——先把 RPC 超时熔断与发现做对,往往 ROI 更高。
渐进路径:先只开可观测与 mTLS PERMISSIVE → 再开路由灰度 → 最后才碰重试/熔断等「会改变流量形状」的开关。每一步都要有回滚(VirtualService 一键打回)。
4.3 本地调试与「绕过网格」
开发机常常不在网格里。常见手段:
- 本地用
istioctl/ 端口转发打到集群内; - 对少数端口标注旁路(须审计);
- 契约测试仍直接打业务端口,网格策略用单独的集成环境验证。
没有「本地故事」的 Mesh 会逼业务绕路,最终生产与开发行为分叉——这比延迟多一毫秒更危险。
4.4 与 负载均衡 的关系
Mesh 的 DestinationRule 负载均衡(round_robin、least_request、ring_hash)与上一篇 RPC 客户端 LB 是同一族算法,只是执行点换了。选型原则不变:干净的 endpoints + 合适的策略 + 慢实例踢出。Outlier detection 是网格版的「主动健康意识」,别关着还怪 LB 不均。
参考
- Istio Authors. Istio Concepts:控制面/数据面、流量管理与安全模型。
- Envoy Project. Envoy Proxy Documentation:xDS、过滤器链与上游集群。
- William Morgan et al. What’s a service mesh?:Service Mesh 概念缘起与设计动机。
- SPIFFE. SPIFFE / SPIRE:工作负载身份与 mTLS 身份标准。
- Istio. Ambient Mesh Overview:降低 sidecar 成本的数据面演进。
- Envoy. Outlier Detection:主动踢除异常上游的机制说明。
- NIST. Zero Trust Architecture (SP 800-207):理解「按身份授权」与 Mesh mTLS 的政策语境。