如何用 Cilium Egress Gateway 将 Pod 出集群流量经固定网关节点 SNAT 转发
2026/9/13 17:23:33 网站建设 项目流程

如何用 Cilium Egress Gateway 将 Pod 出集群流量经固定网关节点 SNAT 转发

【免费下载链接】ciliumeBPF-based Networking, Security, and Observability项目地址: https://gitcode.com/GitHub_Trending/ci/cilium

如果你的 Pod 出集群的流量需要集中走某台固定节点(例如老防火墙只放行固定出口 IP),Cilium 的 Egress Gateway 功能可以把来自特定 Pod、发往集群外 CIDR 的 IPv4/IPv6 连接统一重定向到指定的"网关节点",并用该节点上可预测的出口 IP 做 masquerade(SNAT)。本文基于 Cilium 官方文档 Egress Gateway,覆盖启用功能、编写CiliumEgressGatewayPolicy、按官方测试流程验证的完整路径。适用前提:Cilium 已正确安装在 Kubernetes 集群中(可运行cilium status确认)。

前提条件

在动手之前,确认以下条件都满足:

  1. 网关节点的网卡与 IP 已由运维预先配好。Cilium 直接使用网关节点上的网络接口和 IP,这部分取决于你的网络环境。例如在 AWS/EKS 上,通常需要创建并挂载一个或多个 Elastic Network Interface 并配上 IP,其他云厂商有类似的构造方式。
  2. 必须同时开启 BPF masquerade 和 kube-proxy replacement,二者是 egress gateway 的硬性要求。
  3. 身份存储必须用 Kubernetes CRD 模式。egress gateway 与kvstore身份分配模式不兼容,需保证identityAllocationModecrd(新安装的默认值)。
  4. 与以下功能不兼容:Cluster Mesh(网关节点必须与所选 Pod 在同一集群)、CiliumEndpointSlice。
  5. 无论以哪种方式配置出口 IP,都要通过 agent 选项--devices确保 Cilium 运行在所选的出集群网卡上。

启用 Egress Gateway 功能

文档给出两种等价方式,任选其一。

Helm 方式:升级 Cilium chart 并追加三个--set参数。<chart引用>替换为你安装 Cilium 时使用的 chart 引用(chart 名称加版本,或 OCI 镜像地址),与现有安装保持一致:

helm upgrade cilium <chart引用> \ --namespace kube-system \ --reuse-values \ --set egressGateway.enabled=true \ --set bpf.masquerade=true \ --set kubeProxyReplacement=true

ConfigMap 方式:在 Cilium 的 ConfigMap 中设置以下三个键:

enable-bpf-masquerade: true enable-egress-gateway: true kube-proxy-replacement: true

无论用哪种方式,改完后都要滚动重启 agent 和 operator 使配置生效:

kubectl rollout restart ds cilium -n kube-system kubectl rollout restart deploy cilium-operator -n kube-system

编写 CiliumEgressGatewayPolicy

驱动该功能的 API 是集群作用域的CiliumEgressGatewayPolicy(CRD),因此metadata中不要写namespace

apiVersion: cilium.io/v2 kind: CiliumEgressGatewayPolicy metadata: name: example-policy

选择源 Pod

selectors通过标签选择器匹配源 Pod,支持matchLabelsmatchExpressions,也可以写多个podSelector。要限定某个 namespace,用特殊标签io.kubernetes.pod.namespace;要限定 Pod 所在的节点,可追加nodeSelector(它不能单独使用,必须与podSelector搭配)。注意只有安全身份相关的标签才会被计入:

selectors: - podSelector: matchLabels: org: empire class: mediabot io.kubernetes.pod.namespace: default nodeSelector: # 可选,不写则策略对所有节点生效 matchLabels: node.kubernetes.io/name: node1

选择目的地址

destinationCIDRs指定一条或多条出集群 CIDR;集群内部 IP(Pod、Node、API Server)即使落在范围内也会被自动排除在 SNAT 逻辑之外。可用excludedCIDRs在大段中挖出例外,例如destinationCIDRsa.b.0.0/16excludedCIDRsa.b.c.0/24时,只有发往a.b.c.0/24的流量不走 egress gateway:

destinationCIDRs: - "a.b.c.d/32" - "e.f.g.0/24" - "a:b::/48" excludedCIDRs: - "a.b.c.0/24"

选择网关节点与出口 IP

egressGateway.nodeSelector按节点标签匹配网关节点。若多个节点同时匹配,会取按名称字典序的第一个;若没有任何节点匹配,Cilium 会直接丢弃命中这些目的 CIDR 的流量。

出口网卡和出口 IP 有三种配置方式,三选一:

# 方式 1:指定网卡,使用该网卡上的第一个 IPv4 与 IPv6 地址 egressGateway: nodeSelector: matchLabels: testLabel: testVal interface: ethX # 方式 2:显式指定出口 IP;出集群网卡由数据通路按报文动态做路由查找确定 # 注意:该 IP 必须已分配到节点上的某个网络设备 egressGateway: nodeSelector: matchLabels: testLabel: testVal egressIP: a.b.c.d # 方式 3:两者都省略,选择拥有默认路由的网卡,并用其第一个 IPv4/IPv6 作为出口 IP egressGateway: nodeSelector: matchLabels: testLabel: testVal

使用限制(均直接来自文档,配置前务必核对):

  • egressIPinterface不能同时出现,同时出现的策略会被 Cilium 忽略;
  • 如果 Cilium 无法为策略选出出口 IP(例如egressIP没有配置在网关节点的任何网卡上),网关节点会以No Egress IP configured为由丢弃命中策略的流量;
  • Cilium 选定(或选定失败)网卡与出口 IP 后,不会自动响应网关节点网络配置的后续变化(如 IP 被添加或删除)。此时重新 apply 一次该策略可以强制重新选择;
  • 也可以在同一策略里用egressGateways列表选择多个网关节点,每个条目配置项与egressGateway完全相同;同一端点只会被分配到其中一个网关(按 CiliumEndpoint 的 UID 决定),列表成员发生变化时端点会重新分配。

完整测试流程:客户端 Pod + 外部 Nginx

文档给出的测试假设一个两节点集群,节点 IP 分别为192.168.60.11(node1)和192.168.60.12(node2):客户端 Pod 部署在 node1,策略把 node2 选为网关节点。

准备一个可观测出口的外部服务(可选)

如果没有现成的外部服务,可以在集群之外的一台 Linux 主机上装 Nginx,用它的访问日志观察请求实际来自哪个 IP。以下两条命令会在该外部主机上安装并启动系统 nginx 服务(需要 root 权限):

sudo apt install nginx sudo systemctl start nginx

文档示例中该主机的 IP 为192.168.60.13,把它作为出口流量的目的地址。

部署客户端 Pod

仓库中提供了现成的客户端示例 examples/kubernetes-dns/dns-sw-app.yaml(名为mediabot的 Pod,带org: empireclass: mediabot标签):

kubectl create -f examples/kubernetes-dns/dns-sw-app.yaml kubectl get pods

文档示例输出(仅供对照,实际时间戳和镜像解析结果会有差异):

NAME READY STATUS RESTARTS AGE mediabot 1/1 Running 0 14s

先做一次基线请求:

kubectl exec mediabot -- curl http://192.168.60.13:80

此时在外部主机上查看 Nginx 访问日志:

tail /var/log/nginx/access.log

文档示例输出:

192.168.60.11 - - [04/Apr/2021:22:06:57 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.52.1"

因为客户端 Pod 跑在192.168.60.11节点上,且尚未配置 egress gateway 策略,流量会带着所在节点的 IP 出集群——这正是没有策略时的预期行为。

给网关节点打标签并应用策略

仓库中的示例策略文件是 examples/kubernetes-egress-gateway/egress-gateway-policy.yaml,按文档要求做两处调整:

  1. destinationCIDRs改成包含你的外部服务 IP(例如192.168.60.13/32);
  2. egressIP字段是可选的。为了简化,可以把它注释掉,此时 agent 会使用默认路由网卡上分配的 IPv4/IPv6 地址作为出口 IP。

另外说明一点:该示例文件里除了单网关的egressGateway字段,还带有一个egressGateways多网关示例块;若使用egressGateways字段,egressGateway字段的内容会被忽略,单网关测试时可以将其移除。

给目标网关节点打标签(<egress-gateway-node>替换为你选定的网关节点名;注意它应与mediabotPod 所在节点不同):

kubectl label nodes <egress-gateway-node> egress-node=true

然后应用策略:

kubectl apply -f egress-gateway-policy.yaml

应用后,mediabotPod 的全部出集群流量都会经网关节点转发,并以该节点配置的出口 IP 做 SNAT。

验证结果

再次从客户端 Pod 发起请求:

kubectl exec mediabot -- curl http://192.168.60.13:80

回到外部主机看 Nginx 访问日志。策略生效的标志是:日志中的来源 IP 从客户端所在节点 IP 变成了选定的出口 IP。文档示例输出:

192.168.60.100 - - [04/Apr/2021:22:06:57 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.52.1"

策略不生效时如何排查

策略行为不符合预期时,可以查看任意一个 cilium agent 容器中的 egress 配置(配置会同步到所有 agent):

kubectl -n kube-system exec ds/cilium -- cilium-dbg bpf egress list

文档示例输出:

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 的 IP;
  • Gateway IP:命中策略nodeSelector的网关节点的内部 IP;
  • Egress IP:除网关节点上的 agent 外,其他 agent 上显示为0.0.0.0;网关节点上的 agent 应显示实际使用的出口 IP(若策略指定了egressIP,即该值)。

如果列表中没有预期条目,按文档提示检查 Pod 与网关节点的标签是否与策略选择器匹配。另外两个已知边界要注意:新启动的 Pod 存在策略生效前的延迟窗口,期间其流量可能以 Pod IP 或节点 IP 出集群且不经网关;网关节点的网络配置发生变化后需要重新 apply 策略才会触发出口 IP 重选。

进阶:SNAT 连接数限制

当大量客户端经同一个出口 IP 连接同一个远端端点时,Cilium 的 SNAT map 会到达容量上限,旧连接会被驱逐,导致连接中断。该上限等于 NAT 最大端口值(65535)减去--node-port-range上界(默认 32767),即默认 32768 条到同一远端地址(同一出口 IP)的连接。详细分析与处理见 SNAT Connection Limits:agent 会统计 top 30 的连接五元组,每 30 秒刷新一次,可在 agent 容器内查看:

kubectl -n kube-system exec ds/cilium -- cilium-dbg shell -- db/show nat-stats

输出(文档示例)形如:

# IPFamily Proto EgressIP RemoteAddr Count ipv4 TCP 172.18.0.2 104.198.14.52:443 294 ipv4 TCP 172.18.0.2 172.18.0.4:6443 50

若某一行连接数接近默认上限 32768,即可能出现了 SNAT 连接溢出;文档给出的唯一解决方向是减少经同一出口 IP 到同一远端地址的 SNAT 连接数(让客户端少建连接,或用多个出口 IP / 拆分远端地址分散流量)。

小结与限制

到这里,一个连续的验证路径是:启用三项配置并重启 Cilium → 应用CiliumEgressGatewayPolicy→ 用客户端 Pod 对外部 Nginx 的请求日志确认来源 IP 从节点 IP 变为出口 IP → 用cilium-dbg bpf egress list核对每条策略的 Source/Destination/Egress/Gateway 四列。需要记住的边界:该功能与 Cluster Mesh、CiliumEndpointSlice、kvstore身份模式互斥;egressIPinterface不能同设;网关节点标签无匹配时命中流量会被丢弃;更换网关节点会断开已有的出集群连接。

【免费下载链接】ciliumeBPF-based Networking, Security, and Observability项目地址: https://gitcode.com/GitHub_Trending/ci/cilium

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询