前几篇讨论的推送机制默认了一个前提:连接到 istiod 的是一个 Envoy sidecar,按 Sidecar 作用域订阅一整棵 LDS/RDS/CDS/EDS 资源树。Ambient 模式打破了这个前提——同一个 istiod 现在要服务两种订阅形状完全不同的客户端:ztunnel(每节点一个,只处理 L4,甚至不走标准 Envoy 资源树),以及 waypoint(每命名空间/服务账号一个,是货真价实的 Envoy,但订阅范围按”挂载关系”而不是 Sidecar CR 决定)。
本文只回答控制面视角的问题:istiod 如何区分这两类客户端;它们各自订阅什么资源类型;推送过滤逻辑在 ambient 下有哪些还没做完的地方。Ambient 的税与形态图见 architecture/76,本文不重复;eBPF L4 数据面的对照见 k8s-network/13,本文只在必要处提一句边界,不展开。
本文是「Istio 控制面内核」系列第 13 篇(共 16 篇)。→ 系列目录
版本锚定:Istio 1.30.3;官方 Ambient 文档
istio.io/latest/docs/ambient/。
一、istiod 眼中的四种代理身份
pilot/pkg/model/context.go
里代理类型是一个封闭集合:
// pilot/pkg/model/context.go(Istio 1.30.3,节选)
var NodeTypes = [...]NodeType{SidecarProxy, Router, Waypoint, Ztunnel}Router
对应网关(含东西向网关),SidecarProxy 是传统
sidecar,Waypoint 和 Ztunnel 是
Ambient
引入的两种新身份。这不是文档层面的分类,而是每次推送决策都要判的类型字段——pilot/pkg/xds/xdsgen.go
的 xdsNeedsPush 第一行就是:
// pilot/pkg/xds/xdsgen.go(Istio 1.30.3,节选)
func xdsNeedsPush(req *model.PushRequest, proxy *model.Proxy) (needsPush, definitive bool) {
if proxy.Type == model.Ztunnel {
// Not supported for ztunnel
return false, true
}
...
}Waypoint/Ztunnel 走的不是给
sidecar
用的通用”是否需要推”判断——它们的资源类型体系本身就不一样,用同一套判断逻辑没有意义。
二、ztunnel:L4、per-node、不进 CDS/LDS/RDS 体系
官方 Ambient 概览的定位很直接:ztunnel 是用 Rust 写的、按节点部署(DaemonSet)的专用代理,职责严格限定在 L3/L4:mTLS、身份认证、L4 授权、L4 遥测;不终止工作负载的 HTTP 流量,不解析 HTTP 头。传输层用 HBONE(HTTP CONNECT 隧道,mTLS 加密,复用 HTTP/2,标准端口 15008)把流量安全地转发给目标工作负载、其他 ztunnel,或 waypoint。
对应到 istiod 的资源生成侧,ztunnel 消费的不是
Cluster/Listener/Route,而是
pilot/pkg/xds/workload.go 里的
WorkloadGenerator(生成
v3.AddressType
资源,即工作负载/服务地址信息)与
WorkloadRBACGenerator(生成
WorkloadAuthorizationType 资源,L4
授权策略):
// pilot/pkg/xds/workload.go(Istio 1.30.3,节选,appendAddress 内 case 分支)
case v3.AddressType:
// 工作负载 / 服务地址信息,ztunnel 用它决定往哪转发、期望的对端身份是什么也就是说,ztunnel 完全没有 sidecar 意义上的”资源树”(LDS/RDS/CDS/EDS 依赖关系,见 Envoy 第 9 篇):它只有地址 + 身份 + L4 授权三类信息,没有 HTTP 路由表、没有 Listener 上的 Filter Chain 概念。这是”per-node L4”在控制面订阅形状上的直接体现——ztunnel 的故障域是该节点上所有工作负载共享:ztunnel 挂了,节点上所有 Pod 同时失去 mTLS 身份与 L4 遥测,这与 sidecar”故障域=单个 Pod”是完全不同的爆炸半径模型,机制原因在于订阅关系从”每工作负载一个客户端”变成了”每节点一个客户端服务该节点全部工作负载”。
三、waypoint:Envoy 内核,但作用域按”挂载关系”划定
官方文档明确 waypoint 就是 Envoy 代理的一次部署——与 sidecar 用的是同一套引擎,只是运行位置和触发条件不同:它作为独立 Deployment 运行在基础设施里,按命名空间或服务账号共享,只有工作负载需要 L7 策略(重试、超时、Header 路由、L7 鉴权等)时才需要部署。
waypoint 消费的资源类型与 sidecar 一样是
CDS/LDS/RDS/EDS,但订阅范围的判定机制不同。pilot/pkg/xds/xdsgen.go
的 waypointNeedsPush 注释写得很清楚:
// pilot/pkg/xds/xdsgen.go(Istio 1.30.3,节选)
// waypointNeedsPush checks if a push is needed for a waypoint proxy on incremental kind.Address changes.
// Waypoint listeners, clusters, and routes are built from the services and workloads attached to the
// waypoint (e.g. the main_internal listener matches attached service VIPs and attached workload IPs),
// and attachment is only surfaced through kind.Address updates (use-waypoint changes, VIP or pod IP
// changes of attached resources).
func waypointNeedsPush(req *model.PushRequest, proxy *model.Proxy) bool {
if !model.HasConfigsOfKind(req.ConfigsUpdated, kind.Address) {
return false
}
...
}sidecar 的作用域由 Sidecar CR /
命名空间可见性决定(SidecarScope,见系列第
9 篇);waypoint 的作用域由哪些
Service/Workload”挂载”到了它决定,而这个挂载关系本身是通过
kind.Address 类型的配置更新体现的——VIP
变化、Pod IP 变化、use-waypoint
标签变化都会触发这条判断。同一个 istiod 内部,两种
Envoy 客户端(sidecar 与
waypoint)用完全不同的规则决定”这次变更是否与我有关”,这是理解
waypoint 排障时”为什么这个 Service
的策略没生效”要先查什么的关键——先查它是否真的挂载到了目标
waypoint,而不是先查 CDS/RDS 内容本身。
waypoint 的故障域对应”挂载到它的所有 Service/Workload 的 L7 处理”:一个 waypoint 通常按命名空间或服务账号共享,故障半径介于”per-pod sidecar”与”per-node ztunnel”之间。
四、推送优化的诚实边界:ambient 路径上一个明写的 TODO
pilot/pkg/xds/proxy_dependencies.go 的
DefaultProxyNeedsPush
是所有代理(除个别特化路径)判断”这次配置变更是否需要推给我”的入口,其中对
ambient 代理的处理是显式跳过优化:
// pilot/pkg/xds/proxy_dependencies.go(Istio 1.30.3,节选)
func DefaultProxyNeedsPush(proxy *model.Proxy, req *model.PushRequest) (*model.PushRequest, bool) {
if req.Forced {
return req, true
}
if proxy.IsWaypointProxy() || proxy.IsZTunnel() || proxy.IsAgentgateway() {
// Optimizations do not apply since scoping uses different mechanism
// TODO: implement ambient aware scoping
return req, true
}
req = filterRelevantUpdates(proxy, req)
return req, len(req.ConfigsUpdated) > 0
}对 sidecar,filterRelevantUpdates 会先按
Sidecar
作用域过滤,只有确实与该代理相关的变更才会触发真正的推送计算。对
waypoint/ztunnel/agentgateway,这一步直接跳过,注释原话是”scoping
uses different mechanism”,并留了一句
TODO: implement ambient aware scoping。
这不是道听途说的”ambient 还不够成熟”,而是源码里当前版本明确写着尚未做的优化:在大规模集群里,任意一次配置变更都会让所有 ambient 代理进入完整的推送判断路径,而不是先用轻量的作用域过滤器排除掉明显无关的连接。sidecar 已经有的这一层优化,ambient 代理目前没有。这是评估”能不能把整个大集群迁到 ambient”时需要纳入考量的具体机制,而不是抽象的”新技术不够稳”。
flowchart TD
change["Config change (PushRequest)"] --> check{"proxy.Type?"}
check -->|"SidecarProxy"| filter["filterRelevantUpdates<br/>(Sidecar scope filter)"]
check -->|"Waypoint / Ztunnel / Agentgateway"| skip["Skip filter — TODO: ambient aware scoping"]
filter --> maybe1["Push only if ConfigsUpdated relevant"]
skip --> maybe2["Enter full push evaluation regardless"]
五、争论与开放问题
| 争论 | A | B |
|---|---|---|
| ztunnel 完全不做 L7 | 攻击面小、per-node 资源可预测,适合作为强制基线 | 需要 L7 策略的工作负载必须额外部署 waypoint,多一跳、多一个组件 |
| waypoint 复用 Envoy 引擎 | 复用现有 Filter/可观测生态,运维经验可迁移 | waypoint 是共享组件,单点故障半径覆盖挂载在它上面的所有 Service |
| ambient 推送暂不做 scoping 优化 | 实现简单,行为可预测 | 大规模集群下 ambient 代理的推送计算开销高于对应规模的 sidecar 部署,是当前版本的已知差距 |
开放问题(呼应 architecture/76
的 Ambient 成熟度判断):DefaultProxyNeedsPush
里的 ambient scoping TODO
何时补上,补上之后的收益需要在真实规模下量化——本篇不编造尚未实现的优化效果。另一个开放问题是多集群
+ ambient 组合下的东西向路径:官方文档提到跨集群 ambient
流量经东西向网关时使用”double HBONE”(外层 mTLS
隧道由网关终止,内层 mTLS
隧道穿透到目标),这个双层隧道相对单集群 HBONE
的开销,目前没有本站可引用的一手实测数据。
六、参考资料
规范 / 官方文档(A)
- Istio, Ambient
overview,
istio.io/latest/docs/ambient/overview/(ztunnel/waypoint 职责划分、HBONE)。 - Istio, Configure waypoint
proxies,
istio.io/latest/docs/ambient/usage/waypoint/(waypoint 部署粒度、istio.io/waypoint-for)。
源码(A)
istio/istiotag 1.30.3:pilot/pkg/model/context.go(NodeTypes:SidecarProxy/Router/Waypoint/Ztunnel)。istio/istiotag 1.30.3:pilot/pkg/xds/xdsgen.go(xdsNeedsPush对 Ztunnel 的特化、waypointNeedsPush)。istio/istiotag 1.30.3:pilot/pkg/xds/proxy_dependencies.go(DefaultProxyNeedsPush、ambient scoping TODO)。istio/istiotag 1.30.3:pilot/pkg/xds/workload.go(WorkloadGenerator、WorkloadRBACGenerator)。
站内对照
- architecture/76 · Service Mesh(税与形态地图,边界引用)
- k8s-network/13 · Cilium 深度拆解(eBPF L4 对照,边界引用)
- 系列第 12 篇 · 多集群东西向发现扇出
实验台账
- 本篇为源码 + 官方文档推导;无本机 ambient 环境实测,不写推送延迟或资源占用数字。
七、小结
- istiod 用一个封闭的
NodeType集合区分四种代理身份;Waypoint/Ztunnel 从推送判断的第一行代码起就走不同路径。 - ztunnel 不进 CDS/LDS/RDS 体系,只消费 Address 与 WorkloadAuthorization 两类资源,故障域是”整个节点”。
- waypoint 是标准
Envoy,但作用域由”挂载关系”(
kind.Address更新)决定,不是 Sidecar CR——排障入口不同。 - ambient 代理目前跳过 sidecar 已有的作用域过滤优化,源码里明写着 TODO,这是评估规模化 ambient 部署时的具体机制依据,不是空泛的成熟度评价。
→ 系列目录 · 上一篇:多集群东西向发现扇出 · 下一篇:Linkerd 对照
同主题继续阅读
把当前热点继续串成多页阅读,而不是停在单篇消费。
【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 控制面】控制面全景:从 CRD 到 xDS 的翻译与推送内核
定位 Istio 控制面内核相对 Envoy 消费侧、Service Mesh 税文与 Gateway API 资源模型的缺口;给出五条坐标系、16 篇地图与本系列明确不写的范围,钉住 istiod 1.30.3 为主线。
Istio / 网格控制面内核:从 CRD 到 xDS
补齐 Envoy 数据面 xDS 消费侧与 Mesh 选型叙事之间的控制面内核:istiod 推送图、CRD/HTTPRoute 翻译、Ambient 多客户端、NACK/warming 责任与排障对齐。
【Istio 控制面】推送图:watch 到 DiscoveryServer.pushXds 的完整链路
画出 istiod 从 K8s watch 事件到某个代理收到 DiscoveryResponse 的完整推送图:ConfigUpdate、debounce、PushQueue、pushXds 各自的边界与代码依据,钉住 VersionInfo/Nonce 的生成规则,并说明为什么控制面的推送成功不等于 Envoy 在服务。