站内已经有三块内容离「Istio
控制面」很近,却都不回答同一个问题。Envoy 数据面系列 09–11 篇
把 xDS 消费侧讲透:资源树怎么按名字引用、ACK/NACK
是什么协议承诺、warming
为什么让「已推送」不等于「已生效」。但那三篇的前提永远是「假设某个管理服务器已经给出了合法的
LDS/RDS/CDS/EDS」——管理服务器内部发生了什么,不在 Envoy
系列的范围里。Service
Mesh 讲清了 sidecar 税与 Ambient
的代价地图,但落点是「要不要上 Mesh」的选型判断,不是「一次
VirtualService apply 之后控制面做了什么」。Gateway
API
定义了南北向流量的资源模型与角色分层,但没有回答这些资源如何被某个具体的网格控制面消费、翻译、推送。
三块内容叠在一起,读者能看到「代理侧怎么消费」「要不要上代理」「资源该长什么样」,唯独看不到中间那台真正做事的机器:istiod 如何把一份 CRD 或 Gateway API 资源,编译成某一个具体代理上的一份 xDS 资源图,推给谁,推送本身是否成功,这件事和 Envoy 端的 warming 状态机是两回事。 本篇是系列第一篇,只做三件事:钉住这个缺口的精确边界、给出后续 15 篇共用的五条坐标系,以及明确划出本系列不会展开的范围。
本文是「Istio / 网格控制面内核」系列第 1 篇(共 16 篇)。→ 系列目录
版本锚定:Istio 1.30.3(
istio/istiotag);架构叙述以该 tag 下architecture/networking/pilot.md、pilot/pkg/xds/、pkg/model/proxy.go为准。数据面消费侧机制外链 Envoy v1.39.0 系列,不在本系列重写。
一、缺口:三份已有内容分别停在哪一层
把四份内容按「回答什么问题」摆在一起,缺口的形状就很清楚:
| 内容 | 回答的问题 | 明确不回答 |
|---|---|---|
| Envoy 09–11 | 给定合法 xDS 响应,Envoy 怎么消费、ACK/NACK、warming | 响应是谁生成的、生成依据是什么 |
| architecture/76 | sidecar/Ambient 的部署代价与选型权衡 | 控制面内部的翻译与推送机制 |
| k8s-network/18 | Gateway API 资源模型与角色分层 | 某个具体控制面(Istio)如何消费这些资源 |
| 本系列 | istiod 如何把配置编译成某代理的资源图,推给谁,何时算失败 | Envoy 侧消费细节(外链)、Mesh 税叙事重写、CRD 教程 |
这张表本身就是本系列的立项理由:读者已经具备「消费侧怎么工作」「值不值得上」「资源长什么样」三种视角,但没有「生产侧怎么工作」的视角。三者合起来才是一次配置变更的完整生命周期——从
kubectl apply 到某个 Pod 里的 Envoy
真的按新规则转发流量。
二、谱系:xDS 从协议变成控制面产品
xDS 最初只是 Envoy 用来描述「动态配置」的一组 gRPC/REST 接口;它变成今天这种带调度、去抖、多客户端类型的控制面产品,经过了三次可考证的分叉。
第一次分叉:从「多流」到「单流可排序」。
Istio Pilot 早期(istio/pilot 仓库,2017
年前后)为 LDS/CDS/EDS 各开一条独立 gRPC 流。2018 年 4 月的
istio/istio#4670「Switching
to ADS」(作者 costinm)把这一套改成了单条
StreamAggregatedResources,提交说明直接写明动机:EDS
不支持按 cluster 批量请求,多流模式下会为每个 cluster
各开一条 Cluster*Endpoints 流,「不
scale」。这次改动是 Istio 主动把自己的生产侧收敛到 Envoy
后来在协议文档里正式定义的 ADS
语义上——这是一条可以在提交记录里直接核对的分叉点,不是后验的历史叙事。
第二次分叉:从「五个微服务」到「一个进程」。
Istio
早期把服务发现(Pilot)、配置校验(Galley)、证书签发(Citadel)、注入
webhook 拆成独立部署。Istio
1.5(2020)的官方博客《Introducing istiod: simplifying the
control
plane》给出的理由很直接:这些组件之间没有独立伸缩或独立团队交付的收益,拆分只带来了运维复杂度,于是合并成单一二进制
istiod。这次合并不改变 xDS
协议本身,但决定了本系列第 2 篇要讲的进程边界。
第三次分叉:从「通用 xDS
类型」退回「领域特化协议」。 Ambient 模式给 ztunnel
设计配置协议时,architecture/ambient/ztunnel.md(1.30.3)记录了一个反直觉的决定:继续用
xDS 传输协议(gRPC 双向流、ACK/NACK
语义),但不用 Listener/Cluster
这些通用资源类型,而是自定义了
Address、Authorization
两种精简类型。文档给出的理由是实测:用 Envoy
通用类型表达一条 mTLS 语义要几十行 Protobuf,而 ztunnel 用带
Istio 语义的字段(例如
ExpectedTLSIdentity)能把同样的信息压缩到十分之一体积与
CPU
开销。这是「协议要不要为特定客户端做领域特化」的一次工程判断,第
3 篇会展开。
三次分叉串起来是同一条主线:「用什么协议传」和「协议里装什么类型」是两个可以独立演化的维度——ADS 解决的是传输层的排序问题,istiod 合并解决的是运维复杂度问题,ztunnel 的自定义类型解决的是资源效率问题。理解这条主线,才能理解为什么本系列要单独用一篇(第 3 篇)讲「谁在订阅」,而不是默认所有代理都在消费同一套 LDS/RDS/CDS/EDS。
三、五条坐标系
后续 15 篇都会落到下面某一条轴上,本篇只钉名字。
3.1 资源来源轴
K8s
Service/EndpointSlice、VirtualService/DestinationRule、Gateway
API
HTTPRoute、PeerAuthentication/AuthorizationPolicy
等输入,先要变成 istiod 内部统一的
model.Service、model.PushContext
等结构,才谈得上生成任何 xDS
资源。输入格式的多样性在这一层被吃掉——第 2
篇(Config Ingestion)与第 5–9
篇的翻译系列都落在这条轴上。
3.2 订阅身份轴
一个 Connection 连上来,istiod
先解析它的节点 ID(type~ip~id~domain 四段式)和
NodeMetadata,得到一个
model.Proxy,其 Type 字段是
SidecarProxy、Router、Waypoint、Ztunnel、Agentgateway
之一(pkg/model/proxy.go,1.30.3)。不同类型订阅的资源集合、走的
Generator 都不同——第 3 篇专门讲这条轴。
3.3 推送图轴
一次配置变更从 K8s watch 事件开始,经过 debounce
合并、PushContext 重算、PushQueue
排队,最后由某个 push worker 调用 pushXds
生成并发送
DiscoveryResponse。这条链路的每一段都可能是延迟或丢推送的来源——第
4 篇专门画这张图。
3.4 一致性 / warming 责任轴
istiod 侧的 ConfigUpdate
只表示「收到了变更」;AdsPushAll
只表示「已经尝试推送」;Envoy 侧的 ACK
只表示「协议校验通过并打算应用」(Envoy
第 10 篇);真正决定流量是否已切换的是 Envoy 的 warming
状态机(Envoy
第 11 篇)。「控制面已推送」「代理已
ACK」「流量已生效」是三个独立事件,排障时把它们混为一谈是生产事故复盘里的高频误判——第
4、10–11、15 篇都会回到这条轴。
3.5 作用域轴
Sidecar、Telemetry、EnvoyFilter
这些资源不生成新的 xDS
类型,而是收窄或改写「某个代理该看到哪些资源」。ProxyNeedsPush
之类的过滤逻辑决定了一次 Service
变化到底要不要推给某个具体代理——作用域算错,代价是要么推送风暴,要么该收到更新的代理没收到。第
9 篇专门展开。
flowchart TD
subgraph axes ["Five coordinate axes"]
A1["Source axis<br/>CRD/K8s objects to model"]
A2["Identity axis<br/>sidecar/ztunnel/waypoint/gateway"]
A3["Push graph axis<br/>watch to debounce to ADS"]
A4["Consistency axis<br/>pushed vs ACKed vs serving"]
A5["Scope axis<br/>Sidecar/Telemetry/EnvoyFilter"]
end
A1 --> A3
A2 --> A3
A3 --> A4
A2 --> A5
A5 --> A3
四、16 篇地图与本篇边界
flowchart LR
p1["01 Overview"] --> p2["02 istiod Process"]
p2 --> p3["03 Proxy Identity"]
p3 --> p4["04 Push Graph"]
p4 --> p5["05-09 Translation"]
p5 --> p10["10-12 Scale & Ownership"]
p3 --> p13["13-14 Ambient & Linkerd"]
p10 --> p15["15-16 Troubleshoot & Selection"]
p13 --> p15
| 部分 | 篇目 | 一句话 |
|---|---|---|
| 坐标与推送图 | 01–04 | 缺口、istiod 进程、代理身份、推送图 |
| 翻译层 | 05–09 | Service/Endpoint、VirtualService、DestinationRule、Authn/Authz、Sidecar/EnvoyFilter |
| 规模与 Ambient | 10–14 | 生产侧 SotW/Delta、NACK/warming 责任、多集群、Ambient、Linkerd 对照 |
| 收束 | 15–16 | 排障对齐、选型与开放问题 |
全系列 01–16 已发布。本篇之后按 02 → 03 → 04 补齐坐标系;翻译层可直达第 5 篇(Service/Endpoint → CDS/EDS),推送经济学可直达第 10 篇(生产侧 SotW/Delta);收束见第 15–16 篇。
五、与站内三份内容的分工
| 站内内容 | 负责 | 本系列不重复 |
|---|---|---|
| Envoy 系列 | 消费侧协议、warming 状态机、排障与选型 | Filter/HCM/连接池内核;Hot restart |
| architecture/76 | sidecar 税、Ambient 代价地图、选型判断 | 未实测延迟排行、云成本报价 |
| k8s-network/18 | Gateway API 资源模型、角色分层、GAMMA | CRD 字段级教程 |
| 本系列 | istiod 翻译、订阅身份、推送图、一致性责任边界 | 上述三者已覆盖的内容 |
读法:只要「建立 Mesh 坐标系」→ architecture/76 足够;要「代理侧卡在哪」→ Envoy 系列;要「控制面推了什么、推给谁、失败算谁的」→ 本系列。
六、明确不写什么
以下内容读者可能期待出现在「Istio 控制面」标题下,本系列刻意不展开,原因写清楚:
- CA 证书签发与轮转的运维手册:第 2 篇会点出 CA 是 istiod 的组成部分之一,但不做 SPIFFE/mTLS 证书运维全书——这是独立的运维主题,塞进控制面内核会稀释主线。
- VirtualService/DestinationRule 字段级配置菜谱:第 6–7 篇只讲字段如何被翻译成 xDS 结构,不做「如何配置金丝雀发布」这类应用教程。
- Gateway API CRD 教程:字段级用法留给 k8s-network/18,本系列只讲翻译到 Istio 内部模型时的冲突边界(第 6 篇)。
- Istio vs Linkerd 谁更好:第 14 篇只做控制面机制对照(xDS vs 非 xDS gRPC),不给产品排名。
- Wasm/EnvoyFilter 编程 SDK:第 9 篇只讲作用域合并顺序,不教怎么写扩展代码。
- 未实测的推送延迟或吞吐数字:凡涉及性能判断,本系列遵守「有环境再跑」原则;没有实测环境时只给出命令语义,不编造输出。
七、参考资料
官方文档 / 规范(A)
- Istio 1.30 文档,Architecture(istiod = discovery + config + CA 的官方定位)。
- Istio 1.30 文档,Ambient / Architecture(ztunnel 与 istiod 的 xDS 关系)。
- Envoy Proxy, xDS REST and gRPC protocol, docs v1.39.0(ADS 定义,供第二节对照)。
源码 / 提交记录(A)
istio/istiotag 1.30.3:architecture/networking/pilot.md(istiod 三段式:Config Ingestion / Translation / Serving)。istio/istiotag 1.30.3:architecture/ambient/ztunnel.md(自定义 xDS 资源类型的工程取舍)。istio/istiotag 1.30.3:pkg/model/proxy.go(NodeType常量:SidecarProxy/Router/Waypoint/Ztunnel/Agentgateway)。- istio/istio#4670 “Switching to ADS”(2018-04-04 合并,Pilot 从分类型流迁移到 ADS 的提交记录)。
官方博客(B)
- Istio 官方博客,Introducing istiod: simplifying the control plane(2020,Pilot/Galley/Citadel/注入 webhook 合并动机)。
站内对照
- Envoy / 数据面代理内核(消费侧先修,尤其第 9–11、16 篇)
- Service Mesh
- Gateway API
实验台账
- 本篇为定位与路线,不涉及命令输出;无本机 istiod
环境,后续篇目凡涉及
istioctl或推送延迟数字,均遵守「有环境再跑」,无环境时只给命令语义。
八、小结
- 缺口是精确的:Envoy 系列管消费侧、architecture/76 管选型、k8s-network/18 管资源模型,三者都不管「istiod 内部如何翻译与推送」——这正是本系列的领地。
- 三次谱系分叉(ADS 化、istiod 合并、ztunnel 自定义协议)说明 xDS 的「传输协议」与「资源类型」是两个可以独立演化的维度,后续篇目会反复用到这个区分。
- 五条坐标系(来源、身份、推送图、一致性、作用域)是全系列的共用语言,第 2–4 篇立刻会用到前三条。
- 明确的不写清单,避免本系列膨胀成 Istio 全功能手册。
→ 系列目录 · 下一篇:istiod 进程模型
同主题继续阅读
把当前热点继续串成多页阅读,而不是停在单篇消费。
Istio / 网格控制面内核:从 CRD 到 xDS
补齐 Envoy 数据面 xDS 消费侧与 Mesh 选型叙事之间的控制面内核:istiod 推送图、CRD/HTTPRoute 翻译、Ambient 多客户端、NACK/warming 责任与排障对齐。
【Istio 控制面】istiod 进程模型:discovery、config、CA 与注入的边界
拆解 pilot-discovery 二进制内部的组件化启动顺序:CA 为何先于 Discovery 初始化、注入 Webhook 与配置校验为何是独立 HTTPS 服务、以及哪些组件真正位于 xDS 推送热路径上。
【Istio 控制面】代理身份与订阅:sidecar、ztunnel、waypoint、Gateway 谁在订阅什么
拆解 istiod 如何从节点 ID 与 mTLS/JWT 得到一个 model.Proxy 身份,以及 NodeType 如何决定该连接会订阅哪一套 xDS 资源类型:sidecar/Router 走标准 LDS/RDS/CDS/EDS,ztunnel 走自定义 Address/Authorization,waypoint 是 Envoy 但推送触发条件不同。
【Istio 控制面】Service / Endpoint / Workload → CDS/EDS:istiod 如何拼出一个 Cluster
拆解 istiod 如何把 K8s Service、EndpointSlice、WorkloadEntry 聚合进内部服务模型,再经 CdsGenerator/EdsGenerator 分别生成 Cluster 与 ClusterLoadAssignment;钉住命名契约与部分推送边界,subset 留给 DestinationRule 篇。