状态:草案(2026-10-05),部分反映了实测数据。 「实测:Istio 边车(Sidecar)的开销」、「服务间 mTLS:配置与实际运行」以及「实测:当 Service 增至一万个时的 kube-proxy 与 Cilium」小节中的数值,是由作者在实验环境中创建的虚拟机上直接测得的,该节中的「补充实测」(2026-10-09)则是在同一主机的 kind 集群中测得的。其余叙述均经官方文档确认,暂无实测。请先阅读文章末尾的「已确认的版本与日期」和「未确认的事项」。编写规则:以实测数据为基础,未测得的内容标注为「未实测」。
目录
- 1. 为什么选择混合架构
- 2. 集群架构选项
- 3. 节点加入与网络路径
- 4. Cilium 的角色
- 5. Istio 的角色
- 实测:Istio 边车的开销
- 服务间 mTLS:配置与实际运行
- 实测:当 Service 增至一万个时的 kube-proxy 与 Cilium
- 6. 配合使用时的注意事项
- 7. LLM 服务中尤为重要的点
- 8. 服务发现、故障域与安全性
- 常见陷阱
- 动手实践
- 面试常见问题
- 常见误解
- 参考资料
- 外部依据
- 已确认的版本与日期
选择混合架构的原因通常涵盖本地 GPU 的单价、数据所在地以及云端的供需与弹性。两者结合使用时,必须亲自设计集群边界、节点加入的路径、服务发现以及故障范围。本文将以官方文档为标准整理选项与限制,并单独探讨在 LLM 服务中尤为成问题的长连接与流式传输。
读完本文后,你将能够在面试中阐述的内容
- 从 etcd 延迟、故障范围和运维负担的角度对比“将集群合并为一个”还是“拆分集群”,并说明 EKS Hybrid Nodes 属于哪一类。
- 解释 Cilium(容器网络接口(CNI)、策略、Gateway API、ClusterMesh)与 Istio(双向 TLS(mTLS)、L7 路由)的角色边界以及同时使用所需的配置。
- 通过实例说明流式 LLM 响应是如何因代理层的空闲超时、HTTP/2 多路复用和浅层健康检查而中断的。
1. 为什么选择混合架构
原因有三。首先是成本:常态负载交给自有设备,峰值负载交给云端的逻辑很常见,但本文并未验证成本数据。其次是 GPU 的供需情况。最后是数据位置。AWS 博客(2025-03-19)指出 Hybrid Nodes 的用途包括靠近数据的检索增强生成(RAG)推理、峰值时利用云端资源以及边缘推理。
2. 集群架构选项
| 选项 | 优势 | 代价 |
|---|---|---|
| 按环境拆分集群,并通过服务网格或多集群服务(MCS)进行连接 | 故障与安全边界得以隔离 | 需在各个环境中重复管理服务发现、策略和部署 |
| 将本地节点加入由云端管理的控制平面(EKS Hybrid Nodes) | 将控制平面的运维交由 AWS 负责 | 连接中断时将与控制平面失联 |
| 将单一控制平面跨 WAN 延伸 | 这是一个单一集群 | etcd 共识对延迟极为敏感 |
etcd 文档建议将心跳间隔(Heartbeat Interval)设为成员间往返时间(RTT)的 0.5 到 1.5 倍,选举超时(Election Timeout)设为 RTT 的 10 倍以上。默认值分别为 100ms 和 1000ms。Kubernetes 文档指出,如果可用性至关重要,应将控制平面组件复制到至少 3 个故障域中。
EKS Hybrid Nodes 文档的核心条件如下:
- AWS 管理控制平面,用户管理本地节点,并按每 vCPU 小时计费。
- 连接必须稳定,不适用于断断续续、中断、间歇或受限环境(DDIL)。
- 不支持在云基础设施(包括其他云)上运行混合节点。
- 节点与 Pod 的 CIDR 需通过
RemoteNodeNetwork和RemotePodNetwork传递,每种最多 15 个,且必须是 RFC 1918 或运营商级 NAT(CGNAT)地址段。 - 建议连接带宽至少为 100Mbps、RTT 在 200ms 以下,但并非严格要求。
- 在混合集群中,云端节点使用 VPC CNI,混合节点使用 Cilium,Webhooks 在云端节点上运行。若要使双向 Pod 直接通信,本地 Pod 的 CIDR 必须可路由。
GCP 仅验证了 GKE Multi-cluster Services(通过ServiceExport共享服务)和多集群 Gateway(由一个专用的配置集群管理 Gateway API 资源)。
3. 节点加入与网络路径
flowchart LR
subgraph ONP["온프레미스"]
G1["GPU 노드 A"] --- R["라우터·방화벽"]
G2["GPU 노드 B"] --- R
end
R -- "Site-to-Site VPN 또는 Direct Connect" --> TGW["Transit Gateway 또는 가상 사설 게이트웨이"]
TGW --> VPC["VPC 서브넷"]
VPC --> CP["EKS 제어 평면"]
VPC --> CN["클라우드 노드"]
AWS Site-to-Site VPN 单个隧道最大为 1.25Gbps,高带宽型为 5Gbps,如果在 Transit Gateway 上采用动态路由,则通过等价多路径(ECMP)进行汇聚。MTU 为 1446 字节,不支持路径 MTU 发现(PMTUD)。由于叠加封装(Overlay Encapsulation)会进一步减小 Pod 的 MTU,因此必须通过测量来确认是否存在分片。Direct Connect 是专用专线,其中 MACsec 在部分专线连接中为可选功能。像模型权重这样的大文件(说明性示例:若以 1Gbps 速度下载 140GB,理论上约需 19 分钟),不应每次都通过 WAN 拉取,最好存放在本地镜像仓库(Registry)和缓存中。
Cilium 在节点间要求的端口包括 VXLAN UDP 8472、WireGuard UDP 51871 以及健康检查 TCP 4240。在混合环境中,这些端口经常被中间防火墙拦截(见下文「常见陷阱」)。
4. Cilium 的角色
- 替代 kube-proxy: 使用 eBPF(扩展 Berkeley 数据包过滤器)处理服务负载均衡,集群内部连接在 socket
connect()阶段分配给后端。若连接时间较长,一旦选定后端便不会再更改。 - 网络策略: 基于身份(Identity)而非 IP 进行判断。
- Gateway API: 支持 Gateway API v1.6.1,前提是使用
kubeProxyReplacement=true和 L7 代理(l7Proxy)。 - ClusterMesh: 提供跨集群 Pod 直接通信(平面 IP)、全局服务以及策略。条件是:所有集群的数据路径模式相同、Cilium 次版本号相差不超过 1、Pod CIDR 不重叠、节点间 IP 连通。默认最多支持 255 个集群,可通过
maxConnectedClusters增加到 511,但只能在安装时设定。由于已连接的集群属于同一个信任域,文档警告称只应连接安全级别相同的集群。策略不会自动同步,且自 1.19 版本起,策略默认仅选择本地集群端点。 - 服务发现: 同时支持服务注解(Annotation)方式的全局服务和标准 MCS-API(
ServiceExport,ServiceImport,clusterset.local)。如果是service.cilium.io/affinity: local,只要本地有健康的后端,就不会将流量发送到远程。 - 基于 SPIFFE/SPIRE 的双向认证(mTLS)在文档中处于 Beta 阶段。
5. Istio 的角色
Istio 负责服务间的 mTLS 身份(基于 SPIFFE 的证书)以及 L7 路由(通过VirtualService的 weight进行流量拆分、重试和超时控制)。
| 项目 | Sidecar 模式 | Ambient 模式(1.24 起 GA) |
|---|---|---|
| 数据平面 | 每个 Pod 一个 Envoy | 每个节点一个 ztunnel(L4),按需配合 waypoint(L7 Envoy) |
| mTLS | Sidecar 之间 | ztunnel 之间的 HBONE 隧道(端口 15008) |
| L7 功能 | 默认包含 | 仅在部署 waypoint 时可用 |
多集群 Ambient 的多网络配置目前处于 Beta 阶段,文档较少的限制包括:不建议用于现有的 Sidecar 生产环境、单网络未经验证、仅支持多个主集群(Primary)、所有集群中的 waypoint 名称必须相同且必须手动同步配置。在 1.31 版本(2026-08-31)中,新增了区域感知负载均衡(zoneAwareLbSetting)与 waypoint 金丝雀发布。
实测:Istio 边车的开销
用数字验证了文档中所说的「Sidecar 会增加延迟」。
环境。 一台 KubeVirt 虚拟机(8 vCPU, 7.9GiB,Guest 内核 6.8.0-139),k3s v1.35.8,Istio 1.31.0(profile=default,原生 Sidecar),压测工具 fortio 1.75.3。在同一个虚拟机内启动客户端 Pod 和服务端 Pod,分别对 plain(无注入)和 mesh(双方均有 Sidecar,开启 mTLS)进行交替测试。参数为:16 个连接,固定 500 QPS 持续 25 秒共 5 次,以及饱和测试 20 秒共 3 次。
| 指标 | 无网格 | Sidecar mTLS | 差异 |
|---|---|---|---|
| 平均延迟(500 QPS,中位数) | 0.241 ms | 2.450 ms | +2.2 ms(约 10 倍) |
| p50 / p90 / p99 | 0.232 / 0.349 / 0.538 ms | 2.474 / 3.068 / 3.726 ms | +2.2 / +2.7 / +3.2 ms |
| 饱和吞吐量 | 217,476 QPS | 16,246 QPS | 无网格时的 7.5% |
5 次测试之间的波动基于平均延迟在 ±3% 以内,没有失败的调用。
这些数字说明了什么。 一次请求穿过两个 Sidecar(客户端侧和服务端侧),在此环境中增加了约 2.2ms。对于首字返回需数百毫秒的 LLM 服务请求而言,这部分开销很小;但对于逐个 Token 流式传输的响应,或者延迟在 1ms 左右的内部微服务调用,则显得较为明显。饱和吞吐量下降到 7.5% 是因为代理共享了相同的 CPU,这意味着对于 Sidecar, 不仅要考虑延迟,还要单独预留 CPU 预算 。
这些数字无法说明的事项。
- 本表的对比对象是「无代理」对「两个代理与 mTLS」。将加密和代理跳转区分开来的结果参见下文的「服务间 mTLS」一节。
- 由于是同一节点内的通信,实际的网卡(NIC)和节点间延迟已被排除。请仅将其作为增加的绝对量来阅读。
- 上表针对的是普通 HTTP/1.1(请求体较小)。gRPC、大请求体以及长流式传输的测试请参见下文「服务间 mTLS」一节末尾的独立测试。
- 未测试 Ambient 模式(ztunnel)。「Sidecar 很贵,所以 Ambient 更好」的说法并未得到此次实测的支持。
测量中获得的教训。 最初的测量由于 fortio 的默认直方图分辨率(1ms)而产生了偏差。如果延迟小于 1ms,百分位数将不会记录为实际值,而是记录为桶中的位置(0.5、0.9、0.99ms)。我是在发现同一次运行的平均延迟与利特尔法则(并发连接数 / 吞吐量)不符时才注意到这一点的。在相信基准测试结果之前, 应当对工具本身产生怀疑并交叉验证两个指标,这个案例将在第 5 篇中再次探讨。
服务间 mTLS:配置与实际行为
这是在 Istio 1.31.0 中实际应用并验证“如何在服务与服务之间建立 mTLS”的结果。
首先需要区分两个层面。 Cilium WireGuard 使用节点级密钥对在节点之间传输的 Pod 流量进行加密(默认情况下仅管理 Cilium 管理的 Pod 之间的流量,节点和主机流量需要开启 Beta 选项,且不加密同一节点内的流量),但它不会验证工作负载的身份。Istio mTLS 通过服务账号签发的证书(SPIFFE 身份)使 双方互相认证,并可以根据该身份决定允许或拒绝访问。如果目的是实现服务间认证,Istio 则是正确的工具。
配置分为三部分。 以下 YAML 均已应用到测试虚拟机中以验证其行为。API 版本为 security.istio.io/v1 。
- 在命名空间中附加
istio-injection=enabled标签,并且必须重启已运行的 Pod 才能注入边车(sidecar)。 - 通过
PeerAuthentication来决定服务端是否接受非 mTLS 连接。由于在没有策略时的默认值为 PERMISSIVE,因此最安全的顺序是先进行观测,然后再提升至 STRICT。 - 由于必须建立 mTLS 才能信任身份,因此在此基础上通过
AuthorizationPolicy来限制可以调用的服务账号。
apiVersion: security.istio.io/v1
kind: PeerAuthentication
metadata: {name: default, namespace: <네임스페이스>}
spec: {mtls: {mode: STRICT}}
---
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata: {name: only-good, namespace: <네임스페이스>}
spec:
selector: {matchLabels: {app: <서버 앱>}}
action: ALLOW
rules:
- from: [{source: {principals: ["cluster.local/ns/<네임스페이스>/sa/good"]}}]
调用方端的 DestinationRule(tls.mode: ISTIO_MUTUAL)根据文档规定,网格内部会自动应用 mTLS,因此并非强制。在实验中为了明确开启和关闭加密而显式指定了它。
实际行为。 客户端是临时的 curl Pod,结果为状态码和 curl 退出码。
| 情况 | 结果 |
|---|---|
| 无策略(PERMISSIVE),网格外的明文客户端 | 200 |
| STRICT,网格外的明文客户端 | 000 exit=56 (连接断开) |
| STRICT,网格内的客户端 | 200 |
| AuthorizationPolicy,允许的服务账号 | 200 |
相同策略,不允许的账号和 default 账号 |
403 |
在运营中有两点非常重要。第一, STRICT 拒绝的方式不是 HTTP 错误,而是连接断开。如果在服务端日志中寻找 403 将会一无所获,而在客户端只会表现为 Connection reset 或 curl: (56) 。当发生故障时,首先应该怀疑的是“虽然是 STRICT 但对方在网格外面吗”。第二, 授权拒绝是 403 。由于 mTLS 连接已经建立,只是身份不在白名单中,因此这两种症状截然不同,可以据此区分原因。
加密与代理跳数(Proxy Hop)的开销分离。 在保持两个边车不变的情况下,仅开启和关闭 mTLS 的对比(同一虚拟机,平均延迟为 500 QPS 下 5 次测试的中位数,饱和吞吐量为 3 次测试的中位数)。
| 配置 | 平均延迟 | 饱和吞吐量 |
|---|---|---|
| 无网格 | 0.241 ms | 217,476 QPS |
| 边车,关闭 mTLS | 2.252 ms | 18,339 QPS |
| 边车,开启 mTLS | 2.437 ms | 16,511 QPS |
在上述增加的大约 2.2ms 中, 两个代理跳数占约 2.0ms(约 92%),加密占约 0.19ms(约 8%) 。与“mTLS 很慢”这一常识相反,在此环境中,大部分开销不是加密,而是代理的 HTTP 处理。不过,由于该虚拟机的 CPU 暴露了 AES 硬件加速(aes, vaes),因此不能断言在没有加速的 CPU、大报文或长流传输中也是相同的比例(未实际测量)。
区分 HTTP 解析与代理跳数的附加测试。 通过名为 http- 的端口(L7 解析)和名为 tcp- 的端口(L4 字节转发)暴露同一台服务器来比较边车(同一虚拟机,500 QPS,20 秒,交叉 5 次,平均延迟中位数)。
| 配置 | 平均延迟 | 相比无网格 |
|---|---|---|
| 无网格 | 0.238 ms | 基准 |
边车,L4(tcp-) |
1.585 ms | +1.35 ms |
边车,L7(http-) |
2.459 ms | +2.22 ms |
在增加的 2.2ms 中,HTTP 解析所占的份额约为 0.87ms(约 39%),而即使设置为不进行解析的 L4,仅仅穿过两个边车也会残留 +1.35ms(约 61%)。因此,“两个代理跳数占约 92%”是所有非加密开销的总和,其中仅 HTTP 解析的份额在 40% 左右。外部研究(《Dissecting Service Mesh Overheads》)报告称协议解析占代理开销的 60~70%,但从该环境下的 L4 与 L7 差异来看,比例要小一些(由于条件不同,不予断定)。
gRPC、大报文、长流传输、明文例外端口。
| 测试 | 无网格 | 边车(mTLS) |
|---|---|---|
| gRPC 单次调用平均延迟(500 QPS,3 次) | 0.397 ms(0.394~0.408) | 3.834 ms(3.054~3.961),约 +3.4ms |
| 下载一次 100MiB 响应(3 次) | 5,363~8,107 MB/s,0.01~0.02 秒 | 1,517~1,895 MB/s,0.06~0.07 秒 |
| 200 个 SSE 事件(服务器每 20ms 发送一次,3 次) | 间隔中位数 20.1ms,p99 20.3ms | 间隔中位数 20.1ms,p99 20.4ms(最大 22.0ms) |
| STRICT 下网格外的明文客户端 | 000 exit=56(连接断开) |
|
在 STRICT 中通过 portLevelMtls 仅将 8080 设为 PERMISSIVE |
200 |
- gRPC 增加的值比同一环境下的 HTTP/1.1(+2.2ms)更大(+3.4ms)。推测是因为每次调用是一个 HTTP/2 流,解析开销更大,但并未进行分解。
- 大报文的吞吐量低了约 3.5~4.5 倍。 由于在同一节点内,明文方式接近内存复制的速度。如果让 LLM 权重或大响应通过边车,将会付出这一开销,因此必须确定排除对象。
- 令牌(token)流传输间隔没有受到影响。 20ms 间隔的事件穿过边车后,中位数保持不变,p99 增加了 0.1ms。也就是说,可以认为边车的延迟并不是在每个事件上累加,而是作为固定值叠加在请求路径上的(基于此间隔和事件大小)。
- 例外端口可以通过
portLevelMtls打开。 在保持 STRICT 的同时,仅将一个端口设为 PERMISSIVE,网格外的明文客户端便会收到200。 - 局限性:所有通信均在同一虚拟机内部。如果实际网卡和 CPU 不同,大报文的比例也会有所不同。
当应用到实际服务集群时的注意事项是尚未经过测试的推论(未实际测量)。 当网格外部传入的明文流量(例如 Cilium Gateway)触及 STRICT 服务时,会遇到上述连接断开的情况,因此采用将对应端口通过 portLevelMtls 设为 PERMISSIVE 的方法(已在上述表格中验证了其行为)。数据库、DaemonSet 和 Job 应在最后进行转换,对于 Job,需要单独检查边车无法终止的问题。
实测:当 Service 增至一万个时的 kube-proxy 与 Cilium
我们用数字验证了“当 Cilium 替代 kube-proxy 后,即使服务增多也能保持极快速度”这一说法。
环境。 两台 KubeVirt 虚拟机(各 8 vCPU,7.9GiB,Guest 内核 6.8.0-139,k3s v1.36.5)。一台使用 k3s 内置 kube-proxy(iptables、nf_tables 后端)和 flannel,另一台使用 Cilium 1.20.1(KubeProxyReplacement=True,VXLAN)。 这两台虚拟机运行在不同的物理宿主机上。 测试时分别将服务数量增加到 0、100、1,000、5,000 和 10,000 个。
| 测量 | kube-proxy(iptables) | Cilium(eBPF) |
|---|---|---|
| 10,000 个 Service 时的 数据平面项 | NAT 规则 110,296 行(每个服务约 11 行) | BPF LB 项 40,104 个(每个服务约 4 个) |
| 新 Service 连接至 ClusterIP 所需时间,0 个服务 | 中位数约 0.96 秒 | 未重新测量(同宿主机 kind 中中位数为 0.24 秒,下方有补充) |
| 相同延迟,10,000 个服务 | 中位数约 2.3 秒 | 中位数 0.31 秒 |
| 新连接平均延迟增量(从 0 到 10,000 个) | 约 +0.05ms(约 15%) | 几乎没有 |
解读方法。
- 双方的规则数量均随服务数量呈线性增长。 不同的地方在于新服务反映到数据平面的时间。kube-proxy 即使在没有服务时也需要接近 1 秒(这与文档中“最小同步间隔为 1 秒”的说法一致,但未能在此虚拟机中确认该配置),而在有一万个服务时增加到了约 2.3 秒。Cilium 则无论服务数量如何,均保持在 0.3 秒左右。
- 在大规模创建后不久,差距会进一步拉大。 在创建一万个服务后不久,kube-proxy 方面直到第一个新服务连接耗费了 45 秒(这种情况在两次测试中出现了一次),而 Cilium 方面为 0.25 秒。这是在同时部署大量服务时的一个重要差异。
- 新连接延迟的变化比想象中要小。 即使规则达到 11 万行,平均延迟也仅增加了约 0.05ms。“如果服务很多,iptables 会导致数据路径变慢”这一常识在此环境(同节点内,8 个连接)中并没有得到显著证实。实际影响更大的是反映延迟。
这些数字无法说明的问题。
- 由于宿主机不同,两台虚拟机的绝对延迟无法进行比较,只有各虚拟机内部的增长幅度才有意义。
- 在此虚拟机实验中,并未测量节点间通信以及每个服务的后端数量增加的情况。同宿主机 kind 节点间测量的值见下文补充,而实际物理节点间仍未实际测量。连接数较多时的 conntrack 见下文的单独测试。
- 被测量的服务位于
KUBE-SERVICES链中的哪个位置并未进行确认。外部测量(华为 2017 年发布,见下文外部依据)报告称,首个数据包的延迟可能会根据其在链中的位置而产生巨大变化,因此新连接延迟变化很小的结果可能会根据服务位置的不同而有所变化。 - 此虚拟机实验中的新连接延迟每个级别仅测量了一次(补充实测中每个条件有 6,000 个样本)。
- 虚拟机实验仅对比了 iptables 模式。
ipvs·nftables模式已在下文补充中通过同宿主机的 kind 进行了测量,而一万个服务的条件并未通过这两种模式重新测量(未实际测量)。
当 conntrack 表被填满时。 在一次性虚拟机中 nf_conntrack_max 降低,并通过回环同时并发打开了 4,000 个连接(连接超时 5 秒,测试前等待现有条目降到 500 个以下)。
| 上限 | 开始前条目数 | 成功 | 失败(5 秒内未建立) | 备注 |
|---|---|---|---|---|
| 262,144(默认) | 223 | 4,000 | 0 | p99 14.5ms |
| 2,048 | 140 | 1,913 | 2,087 | 最大 3,068ms(因 SYN 重传而延迟成功的连接) |
| 1,024 | 59 | 969 | 3,031 |
- 成功连接数几乎等于“上限减去现有条目数”(2,048-140=1,908 对 1,913,1,024-59=965 对 969)。对于超过上限的新连接,在没有拒绝响应的情况下 数据包被默默丢弃,客户端重传后一直等待到超时。
- 内核日志中留下了
nf_conntrack: table full, dropping packet但只有 11~12 行。由于日志会被速率限制,因此不能将该行数视为丢弃次数。 - 在默认上限(此 VM 为 262,144)下,相同的负载顺利通过。也就是说,风险来自于与节点内存成正比的上限和并发连接数的乘积,一旦超过上限,就会以仅部分连接随机失败的形式出现。这就是监控上限(
nf_conntrack_count对nf_conntrack_max)的原因。 - 局限性:是在同一节点的环回连接且连接全部处于打开状态的瞬间进行的测试。未测量实际服务中的短连接频率以及超时分布下的失败率(未实测)。
补充实测:同一宿主机 kind 中的四种数据平面。 上述 VM 实验留下的空白由 Mac (Apple M1 Max) 的 Docker Desktop 内 kind 集群 (Kubernetes v1.36.1,1 个控制平面 + 1 个工作节点) 填补。分别在各自的新集群中用相同的脚本测量了 kube-proxy 的 iptables、ipvs、nftables 模式以及 Cilium 1.20.1 (替代 kube-proxy,VXLAN)。这里的“节点之间”是指 同一宿主机内的 kind 节点容器之间,因此不包含物理 NIC 和线路延迟。新连接延迟是指在新 TCP 连接 → HTTP 请求 → 响应一次的条件下,每种情况测量 6,000 次的中位数。由于客户端是 Python,应重点看相同条件之间的差值,而不是绝对值。
| 测量 | iptables | ipvs | nftables | Cilium |
|---|---|---|---|---|
| 新连接,同节点后端(中位数 / p99) | 0.190 / 0.440ms | 0.202 / 0.477ms | 0.186 / 0.800ms | 0.165 / 0.312ms |
| 新连接,不同节点后端 | 0.213 / 0.449ms | 0.237 / 0.693ms | 0.210 / 0.585ms | 0.227 / 0.426ms |
| 后端从 10 个增加到 1,000 个时的新连接中位数 | 0.281 → 0.307ms | 0.269 → 0.292ms | 0.290 → 0.350ms | 0.317 → 0.300ms |
| 后端为 1,000 个时的数据平面条目 | NAT 规则 4,073 行 | 未统计 | 未统计 | BPF LB 条目 1,016 行 |
| 从 0 个服务到建立新 Service 连接(10 次中位数) | 1.04秒 | 0.12秒 | 1.03秒 | 0.24秒 |
| 反映端点变更(1 个后端,休息 2 秒后更改,5 次) | 7–9ms | 31–36ms | 6–8ms | 首次 82ms,其余 474–477ms |
| 紧接着还原变更的反映(5 次) | 1.02–1.03秒 | 60–83ms | 1.01–1.02秒 | 0.50–0.51秒 |
- 四种情况下的数据路径开销都很小。 在同一节点上,经由 ClusterIP 与直接连接 Pod IP 的中位数差值在 kube-proxy 的三种模式下在 ±0.02ms 以内,Cilium 中为 +0.005ms。转到其他节点时,kube-proxy 侧增加了 0.02–0.04ms,Cilium 增加了 0.06ms。Cilium 侧由于包含 VXLAN 封装,因此是混入了 CNI 差异的值。
- 即使将后端数量从 10 个增加到 1,000 个,新连接中位数的增幅也在 0.06ms 以下(iptables +0.026ms,ipvs +0.023ms,nftables +0.060ms,Cilium 无变化)。iptables 虽然在服务链中依次遍历概率规则,但在 1,000 个规模下,就像 VM 实验中 1 万个服务时一样,数据路径的差异很小。后端是指由 5 个 Pod × 200 个端口构建的 1,000 个 IP:端口。
- 差异体现在反映时间上。 iptables·nftables 模式在休息 2 秒后对所做更改在几毫秒内即可反映,但反映后紧接着的更改则等待了约 1.02 秒。如果创建新 Service,由于 Service 和 EndpointSlice 会依次更改,因此即使服务数为 0 也需要花费 1.03–1.04 秒,这与 VM 实验中的 0.96 秒是相同的现象。在 kind 默认配置中最小同步间隔为 0 秒的 ipvs 模式为 0.12 秒。Cilium 为 0.24 秒,端点变更除首次外,与休息时间无关,保持在大约 0.47 秒(还原为 0.50 秒)恒定。虽然看起来是代理以固定间隔将更改打包反映,但未能确认其配置值。
- 关于条目数,iptables 每个端点约有 4 条 NAT 规则,Cilium 每条 BPF LB 规则 1 行,两者均呈线性。 ipvs·nftables 的条目数由于统计命令错误,此次未能统计。
- 局限性:是同一宿主机虚拟网桥上的 2 个节点,且与其他运行不同任务的集群共享了 Docker。排除了 VM 负载较大区间的执行并重新进行了测量。未测量实际物理节点间的值以及同时增加服务数和端点数的条件(未实测)。
原始数据与脚本: docs/experiments/2026-10-09-dataplane-scale/.
测量中获得的教训。 初次测量中,连接尝试限制(0.2秒)将 Cilium 侧的值固定为了 206ms 和 411ms 两种。对于反复出现相同值的表格,应当怀疑工具的分辨率。已将尝试限制缩短至 20ms 后重新测量。
6. 共同使用时的注意事项
根据 Cilium 文档,如果由 Cilium 替代 kube-proxy 的同时还要使用 Istio,则需要 socketLB.hostNamespaceOnly=true和 cni.exclusive=false。官方建议保留 kube-proxy 的配置是更改最少的方式。L7 HTTP 策略要求在 Cilium 和 Istio 之间仅使用其中一方。由于 Ambient mTLS 区间被隧道化到 15008,Cilium 策略无法查看源和 L7 信息。在这种情况下,网格内部的 L4 及以上策略由 Istio 负责。
7. 在 LLM 服务中尤为重要的点
클라이언트 -> LB/Gateway -> (Envoy 사이드카 or waypoint) -> 모델 서버(vLLM 등)
각 홉마다: 요청 타임아웃 / 스트림 유휴 타임아웃 / 연결 유휴 타임아웃 / 드레인
- 长流式传输: Envoy 的流空闲超时默认值为 5 分钟,如果在收到响应头之前过期则为 408,之后则为流重置。如果在长提示词的队列阶段持续没有响应,可能会触发此问题。
VirtualService的请求timeout默认处于禁用状态。DestinationRule的连接池idleTimeout基于无活动请求的时间,默认值为 1 小时,并且 HTTP/2 PING 无法维持连接。 - 重试: Istio 默认重试次数为 2 次,并且仅适用于
connect-failure,refused-stream,unavailable,cancelled。必须确保开销巨大的生成请求不会被重复发送。 - 负载均衡: 默认算法是最少请求(
LEAST_REQUEST)。Istio Ambient 文档指出,故障转移到远程网络时,由于 HTTP 多路复用和连接池化,请求可能会集中到一个远程端点。对于生成长度差异巨大的 LLM 来说,仅计算请求数的负载均衡的局限性更加明显。Gateway API Inference Extension 旨在通过考虑 KV 缓存和请求开销的端点选择器(Endpoint Picker)来解决此问题,其 README 指出已实现全面可用(GA),选择器实现已迁移至 llm-d 仓库。 - 终止与发布: 边车终止耗尽(drain)默认值为 5 秒。对于长达数分钟的流式传输,必须将
terminationDrainDuration与 Pod 终止宽限期匹配响应长度。 - 健康检查: 服务器进程存活的信号与能够处理请求的信号是不同的。正如后文“常见陷阱”中的语音合成(TTS)案例所示,即使失败率很高,健康检查也可能返回 200。
- 延迟分布: 由于输入处理(prefill)、Token 生成(decode)和队列相互交织导致长尾延迟变长,因此应根据 p95 和 p99 而不是平均值来设定超时。
8. 服务发现、故障域、安全
可以将故障域划分为集群、网络路径(VPN)和控制平面。在混合集群中,通过 Service trafficDistribution(PreferSameZone将流量保留在同一可用区中,并在双方部署 CoreDNS 副本(EKS 文档建议)。安全方面不要依赖边界防火墙,而应具备服务间 mTLS、公共根下的各集群中间 CA,以及 cert-manager 或 SPIRE 等自动证书轮换机制。本地节点使用 SSM 混合激活或 IAM Roles Anywhere 颁发的临时凭证向 EKS 进行身份验证,而不是长期密钥(EKS 文档)。ClusterMesh 和 Istio 多集群在连接的瞬间会合并信任域,因此如果一方遭到入侵,可能会蔓延至另一方。
常见陷阱
作者的实验环境位于同一个私有局域网内,因此没有 WAN、云、ClusterMesh 和 Ambient。以下是将该环境中遇到的事情总结为一般性案例的内容。
- 隧道端口被阻止: 在 Windows 上的 WSL2 节点中,Hyper-V 防火墙阻止了 UDP 8472,导致该节点的 Pod 无法与其他节点通信(通过 tcpdump 确认)。WireGuard UDP 51871 也由于相同原因无法建立。
- kubelet 无法触达自身节点的 Pod: 在相同的 WSL2 节点上,HTTP 和 TCP 探针全部超时,因此更改为了
exec探针。 - 网关超时: 针对不同的路径拆分了请求超时。常规路径为 120 秒,Server-Sent Events (SSE) 流式响应为
0s(无限制),耗时较长的会话生成为 900 秒。如果由网关中断,服务器日志中将不会留下错误,导致排查原因变得困难。 - Istio 最小安装: 由于 Cilium 是
cni-exclusive=true,因此在没有istio-cni的情况下使用了istio-init,并将该命名空间本身的 Pod Security Admission (PSA) 级别降低到了 privileged。上述 Cilium 文档建议cni.exclusive=false。 - 浅层健康检查: 当把多个 TTS Pod 放在一个服务后面时,与其它工作负载共享 GPU 的 Pod 的请求中约有 18% 因内存不足(OOM)而失败,但 Pod 状态依然是 Running,健康检查也返回 200。由于一个生成物需要几十次调用,导致 74 分钟内没有完成任何成品。
亲自动手尝试
以下内容全部为只读或在临时命名空间中结束。编写本文时并未执行并验证相关命令。
# 읽기 전용: Cilium 설정 확인
kubectl -n kube-system get cm cilium-config -o yaml | grep -E "routing-mode|tunnel-protocol|enable-wireguard|kube-proxy-replacement|cni-exclusive|bpf-lb-sock-hostns-only"
kubectl get gateway,httproute -A
# 임시: 노드 사이 지연과 스트리밍 간격 (정리 포함)
kubectl create ns lab-net
kubectl -n lab-net run a --image=nicolaka/netshoot --overrides='{"spec":{"nodeName":"NODE_A"}}' -- sleep 3600
kubectl -n lab-net run b --image=nicolaka/netshoot --overrides='{"spec":{"nodeName":"NODE_B"}}' -- sleep 3600
kubectl -n lab-net exec a -- ping -c 10 $(kubectl -n lab-net get pod b -o jsonpath='{.status.podIP}')
kubectl delete ns lab-net
对于 ping 的结果,请观察平均值以及最大值和标准差。用同样的方式在临时 Pod 中启动一个 SSE 服务器,以 10 秒为间隔发送数据块, curl -N并通过它接收,即可查看服务路径是否会中断流。由于更换网关 timeouts.request的实验会触及实际的服务路由,请仅在单独的测试网关上进行。
面试中可能会问到的问题
- 集群应该合为一个,还是拆分? 为了隔离故障和安全边界,应该拆分;如果统一管理更为重要,则可以像 EKS Hybrid Nodes 那样将节点加入到一个控制平面中。如果将控制平面延伸至 WAN,则必须根据 RTT 调整 etcd 超时,并设计连接断开时的影响范围。
- EKS Hybrid Nodes 需要怎样的网络? 需要通过 VPN 或 Direct Connect 与 VPC 建立私有连接。节点和 Pod 的 CIDR 不能互相重叠,也不能与 VPC 及服务的 CIDR 重叠;对于 Webhook 与节点之间的直接通信,Pod 的 CIDR 必须是可路由的。
- 同时使用 Cilium 和 Istio 会怎样? Cilium 负责 CNI、L3/L4 策略和入口点,而 Istio 负责 mTLS 身份和 L7 路由。在 kube-proxy 替代模式下,必须将 Socket LB 限制在主机命名空间中,并解除 CNI 独占。L7 策略仅在其中一方进行管理。
- 如果 LLM 流式传输中途中断怎么办? 检查每个跳数(hop)的请求超时、流空闲超时以及连接空闲超时,并区分记录日志的层级。隔离流式传输路径的超时设置,并使重试、排干(drain)和健康检查与响应长度相匹配。
- 多集群的故障和安全如何处理? 平时通过区域亲和性(affinity)将流量保留在本地,只有当本地后端全部不健康时才转发到远程。由于信任域合并了,因此只能连接安全级别相同的集群。策略和配置必须分发到各个集群,以通过 GitOps 保持一致性。
常见的误解
- ClusterMesh 并不是将多个集群合并为一个的功能。各个集群的 API Server 和策略是独立的。
- Hybrid Nodes 不是将云节点接入的功能,而是将本地(On-premises)节点接入 EKS 的功能。
- 并不是说叫 Ambient 模式,L7 功能就会自动开启。它还需要 waypoint。
- 专线并不等同于加密。加密需要通过 MACsec 或 mTLS 另外配置。
参考资料
- https://docs.aws.amazon.com/eks/latest/userguide/hybrid-nodes-overview.html
- https://docs.aws.amazon.com/eks/latest/userguide/hybrid-nodes-prereqs.html
- https://docs.aws.amazon.com/eks/latest/userguide/hybrid-nodes-cni.html
- https://docs.aws.amazon.com/eks/latest/userguide/hybrid-nodes-webhooks.html
- https://aws.amazon.com/blogs/containers/run-genai-inference-across-environments-with-amazon-eks-hybrid-nodes/
- https://docs.aws.amazon.com/vpn/latest/s2svpn/vpn-limits.html
- https://docs.aws.amazon.com/directconnect/latest/UserGuide/Welcome.html
- https://docs.cilium.io/en/stable/network/clustermesh/intro/
- https://docs.cilium.io/en/stable/network/clustermesh/setup/
- https://docs.cilium.io/en/stable/network/servicemesh/istio/
- https://docs.cilium.io/en/stable/network/servicemesh/gateway-api/gateway-api/
- https://docs.cilium.io/en/stable/operations/system_requirements/
- https://istio.io/latest/docs/ambient/overview/
- https://github.com/istio/istio.io/blob/master/content/en/docs/ambient/install/multicluster/_index.md
- https://istio.io/latest/news/releases/1.31.x/announcing-1.31/
- https://istio.io/latest/docs/reference/config/networking/destination-rule/
- https://istio.io/latest/docs/reference/config/istio.mesh.v1alpha1/
- https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto
- https://etcd.io/docs/v3.6/tuning/
- https://cloud.google.com/kubernetes-engine/docs/concepts/multi-cluster-services
- https://github.com/kubernetes-sigs/gateway-api-inference-extension
外部依据
对于未能直接测量或仅靠测量尚显不足的主张,我们通过查阅以下资料进行了确认。
| 主张 | 资料 | 确认的内容 |
|---|---|---|
| Sidecar 开销的大部分在于 HTTP 解析 | Zhu 等人, Dissecting Service Mesh Overheads (arXiv 2207.00592, 2022, 预印本) | 在 Istio 1.13 中,HTTP 模式的开销约为 TCP 模式延迟的 4 倍、CPU 的 3 倍,其中大部分(62%~73%)为协议解析。TLS 加密开销未单独测量 |
| Proxy 比 mTLS 加密开销更大 | Bremler-Barr 等人, Performance Comparison of Service Mesh Frameworks: the MTLS Test Case (arXiv 2411.02267, 2024, 技术报告) | 以无 Mesh 仅开启 mTLS 为基准,p99 最大增加 3%。Istio Sidecar 即使关闭 mTLS,其吞吐量也相近,而连 HTTP 解析也关闭的 TCP 模式则约为 5 倍。Ambient 的 p99 增长要小得多。由于测试中对服务器加入了 200ms 的延迟,绝对值中混入了饱和的影响 |
| 每个请求的附加延迟大小 | Istio 官方性能文档 | 1.16.2 文档显示两个 Proxy 使 p90 增加约 1.7ms、p99 增加约 2.7ms(1,000 QPS,16 个连接,mTLS)。1.20 文档显示 p90 约 0.228ms。本文的 +2.2ms 与前一个值相近,比后一个值大约 10 倍。未能确认文档硬件是否发生更改,本文的一台 VM 同时运行了客户端、服务器和 Proxy,CPU 竞争可能是原因之一,但未经验证 |
| Ambient 更轻量的说法 | 上述第二篇论文,Istio 1.24 文档 | 在 3,200 QPS 下强制启用 mTLS 时,p99 增加量:Ambient 为 +0.03 秒,而 Sidecar 为 +0.37 秒。ztunnel 约为 0.06 vCPU·12MB,而 Sidecar 约为 0.20 vCPU·60MB(1,000 QPS)。本文未对 Ambient 进行测试 |
| kube-proxy 同步间隔 | Kubernetes 文档,kube-proxy minSyncPeriod 默认 1 秒 |
这与本文 0 个服务时的约 0.96 秒相吻合 |
| iptables 首个数据包延迟与规模的关系 | 华为 2017 年发布资料(随服务数量而变化的规则遍历开销),Kubernetes 1.28 的部分更新 | 由于规则是线性遍历的,链越靠后的服务,首个数据包越慢。ipvs 模式自 1.35 起被弃用(deprecated),nftables 是替代方案 |
| WireGuard 加密范围与开销 | Cilium 文档(WireGuard Transparent Encryption),WireGuard 论文(NDSS 2017) | 默认情况下仅限 Cilium 管理的 Pod 流量,节点流量为 Beta 选项,同一节点内部不加密。在隧道路由模式下为双重封装。论文报告在 1GbE 区间内达到 1,011Mbit/s(作者对比,链路达到极限) |
| 混合架构限制 | AWS EKS Hybrid Nodes 文档,Site-to-Site VPN 限制文档 | 每个隧道限制 1.25Gbps,Cilium 是混合节点支持的 CNI,混合节点按每小时 vCPU 收取费用 |
| conntrack 饱和时的行为 | 本文的测试(见上文) | 未查阅外部文献,以直接测量代替 |
确认的版本与日期
截至 2026-10-05:Kubernetes 1.37.1(2026-09-23)、Cilium 1.20.2(2026-09-16,存在 1.21.0-pre.3)、Istio 1.31.1(2026-09-21,1.31 支持 Kubernetes 1.32~1.36)、Gateway API v1.6.1(基于 Cilium 文档)。实际测量使用的 Cilium 为 1.20.1,Istio 为 1.31.0。
未能确认的事项:EKS Hybrid Nodes 价格表的具体数值、最新支持的 Cilium 版本(AWS 文档仅提及 1.17 和 1.18)、将本地节点接入 GKE 的产品、Direct Connect 的默认加密策略、ClusterMesh 在远程集群断开时的准确行为、Ambient 受 WAN 延迟的影响、上述命令的实际输出。