把代理从 Pod 里拿出来,变了什么
一句话总结
Ambient 模式将 L4 与 L7 分离,形成了不再强制所有工作负载承担完整 Envoy 成本的架构。但与此同时,故障域也从 Pod 扩大到了节点。
为什么需要它
Sidecar 模型的成本用数字看十分直观。在拥有 500 个 Pod 的集群中,如果每个 sidecar 占用 120MiB 内存,单是这些代理就需要约 58GiB;如果每个代理的 CPU request 为 100m,还会有 50 vCPU 从可调度容量中消失。此外,由于 proxy 位于 Pod 内,升级版本就必须滚动重启所有工作负载。注入 label、revision tag 或 webhook 配置只要有一项不一致,就会出现“部分 Pod 在 mesh 内、部分 Pod 在 mesh 外”的状态。此时若启用 STRICT,所有未注入的 Pod 都会断开。
除此之外,还有一项决定性观察:真正需要 L7 功能的服务只占全部服务的一部分。其余大多数服务只需要 mTLS 和基础 telemetry,sidecar 模型却仍要求它们承担完整 Envoy 的成本。
工作原理
Ambient 分为两层。
- ztunnel — 每个节点运行一个的 DaemonSet。它是用 Rust 编写的轻量级 proxy,而不是 Envoy。其职责被刻意限制在 L4:提供 mTLS、基于 SPIFFE 身份的 L4 授权,以及 TCP telemetry。由于它不解析 HTTP,内存用量与连接数成正比,而非与 Pod 数量成正比。
- waypoint — 仅按需要 L7 的 namespace 或 ServiceAccount 部署的 Envoy proxy。它通过 Gateway API 的 Gateway 资源声明,并作为普通 Deployment 运行,因此可以通过 HPA 独立扩缩容。
节点间流量通过 HBONE tunnel 传输。它在端口 15008 上,以基于 mTLS 的 HTTP/2 CONNECT 封装原始 TCP stream。借助 HTTP/2 stream multiplexing,同一对节点间的多个连接可以共享一条 mTLS 连接,从而降低握手成本。CONNECT header 中携带原始目的地与身份,因此接收端 ztunnel 无需打开 payload 就能完成 L4 授权判定。
有一项设计原则必须牢记:waypoint 归目的地所有。 在 sidecar 模型中,客户端 proxy 执行路由与重试;在 Ambient 中,则由拥有目标服务的团队通过自己的 waypoint 执行 L7 策略。这样策略所有权更加清晰,但“按调用方设置不同 timeout”等客户端策略需要重新设计。
也必须诚实看待代价。经过 waypoint 的路径包含 3 个 hop(ztunnel → waypoint → ztunnel),可能比 sidecar 的 2 个 hop 更长。而且,一旦 ztunnel 故障,该节点的全部 mesh 流量都会受到影响。 sidecar 的 proxy 故障被限制在单个 Pod,Ambient 的故障域却是整个节点。PodDisruptionBudget、priority class 和重启监控都必须纳入运维设计。
调试时,顺序决定一切。应按照流量经过的顺序读取 istioctl proxy-config。
listener 이 프록시가 그 포트를 듣고 있는가
->
route VirtualService 가 라우팅 테이블로 번역됐는가
->
cluster DestinationRule(서킷브레이커, TLS)이 반영됐는가
->
endpoint 그 subset 에 실제 파드 IP 가 잡혀 있는가
只要找到从哪个阶段开始与预期不符,需要修正的资源也就自然确定了。503 response flag 对照表也是同样的思路。
| 标志 | 含义 | 首要怀疑对象 |
|---|---|---|
| UH | no healthy upstream | subset label 与 Pod label 不一致 |
| UO | upstream overflow | 超过 connectionPool 上限 |
| UF | upstream connection failure | 仅一侧启用 STRICT 导致 mTLS 不一致,或错误判断端口协议 |
| NR | no route | VirtualService 匹配缺失,或没有 catch-all |
| URX | max retries reached | 重试耗尽——应结合其他标志追踪根因 |
关于可观测性,还要注意最后一点。Envoy 会自动创建 span,但如果应用没有把入站请求的 trace header 传播到出站请求,trace 就会中断。 部署 mesh 后 trace 仍支离破碎,多数是这个原因;这也是 mesh 唯一无法替应用完成的部分。
现场会遇到的情况
作者的家庭实验室中,hubble-relay 与 hubble-ui 曾一直处于 Pending。事件信息为 0/1 nodes are available: 1 node(s) had untolerated taint(s)。原因很简单:控制平面节点带有 node-role.kubernetes.io/control-plane:NoSchedule taint,而这两个组件不是 DaemonSet,而是 Deployment,因此没有 toleration。 相比之下,CoreDNS 带有默认 toleration,所以能够正常启动;worker 加入后,问题立即消失。
这个案例也可以直接用于理解 Ambient:ztunnel 是 DaemonSet,waypoint 是 Deployment。 也就是说,ztunnel 会自动在每个节点启动,而 waypoint 必须由 scheduler 寻找可用位置。若只剩带 taint 的节点或资源不足,waypoint 会静默停留在 Pending,该 namespace 的 L7 策略将完全无法执行。因此,当出现“L4 正常,只有 L7 策略不生效”的症状时,首先要检查 waypoint Pod 状态。
还有一点:该家庭实验室使用 --skip-phases=addon/kube-proxy,从一开始就不安装 kube-proxy,而不是安装后再删除。因为只要 kube-proxy 运行过一次,KUBE-SERVICES / KUBE-SVC-* / KUBE-SEP-* chain 就会残留在节点上。从 sidecar 转换到 Ambient 时也适用同样原则:一个 namespace 中不能同时存在注入 label 与 ambient label。删除注入 label 后,必须通过滚动重启清除现有 sidecar,然后再添加 ambient label。
下一项测验要确认什么
本模块是概念模块,不安排实验,而是通过测验检查 Ambient 架构、HBONE、故障域、读取 proxy-config 的顺序以及 response flag 判读。这五项不仅是考试重点,也是实际故障处理时最先使用的工具。