Cilium Egress Gateway 高级故障排查:SNAT 连接数上限与源端口耗尽问题深入解析
Cilium Egress Gateway 高级故障排查SNAT 连接数上限与源端口耗尽问题深入解析【免费下载链接】ciliumeBPF-based Networking, Security, and Observability项目地址: https://gitcode.com/GitHub_Trending/ci/ciliumCilium 的 Egress Gateway出口网关功能可以把 Pod 的出口流量路由到指定的网关节点并以可预测的 Egress IP 做 SNAT 伪装从而与遗留防火墙等外部基础设施对接。但当一个 Egress IP 承载了过多并发连接时会触及 Cilium 数据面 NAT 映射的硬性上限导致旧连接被驱逐、新连接建立失败。本文基于 Documentation/network/egress-gateway/egress-gateway-troubleshooting.rst 展开结合仓库源码系统性讲解 SNAT 连接数上限的成因、判定方法与缓解方案并覆盖 Egress Gateway 的基础配置与常见排障手段帮助你快速定位和解决生产中可能遇到的连接中断问题。问题背景为什么 Egress Gateway 会触发 SNAT 连接数上限Egress Gateway 的工作方式回顾在深入故障场景之前先简要回顾 Egress Gateway 的机制。该功能将来自 Pod、目的地为特定集群外 CIDR 的 IPv4/IPv6 连接经由被选为网关节点gateway node的特定节点转发并在离开集群时使用与网关节点绑定的、可预测的 IPEgress IP进行伪装masquerade。典型场景是与遗留防火墙配合防火墙只放行来自特定源 IP 的流量而 Pod 的 IP 会频繁变化节点 IP 也可能随生命周期变化因此需要一个稳定、可预测的出口源 IP。详细的功能启用与策略配置见 Documentation/network/egress-gateway/egress-gateway.rst。关键点在于Egress Gateway 必须同时启用 BPF masquerading 与 kube-proxy replacement对应 Helm 参数bpf.masqueradetrue、kubeProxyReplacementtrue或 ConfigMap 中的enable-bpf-masquerade、enable-egress-gateway、kube-proxy-replacement。也就是说出口流量的 SNAT 由 Cilium 的 BPF NAT 映射NAT map完成而这正是 SNAT 连接数上限问题的根源。问题本质每个远端端点可分配的源 IP 数量存在上限Egress Gateway 被用于向一小撮远端端点伪装流量时可能因为超过 Cilium NAT 映射为每个远端端点可分配的源 IP 数量上限而引发问题。当上限被突破时旧连接会被自动驱逐以给新连接腾出空间从而导致已有连接中断、新连接也可能失败。典型故障场景防火墙白名单下的连接雪崩场景描述假设你有一个运行 Cilium Egress Gateway 的 Kubernetes 集群策略配置为使用 Egress IP10.1.0.0伪装发往10.2.0.0:8080服务器的外部连接该服务器位于防火墙之后且防火墙只允许源 IP 为10.1.0.0的连接通过。集群中大量客户端都会以相同的四元组连接后端服务器{egress-IP, 远端端点 IP, 远端端点端口} {10.1.0.0, 10.2.0.0, 8080}这些连接拥有相同的源 IP 与相同的目的 IP、目的端口。在 Cilium 数据面中通往同一目的地的每条连接会使用唯一的源端口进行 NAT 映射。由于源端口数量有限当并发连接过多时SNAT 映射的可用源端口被耗尽。触发链路从连接建立到端口耗尽大量 Pod 通过网关节点访问10.2.0.0:8080数据面为每条连接分配一个唯一的源端口该源端口取自 Egress IP 的可用端口区间源端口用尽后旧连接从 NAT 映射中被自动驱逐以容纳新连接被驱逐的旧连接不再被跟踪表现为连接中断同时新连接也因找不到可用源端口而建立失败。从源码结构看NAT 映射中的连接元组被定义为四元组{port, egressIP, remoteEndpointIP, remoteEndpointPort}见 pkg/maps/nat/stats/cell.go 的注释即共享同一 Egress IP 与同一远端端点地址的一组被翻译连接。数据面为这类元组分配 Egress 源端口当同一端点连接过多时就可能出现源端口耗尽或分配失败。连接数上限的计算方式默认上限32768 个连接连接上限等于最大 NAT NodePort 值65535与--node-port-range上界的差值。默认情况下--node-port-range上界为 32767因此一个 Egress Gateway 节点默认可以处理65535 - 32767 32768即默认情况下使用同一个 Egress IP 访问同一个远端端点地址最多支持 32768 个并发连接。源码依据这一上限在 pkg/maps/nat/stats/stats.go 中得到印证常量nodePortMaxNAT 65535第 127 行可用源端口数按maxAvailPorts : nodePortMaxNAT - (params.LBConfig.NodePortMax 1)计算第 142 行即可用源端口 65535 - (NodePort 上界 1)当NodePortMax为默认值 32767 时结果为 32767与文档中约 32768的量级一致精确值取决于是否把 0 号端口计入。NodePort 范围的默认值在 pkg/loadbalancer/config.go 中定义NodePortMinDefault与NodePortMaxDefault 32767第 111 行通过解析--node-port-range配置项node-port-range第 56 行覆盖默认值cfg.NodePortMax max第 468 行。这意味着如果你上调了--node-port-range的上界SNAT 可用的源端口区间会被进一步压缩Egress Gateway 单端点的并发连接上限会相应降低。反之缩小 NodePort 范围可以为 SNAT 留出更多源端口。需要区分的两类问题连接数逼近上限NAT 映射接近容量旧连接开始被驱逐表现为存量连接随机中断SNAT 源端口映射耗尽SNAT 映射找不到可用源端口完成 masquerade表现为新连接建立失败。两者本质上都是同一上限被逼近/突破的不同阶段表现。文档原文指出High SNAT port mapping utilization can also result in egress-gateway connection failures as Ciliums SNAT mapping fails to find available source ports for masquerade SNAT.诊断工具用 nat-stats 查看连接元组计数查看命令Cilium Agent 会存储计数最高的 30 个连接元组的统计信息可以通过cilium-dbg工具在 cilium agent 容器内访问$ kubectl -n kube-system exec ds/cilium -- cilium-dbg shell -- db/show nat-stats输出示例列为 IPFamily、Proto、EgressIP、RemoteAddr、Count# IPFamily Proto EgressIP RemoteAddr Count ipv4 TCP 10.244.1.160 10.244.3.174:4240 1 ipv4 ICMP 172.18.0.2 172.18.0.3 1 ipv4 TCP 172.18.0.2 172.18.0.3:4240 1 ipv4 TCP 172.18.0.2 172.18.0.4:6443 50 ipv4 TCP 172.18.0.2 104.198.14.52:443 294 ipv6 ICMPv6 [fc00:c111::2] [fc00:c111::3] 1 ipv6 TCP [fd00:10:244:1::ec5d] [fd00:10:244:3::730c]:4240 1 ipv6 TCP [fd00:10:244:1::ec5d] [fd00:10:244::915]:4240 1每一行表示一个连接元组{EgressIP, RemoteAddr, Proto}组合下当前 NAT 映射中的连接计数。统计同时覆盖 IPv4TCP/ICMP与 IPv6TCP/ICMPv6端口为 0 的条目如 ICMP/ICMPv6表示该元组不依赖具体端口。数据生成原理源码级这些统计由nat-stats模块周期性计算得出模块定义见 pkg/maps/nat/stats/cell.go统计周期默认每 30 秒重新计算一次对应配置项NATMapStatInterval30 秒见 cell.go 与 stats.go。因此新连接出现到统计更新之间存在延迟文档特别提示了这一点Top-K 存储仅保留计数最高的前 K 个元组K 默认为 32NatMapStatKStoredEntries上限 4096见 cell.go。文档中所说的top 30即对应此 Top-K 机制的默认配置计数逻辑遍历 IPv4/IPv6 NAT 映射仅统计带TUPLE_F_IN标志且协议为 TCP/ICMP/ICMPv6/UDP 的条目将 DestPort 归零后按元组聚合计数见 stats.go 的countNat函数再通过最小堆实现 Top-K 排序见 stats.go数据落点结果写入名为nat-stats的 StateDB 表TableName见 cell.gocilium-dbg shell -- db/show nat-stats即查询该表。表的行格式定义在NatMapStats结构体的TableHeader/TableRow方法中见 stats.go。阈值判定如果观察到一行或多行的连接计数非常大逼近默认连接上限 32768则可能表明存在 SNAT 连接溢出问题。计数接近上限的元组其 Egress IP 对应的源端口区间已经高度紧张。解决方案减少单个 Egress IP 承载的连接压力由于该问题源于 Cilium Egress Gateway 功能的硬性上限唯一有效的解决方向是减少经由 Egress Gateway 做 SNAT 的连接数量。文档给出的两条路径降低客户端新建连接的频率让客户端复用连接连接池化、HTTP keep-alive、长连接避免大量短生命周期连接不断触发新的 SNAT 映射分散连接压力降低发往同一远端地址的连接数量通过使用不同的 Egress IP分摊流量让流量分散到不同的远端端点地址。例如为不同命名空间或不同业务分配独立的 Egress IP或多网关节点配合egressGateways列表将不同源端点分配到不同网关详见 egress-gateway.rst 的Selecting multiple gateway nodes小节从而把单元组连接数降到上限以下。用指标做告警与可观测性对 SNAT 源端口利用率进行告警和可观测性监控可以使用nat_endpoint_max_connection指标文档中对应:ref:NAT endpoint max connection nat_metrics。该指标跟踪单个 Cilium Agent 的最大饱和度占最大可用源端口数的百分比。源码实现见 cell.go指标名nat_endpoint_max_connection为 Gauge 类型指标说明Max saturation of source ports on a egress-ip/external endpoint tupleEgress IP/外部端点元组上的源端口最大饱和度带family标签ipv4/ipv6见 cell.go计算方式在每次统计时取 Top-K 中计数最高的元组第 1 名计算count / maxPorts并写入指标见 stats.go 与 cell.go。当该指标逼近 1100%时意味着某个 Egress IP/远端端点元组的源端口即将耗尽应触发告警。相关配置参数速查与本文诊断相关的两个 Agent 配置参数见 pkg/maps/nat/stats/cell.go参数默认值说明nat-map-stats-interval30sNAT 映射统计的计算周期0 表示禁用统计nat-map-stats-entries32本地 StateDB 中保存的 Top-K 统计条目数范围 1~4096另外NodePort 范围参数node-port-range默认上界 32767直接决定 SNAT 可用源端口区间调整它会改变单端点连接上限。附Egress Gateway 常见故障排查总览除了 SNAT 连接数上限这一高级话题日常排障中还可使用以下手段完整说明见 egress-gateway.rst 的 Troubleshooting 小节查看 BPF 中的 Egress 配置策略未按预期生效时可在任意 cilium agent 中查看 egress 配置该配置会同步到所有 agent$ kubectl -n kube-system exec ds/cilium -- cilium-dbg bpf egress list Defaulted container cilium-agent out of: cilium-agent, config (init), ... Source IP Destination CIDR Egress IP Gateway IP 192.168.2.23 192.168.60.13/32 0.0.0.0 192.168.60.12解读规则Source IP匹配策略podSelector选中的每个 Pod 的 IPGateway IP匹配策略nodeSelector选中的出口节点的内网IPEgress IP在除网关节点外的所有 agent 上显示为0.0.0.0只有在网关节点上的 agent 才显示实际使用的 Egress IP即策略中显式指定的egressIP。若列表与策略预期不符请检查 Pod 与出口节点上的标签是否与策略 selector 匹配如示例策略中的egress-node: true。关键约束提醒无法为策略选择 Egress IP 时如指定的egressIP未配置到网关节点任何网卡网关节点会以No Egress IP configured为原因丢弃匹配流量网关节点网络配置变更后Cilium 不会自动重新选择 Egress IP/网卡需重新应用策略强制触发新的选择egressIP与interface不能同时出现在egressGateway规格中同时指定会导致策略被忽略更换网关节点会中断现有出口连接单网关与多网关策略均适用多网关场景见 egress-gateway.rst 的 Selecting multiple gateway nodes 小节。示例策略文件仓库提供了可直接参考的完整示例examples/kubernetes-egress-gateway/egress-gateway-policy.yaml含单网关egressGateway与多网关egressGateways两种写法以及配套的 examples/kubernetes-egress-gateway/egress-ip-deployment.yaml。对应文档中的完整实验步骤部署 nginx 外部服务、部署mediabot客户端 Pod、打标签、应用策略、验证 Nginx 访问日志中的源 IP 变化详见 egress-gateway.rst 的 Testing the egress gateway feature 小节。总结Egress Gateway 的 SNAT 连接数上限源于数据面 NAT 映射的源端口数量限制默认node-port-range上界 32767之下单个 Egress IP 到单个远端端点的并发连接上限约为 32768。当大量客户端以相同元组访问同一后端时NAT 映射源端口会耗尽引发旧连接驱逐与新连接失败。排查时可使用cilium-dbg shell -- db/show nat-stats查看 Top-K 连接元组计数默认每 30 秒刷新并配合nat_endpoint_max_connection指标对源端口饱和度进行告警。最终解决方案是降低单 Egress IP 承载的连接数——复用连接、分散到不同 Egress IP 或不同远端端点从而在硬性上限之内维持稳定连接。【免费下载链接】ciliumeBPF-based Networking, Security, and Observability项目地址: https://gitcode.com/GitHub_Trending/ci/cilium创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考