Kubernetes 中的 Pod VPN 与外发网关:隧道归谁所有?
Pod 拥有自己的网络命名空间,因此真正的选择是哪一层拥有隧道:按工作负载的独立身份,还是由 CNI 引导、共用单一源地址的外发流量。
你部署了一个名为 vpn-gateway 的 Pod,把测试客户端指向它的地址,却看到数据包消失。清单里没有任何错误,kubelet 也无从抱怨。原因在于结构而非配置:Kubernetes 为每个 Pod 提供自己的网络命名空间,因此名称、标签或共享的 API 命名空间都不会改变数据包的去向。所以,决定 VPN 流量如何离开集群,要从所有权问题开始,而不是从容器镜像开始。
两种机制覆盖了大多数真实部署,它们不能互换。一种把隧道放进工作负载自己的网络命名空间。另一种请 CNI 把选定的流量引导经过指定的网关节点。它们的失败方式不同、扩缩方式不同、归属的团队也不同,因此这个容器问题往往引出永远无法收场的争论。
Pod 的边界是命名空间,不是跳板机
Linux 网络命名空间拥有自己的接口、地址、路由和防火墙规则,每个 Pod 都会得到一个。该 Pod 内的容器加入其中,正因如此,辅助容器才能改写应用随后遵循的路由。相邻的 Pod 有着看起来相同却完全独立的协议栈。不存在可供网关 Pod 继承的共享路由表,也不存在从一个命名空间到另一个命名空间的隐式流量交接。
这也是为什么网关 Pod 模式只有在有上游真的把数据包送给它时才成立:一个 Service、节点上的静态路由,或 CNI 中的引导策略。仅有地址本身吸引不了任何流量。
按 Pod 建隧道能买到什么,又要付出什么
在工作负载命名空间中运行的隧道,让该工作负载拥有自己的客户端身份,许多远程访问服务在授权或计费会话前都要求这一点。影响半径也保持局部:隧道损坏只让一个 Deployment 下线,而不是所有东西共用的路径。它如何运行取决于实现,而这正是人们容易猜错的地方。
只需要内核状态的配置可以在 init 容器中完成并退出。必须随 Pod 整个生命周期存活的用户态守护进程,则需要 Kubernetes 1.29 中作为原生 sidecar 引入的可重启 init 容器形式,这个区别来自隧道的实现方式,而不是个人偏好。这种模型的代价是重复:每个副本都要认证、获得自己的隧道地址并占用一个远端会话名额,于是扩缩容事件变成链路另一端的容量事件。把同一份凭据复制到所有副本并不能消除这项代价,只会消除审计线索。权限同样值得审视,因为一个想在自己命名空间里获得 NET_ADMIN 的隧道,没有理由继承 privileged: true 示例中横跨整个主机的权限。
当 CNI 拥有外发流量时
如果 CNI 支持,外发网关会按工作负载和目标网段选择流量,然后通过选定节点在单一可预测的源地址下发送。Cilium 的实现是常见参照,其文档明确指出该功能需要特定的 Cilium 配置,以及配备正确接口和地址的网关节点。它还提到 Pod 创建后有一段初始时期引导尚未生效,这是设计输入而不是脚注。
把这个延迟放到你自己的承诺面前看。如果规则是不允许任何流量走别的路径,那么在这段空窗期内发送流量的新 Pod 已经违反了它,因此启动流程需要自己的答案:在策略生效前挂起应用,或者接受这个窗口并记录下来。
把主张收紧。外发网关只选择和转换路径。它不终结 VPN,也不会在没有受支持连接的情况下把集群变成你远程网络的客户端。
| Question | Per-Pod tunnel | CNI egress gateway |
|---|---|---|
| Who holds the client identity | Each workload | The gateway, shared |
| What scaling changes | Enrollment and sessions per replica | Throughput and capacity per node |
| What the remote side sees | Distinct addresses | One translated source |
| Failure scope | One Deployment | Every workload behind the gateway |
地址转换悄悄改变了远端看到的东西
一旦网关重写源地址,按 Pod 划分的地址就不复存在,于是以身份为键的允许列表变成以一个共享地址为键的列表。这是交换而非缺陷:要维护的规则更少,出问题时归因更粗。
应用层认证和请求日志必须承担网络不再报告的东西。把网关容量和更换当作共享基础设施,因为单节点重启现在会中断其背后的每个工作负载;同时尽早检查回程路径:远端必须知道如何到达转换后的地址,通常要经由一条人或流程、而不是清单所拥有的路由或 NAT。地址规划也属于这里,因为重叠的集群与远端网段会破坏任一模型,采用 IPv6 并不能为冲突的方案开脱。
按所有权做决定,然后测试你承诺过的故障
当远程服务对单个工作负载做认证、只有少数 Deployment 需要这条链路、以及故障应当留在局部时,选择按 Pod 建隧道。当网络团队已经拥有路径、许多工作负载共用一个目的地、以及稳定源地址正是这次练习的全部目的时,选择网关。
无论哪种,都要验证你卖出的属性,而不是稳态。创建一个 Pod,让它立即发送,如果承诺是没有任何东西逃出隧道,就观察最初几秒。扩到五个副本,如果承诺是按工作负载的身份,就数一数远端报告的会话数。这两项检查比审阅清单更快地暴露机制的局限,也会告诉你下一个事故归哪个团队。