跳到主要内容

Egress Gateway 应用实践

概述

本文介绍如何使用 Cilium 的 Egress Gateway 和 CiliumEgressGatewayPolicy 来灵活控制哪些集群外访流量用哪个出口 IP 出去。

已知问题

使用 Cilium 的 Egress Gateway 功能存在以下已知问题:

  1. 对新 Pod 执行 Egress 策略有延迟。新 Pod 启动后,如果该 Pod 命中 Egress 策略,期望 Pod 的外访流量走指定出口网关出去,但实际上在 Pod 刚启动的一段时间内,该策略可能并未生效,不过这个时间通常会很短,大部分场景不受影响。
  2. 与 Cilium 的 Cluster Mesh 和 CiliumEndpointSlice 功能不兼容。

启用 Egress Gateway

如果要启用 Egress Gateway,需满足以下条件:

  1. 启用 cilium 替代 kube-proxy。
  2. 启用 ip masquerade,且使用 bpf 的实现进行 masquerade 而非默认的 iptables 实现。
  3. 仅 VPC-CNI Native Routing 模式额外要求:必须配置 ipMasqAgent.config.nonMasqueradeCIDRs 覆盖 VPC 全部网段(主网段+全部辅助网段),否则跨节点 Pod-to-Pod 流量会被 BPF masquerade 错误 SNAT 成节点 IP 或 link-local 地址,对端节点无法把 SNAT 后的源 IP 还原回原始 Pod 的 cilium identity,跨节点 NetworkPolicy 失效
Overlay 模式不受此约束

Overlay 模式(VPC-CNI / GR)的跨节点 Pod-to-Pod 流量走 vxlan 封装,外层是节点 IP,内层 Pod IP 原样保留。BPF masquerade 只对最外层、出节点物理网卡的流量生效,看不到内层 Pod IP,所以不需要配 nonMasqueradeCIDRs

下文 nonMasqueradeCIDRs 相关的全部内容(脚本交互、helm 参数、选型说明)只适用于 Native Routing 场景

一键启用

可使用脚本一键启用 Egress Gateway(自动处理 helm upgrade 和组件重启):

bash -c "$(curl -sfL https://raw.githubusercontent.com/imroc/tke-guide/main/static/scripts/cilium.sh)" -- enable-egress-gateway

如果网络环境无法连接 GitHub,可使用站点地址:

bash -c "$(curl -sfL https://imroc.cc/tke/scripts/cilium.sh)" -- enable-egress-gateway
Native Routing 集群上脚本会自动处理 nonMasqueradeCIDRs

在 VPC-CNI Native Routing 集群上执行 enable-egress-gateway 时,脚本按以下优先级自动确定 nonMasqueradeCIDRs

  1. 环境变量 NON_MASQ_CIDRS="10.0.0.0/8 172.16.0.0/12 ..."(空格分隔)—— 适合非交互场景(CI / Terraform)
  2. 自动复用 TKE 集群自带的 kube-system/ip-masq-agent-config ConfigMap(TKE 安装该插件时会把 VPC 主网段+辅助网段写入其中)
  3. 交互提示输入(默认填 RFC 1918 三段全集 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16,可覆盖任意合法腾讯云 VPC 配置)

Overlay 集群上脚本不会问也不会注入这个配置。

手动启用

根据当前 cilium 安装的路由模式选择对应方案:

启用 Egress Gateway 的 cilium 安装方法(高亮部分为相对默认安装方案的新增/修改项):

helm upgrade --install cilium cilium/cilium --version 1.19.4 \
--namespace kube-system \
--set image.repository=quay.tencentcloudcr.com/cilium/cilium \
--set envoy.image.repository=quay.tencentcloudcr.com/cilium/cilium-envoy \
--set operator.image.repository=quay.tencentcloudcr.com/cilium/operator \
--set certgen.image.repository=quay.tencentcloudcr.com/cilium/certgen \
--set hubble.relay.image.repository=quay.tencentcloudcr.com/cilium/hubble-relay \
--set hubble.ui.backend.image.repository=quay.tencentcloudcr.com/cilium/hubble-ui-backend \
--set hubble.ui.frontend.image.repository=quay.tencentcloudcr.com/cilium/hubble-ui \
--set nodeinit.image.repository=quay.tencentcloudcr.com/cilium/startup-script \
--set preflight.image.repository=quay.tencentcloudcr.com/cilium/cilium \
--set preflight.envoy.image.repository=quay.tencentcloudcr.com/cilium/cilium-envoy \
--set clustermesh.apiserver.image.repository=quay.tencentcloudcr.com/cilium/clustermesh-apiserver \
--set authentication.mutual.spire.install.agent.image.repository=docker.io/k8smirror/spire-agent \
--set authentication.mutual.spire.install.server.image.repository=docker.io/k8smirror/spire-server \
--set operator.tolerations[0].key="node-role.kubernetes.io/control-plane",operator.tolerations[0].operator="Exists" \
--set operator.tolerations[1].key="node-role.kubernetes.io/master",operator.tolerations[1].operator="Exists" \
--set operator.tolerations[2].key="node.kubernetes.io/not-ready",operator.tolerations[2].operator="Exists" \
--set operator.tolerations[3].key="node.cloudprovider.kubernetes.io/uninitialized",operator.tolerations[3].operator="Exists" \
--set operator.tolerations[4].key="tke.cloud.tencent.com/uninitialized",operator.tolerations[4].operator="Exists" \
--set operator.tolerations[5].key="tke.cloud.tencent.com/eni-ip-unavailable",operator.tolerations[5].operator="Exists" \
--set routingMode=native \
--set endpointRoutes.enabled=true \
--set ipam.mode=delegated-plugin \
--set devices=eth+ \
--set cni.chainingMode=generic-veth \
--set cni.customConf=true \
--set cni.configMap=cni-config \
--set cni.externalRouting=true \
--set extraConfig.local-router-ipv4=169.254.32.16 \
--set localRedirectPolicies.enabled=true \
--set sysctlfix.enabled=false \
--set kubeProxyReplacement=true \
--set k8sServiceHost=$(kubectl get ep kubernetes -n default -o jsonpath='{.subsets[0].addresses[0].ip}') \
--set k8sServicePort=60002 \
--set egressGateway.enabled=true \
--set enableIPv4Masquerade=true \
--set bpf.masquerade=true \
--set ipMasqAgent.enabled=true \
--set ipMasqAgent.config.masqLinkLocal=true \
--set ipMasqAgent.config.nonMasqueradeCIDRs[0]=10.0.0.0/8 \
--set ipMasqAgent.config.nonMasqueradeCIDRs[1]=172.16.0.0/12 \
--set ipMasqAgent.config.nonMasqueradeCIDRs[2]=192.168.0.0/16

然后重启 cilium 组件生效:

kubectl rollout restart ds cilium -n kube-system
kubectl rollout restart deploy cilium-operator -n kube-system
关于 nonMasqueradeCIDRs 的取值

ipMasqAgent.config.nonMasqueradeCIDRs 必须覆盖 VPC 全部网段(主网段+全部辅助网段,包含节点子网和 VPC-CNI Pod 子网),上面示例直接用 RFC 1918 三段全集兜底,可覆盖任意合法腾讯云 VPC 配置,是最省心的做法。

如果你想精确填写,可以从 TKE 集群自带的 kube-system/ip-masq-agent-config ConfigMap 里直接取(TKE 安装 ip-masq-agent 插件时会自动把 VPC 主+辅助网段写进去):

kubectl -n kube-system get cm ip-masq-agent-config -o jsonpath='{.data.config}'
已使用默认安装方案,简化命令

如果你已经使用了 安装cilium使用 helm 安装 cilium 给的安装方法进行了安装,启用 Egress Gateway 的命令可以简化成这样:

helm upgrade cilium cilium/cilium --version 1.19.4 \
--namespace kube-system \
--reuse-values \
--set egressGateway.enabled=true \
--set enableIPv4Masquerade=true \
--set bpf.masquerade=true \
--set ipMasqAgent.enabled=true \
--set ipMasqAgent.config.masqLinkLocal=true \
--set ipMasqAgent.config.nonMasqueradeCIDRs[0]=10.0.0.0/8 \
--set ipMasqAgent.config.nonMasqueradeCIDRs[1]=172.16.0.0/12 \
--set ipMasqAgent.config.nonMasqueradeCIDRs[2]=192.168.0.0/16
为什么用 ipMasqAgent.config.nonMasqueradeCIDRs,而不是 ipv4NativeRoutingCIDR?

cilium 提供两种方式告诉 BPF masquerade 哪些流量不要 SNAT:

配置项类型是否够用
ipv4NativeRoutingCIDR单个 CIDR❌ 不够。腾讯云 VPC 支持「主网段 + 多个辅助网段」,VPC-CNI Pod 可从任意一段分配 IP,单值 CIDR 无法表达
ipMasqAgent.config.nonMasqueradeCIDRsCIDR 列表✅ 可列出 VPC 全部网段,TKE 自带 ip-masq-agent 插件也用同名字段,配置可直接复用

所以 Native Routing + Egress Gateway 必须用 nonMasqueradeCIDRs,本指南所有 Native + Egress 场景统一以此为准。

创建 Egress 节点

可以创建一个节点池作为 Egress 节点池,后续可以配置让某些 Pod 出集群的流量经过这些节点出去,创建方法参考 安装cilium新建节点池 部分。

需要注意的是:

  1. 要通过节点池为扩出来的节点打上 label(如 egress-node=true)用以标识的用于 Egress Gateway。
  2. 如果需要出公网,要为节点分配公网 IP。
  3. 如果不希望普通 Pod 调度过去,可以加下污点。
  4. Egress 节点池通常不启用自动伸缩,设置固定数量的节点。
  5. 如果存在 NAT 网关共存的需求(部分 Pod 走 NAT 网关、部分走 Egress 节点),egress 节点池应使用独立的子网,该子网的路由表不配置 NAT 网关路由,详见常见问题 如何让 Egress Gateway 与 NAT 网关共存?

以下是操作创建节点池的具体注意事项参考。

如果通过控制台创建,注意勾选创建弹性公网IP:

新增一下 Labels 和 Taints (可选):

如果通过 terraform 创建,参考以下代码片段:

resource "tencentcloud_kubernetes_native_node_pool" "cilium" {
name = "cilium"
cluster_id = tencentcloud_kubernetes_cluster.tke_cluster.id
type = "Native"
annotations {
name = "node.tke.cloud.tencent.com/beta-image"
value = "ts4-public"
}
# 给扩出来的 Node 打上这个 label
labels {
name = "egress-node"
value = "true"
}
# (可选)给节点加污点,避免普通 Pod 调度到 Egress 节点
taints {
key = "egress-node"
effect = "NoSchedule"
value = "true"
}
native {
# 设置 egress 节点副本数量
replicas = 1
internet_accessible {
# 按流量计费
charge_type = "TRAFFIC_POSTPAID_BY_HOUR"
# 最大出带宽 100Mbps
max_bandwidth_out = 100
}
# 省略其它必要但不相关配置
}

节点池创建并初始化节点后,通过如下方式查看哪些节点是 egress 节点,以及分配的公网 IP 是什么:

$ kubectl get nodes -o wide -l egress-node=true
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
172.22.48.125 Ready <none> 3h17m v1.32.2-tke.6 172.22.48.125 43.134.181.245 TencentOS Server 4.4 6.6.98-40.2.tl4.x86_64 containerd://1.6.9-tke.8
172.22.48.48 Ready <none> 3h17m v1.32.2-tke.6 172.22.48.48 43.156.74.191 TencentOS Server 4.4 6.6.98-40.2.tl4.x86_64 containerd://1.6.9-tke.8
172.22.48.64 Ready <none> 3h17m v1.32.2-tke.6 172.22.48.64 43.134.178.226 TencentOS Server 4.4 6.6.98-40.2.tl4.x86_64 containerd://1.6.9-tke.8

配置 CiliumEgressGatewayPolicy

通过配置 CiliumEgressGatewayPolicy 可以灵活的定义哪些 Pod 的流量走哪些网关的出口 IP 出集群,配置方法参考官方文档 Writing egress gateway policies

使用案例

外访流量走固定的 Egress 节点出去

如果希望让外访流量通过固定的 Egress 节点出去(出公网时,出口源 IP 将固定是 Egress 节点绑定的公网 IP),可参考下面的方法配置。

部署一个 nginx 工作负载:

apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:latest

通过配置 CiliumEgressGatewayPolicy 来指定该工作负载使用指定 egress 节点访问公网:

apiVersion: cilium.io/v2
kind: CiliumEgressGatewayPolicy
metadata:
name: egress-test
spec:
selectors:
- podSelector: # 指定该 egress 策略针对哪些 Pod 生效
matchLabels:
app: nginx # 指定带 app=nginx 标签的 Pod
io.kubernetes.pod.namespace: default # 指定 default 命名空间
destinationCIDRs:
- "0.0.0.0/0"
- "::/0"
egressGateway:
nodeSelector:
matchLabels:
kubernetes.io/hostname: 172.22.49.119 # egress 节点名称
# 重要:经测试在 TKE 环境这里必须指定使用 egress 节点的内网 IP,
# 用于决定 egress 节点转发外访流量时使用什么源 IP,不管是转发内网
# 还是公网流量,出 egress 节点时使用的源 IP 都是使用节点的内网 IP。
egressIP: 172.22.49.119

查看 egress 节点:

$ kubectl get nodes -o wide 172.22.49.119
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
172.22.49.119 Ready <none> 69m v1.32.2-tke.6 172.22.49.119 129.226.84.9 TencentOS Server 4.4 6.6.98-40.2.tl4.x86_64 containerd://1.6.9-tke.8

可以看到该节点的公网 IP 为 129.226.84.9,进入 Pod 测试当前出口 IP:

$ kubectl -n default exec -it deployment/nginx -- curl ifconfig.me
129.226.84.9

可以看到最终的出口 IP 就是 129.226.84.9,符合预期。

外访流量走一组 Egress 节点出去

如果希望让外访流量通过固定的一组 Egress 节点出去(出公网时,出口源 IP 将固定是 Egress 节点绑定的公网 IP),可参考下面的方法配置。

部署一个 nginx 工作负载:

apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
namespace: default
spec:
replicas: 10
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:latest

通过配置 CiliumEgressGatewayPolicy 来指定该工作负载通过一组 Egress 节点进行流量外访:

apiVersion: cilium.io/v2
kind: CiliumEgressGatewayPolicy
metadata:
name: egress-test
spec:
selectors:
- podSelector: # 指定该 egress 针对哪些 Pod 生效
matchLabels:
app: nginx # 指定带 app=nginx 标签的 Pod
io.kubernetes.pod.namespace: default # 指定命名空间
destinationCIDRs:
- "0.0.0.0/0"
- "::/0"
egressGateway: # 该字段是必填的,如果要指定多个 egress 节点,这里还是必须要指定一个,不然会报错: spec.egressGateway: Required value
nodeSelector:
matchLabels:
kubernetes.io/hostname: 172.22.49.20 # egress 节点名称
egressIP: 172.22.49.20 # egress 节点内网 IP
egressGateways: # 其余的 egress 节点追加到这个列表
- nodeSelector:
matchLabels:
kubernetes.io/hostname: 172.22.49.147
egressIP: 172.22.49.147
- nodeSelector:
matchLabels:
kubernetes.io/hostname: 172.22.49.119
egressIP: 172.22.49.119

测试可以看到工作负载中各个 Pod 使用的出口公网 IP 可能不同:

$ kubectl get pods -o jsonpath='{range .items[*]}{.metadata.name}{"\n"}{end}' | xargs -I {} sh -c 'kubectl exec -n default -it {} -- curl -s ifconfig.me 2>/dev/null || echo "Failed"; printf ":\t%s\n" "{}"'
129.226.84.9: nginx-54c98b4f84-5wlpc
43.156.123.70: nginx-54c98b4f84-6jx8n
43.156.123.70: nginx-54c98b4f84-82wmq
129.226.84.9: nginx-54c98b4f84-8ptvh
129.226.84.9: nginx-54c98b4f84-jfr2x
129.226.84.9: nginx-54c98b4f84-jlrr7
43.156.123.70: nginx-54c98b4f84-mpvpz
129.226.84.9: nginx-54c98b4f84-s7q4s
43.156.123.70: nginx-54c98b4f84-vsnng
43.156.123.70: nginx-54c98b4f84-xt8bs

但都使用的当前定义的这组 egress 节点所绑定的公网 IP:

$ kubectl get nodes -o custom-columns="NAME:.metadata.name,EXTERNAL-IP:.status.addresses[?(@.type=='ExternalIP')].address" -l egress-node=true
NAME EXTERNAL-IP
172.22.49.119 129.226.84.9
172.22.49.147 43.156.123.70
172.22.49.20 43.163.1.23

集群所有外访流量统一走 Egress 节点出去

如果要让集群中所有 Pod 的外访流量统一走 Egress 节点出去,可以使用 podSelector: {} 选中集群全部 Pod:

apiVersion: cilium.io/v2
kind: CiliumEgressGatewayPolicy
metadata:
name: egress-test
spec:
selectors:
- podSelector: {} # 选中集群全部 Pod
destinationCIDRs:
- "0.0.0.0/0"
- "::/0"
egressGateway: # 该字段是必填的,如果要指定多个 egress 节点,这里还是必须要指定一个,不然会报错: spec.egressGateway: Required value
nodeSelector:
matchLabels:
kubernetes.io/hostname: 172.22.49.20 # egress 节点名称
egressIP: 172.22.49.20 # egress 节点内网 IP
egressGateways: # 其余的 egress 节点追加到这个列表
- nodeSelector:
matchLabels:
kubernetes.io/hostname: 172.22.49.147
egressIP: 172.22.49.147
- nodeSelector:
matchLabels:
kubernetes.io/hostname: 172.22.49.119
egressIP: 172.22.49.119

不同环境或不同业务 Pod 的外访流量走不同 Egress 节点出去

如果不同环境或不同业务的 Pod 通过 namespace 隔离,可以指定某 namespace 下 Pod 的外访流量走指定 Egress 节点出去:

apiVersion: cilium.io/v2
kind: CiliumEgressGatewayPolicy
metadata:
name: egress-test
spec:
selectors:
- podSelector:
matchLabels:
io.kubernetes.pod.namespace: prod # 指定 prod 命名空间下所有 Pod
destinationCIDRs:
- "0.0.0.0/0"
- "::/0"
egressGateway:
nodeSelector:
matchLabels:
kubernetes.io/hostname: 172.22.49.119
egressIP: 172.22.49.119

如果通过 label 来区分不同业务,可以指定所有 namespace 下指定 label 的 Pod 的外访流量走指定 Egress 节点出去:

apiVersion: cilium.io/v2
kind: CiliumEgressGatewayPolicy
metadata:
name: egress-test
spec:
selectors:
- podSelector:
matchLabels:
business: mall # 指定所有含 business=mall 的 Pod
destinationCIDRs:
- "0.0.0.0/0"
- "::/0"
egressGateway:
nodeSelector:
matchLabels:
kubernetes.io/hostname: 172.22.49.119
egressIP: 172.22.49.119

常见问题

配置策略后网络不通

首先确认 CiliumEgressGatewayPolicy 配置方法是否正确,在 TKE 环境下,确保 egressGateway 的 nodeSelector 只选中一个 node,egressIP 必须配置该 node 的内网 IP,否则可能就会出现不通的问题。

另外还可以登录 egress 节点所在的 cilium pod,执行 cilium-dbg bpf egress list 查看当前节点上的 egress bpf 规则:

$ kubectl -n kube-system exec -it cilium-nz5hd -- bash
root@VM-49-119-tencentos:/home/cilium# cilium-dbg bpf egress list
Source IP Destination CIDR Egress IP Gateway IP
172.22.48.4 0.0.0.0/0 172.22.49.119 172.22.49.119
172.22.48.10 0.0.0.0/0 0.0.0.0 172.22.49.147
172.22.48.14 0.0.0.0/0 0.0.0.0 172.22.49.147
172.22.48.37 0.0.0.0/0 0.0.0.0 172.22.49.147
172.22.48.38 0.0.0.0/0 172.22.49.119 172.22.49.119
172.22.48.39 0.0.0.0/0 172.22.49.119 172.22.49.119
172.22.48.41 0.0.0.0/0 0.0.0.0 172.22.49.147
172.22.48.42 0.0.0.0/0 0.0.0.0 172.22.49.147
172.22.48.43 0.0.0.0/0 172.22.49.119 172.22.49.119
172.22.48.44 0.0.0.0/0 172.22.49.119 172.22.49.119
172.22.48.45 0.0.0.0/0 172.22.49.119 172.22.49.119
172.22.48.46 0.0.0.0/0 0.0.0.0 172.22.49.147
172.22.48.47 0.0.0.0/0 0.0.0.0 172.22.49.147

Source IP 是 Pod IP,Egress IP 是走当前节点出去使用的源 IP, 0.0.0.0 表示当前节点没有转发对应 Pod IP 的流量,如果全都为 0.0.0.0 表示没有 egress 规则选中当前节点。

出口 IP 不符预期

Egress Gateway 的流量最终是从 egress 节点出去的,如果 egress 节点所在子网的 VPC 路由表配置了 0.0.0.0/0 下一跳为 NAT 网关,那么流量从 egress 节点出公网时会被 NAT 网关截获,最终出口 IP 变成 NAT 网关的 IP 而非 egress 节点绑定的 EIP。

排查步骤:

  1. 确认 egress 节点所在子网绑定的路由表中,是否存在 0.0.0.0/0 指向 NAT 网关的路由规则。
  2. 如果存在,移除该路由规则或将 egress 节点迁移到不绑 NAT 网关路由的子网中。
注意

这里的关键是 egress 节点所在子网的路由表,而非 work 节点(client Pod 所在节点)子网的路由表。Egress Gateway 通过隧道将流量从 work 节点转发到 egress 节点,该隧道通信走 VPC 内网路由,不受 work 子网公网路由的影响。

如何让 Egress Gateway 与 NAT 网关共存?

场景:集群中大部分 Pod 默认走 NAT 网关出公网,仅部分工作负载需要通过 Egress Gateway 走固定的 EIP 出去。

方案:将 work 节点和 egress 节点放在不同子网,两个子网绑定不同的路由表:

  • work 子网的路由表:0.0.0.0/0 下一跳为 NAT 网关(未命中 Egress 策略的 Pod 通过 NAT 网关出公网)
  • egress 子网的路由表:不配置 NAT 网关路由(命中 Egress 策略的流量通过 egress 节点 EIP 出公网)

这个方案可行的原因是 Cilium Egress Gateway 使用隧道封装(VXLAN)将流量从 work 节点转发到 egress 节点,隧道外层目的 IP 是 egress 节点的内网 IP,走的是 VPC 内网路由,不会被 work 子网的 NAT 网关路由截获:

配置步骤:

  1. 在 VPC 中创建两个子网(如 work-subnetegress-subnet),并分别关联不同的路由表。
  2. work-subnet 路由表中添加 0.0.0.0/0 → NAT 网关
  3. egress-subnet 路由表中不添加 NAT 网关路由。
  4. work 节点池使用 work-subnet,egress 节点池使用 egress-subnet
  5. 按正常流程配置 CiliumEgressGatewayPolicy 即可。

未命中 Egress 策略的 Pod 外访流量走正常路径(BPF masquerade 将 Pod IP SNAT 为 work 节点 IP → 从 work 节点出去 → work 子网路由表将流量导向 NAT 网关)。

注意
  1. 确保 work 子网和 egress 子网之间的安全组互通 UDP 4789(VXLAN 隧道端口)。
  2. egressIP 填 egress 节点的内网 IP,不是 EIP。
  3. 如果 egress 节点本身也需要访问公网(如拉取镜像),可以单独给 egress 子网配置特定的路由规则,或确保 egress 节点有 EIP 且安全组出方向放通。

如何让外访流量走 VPC 之外的机器出去?

在某些特定场景下,可能希望某些 Pod 外访流量通过 VPC 之外的指定机器出去(比如业务出口 IP 在其它 VPC、其它云或者 IDC 机房,且第三方绑了该 IP 到白名单,不方便更改,需要让外访流量走这个 IP 所在机器出去),而 cilium 使用 CiliumEgressGatewayPolicy 配置策略时必须要求 Egress 机器是当前集群的节点,正常情况下,TKE 集群添加的节点都是 VPC 内的机器,如何实现让外访流量走 VPC 之外的机器出去?

可以将 VPC 之外的机器以注册节点的形式加入到 TKE 集群中,然后在 CiliumEgressGatewayPolicy 中配置 egress gateway 为该节点即可。

具体操作方法是:

  1. 在安装 cilium 之前,先在 TKE 集群的基本信息页面中启用注册节点,专线连接勾选开启支持(启用后,集群的 apiserver 地址将发生变化,cilium 替代了 kube-proxy,需感知 apiserver 地址,这也是为什么要在安装 cilium 之前启用)。
  2. 安装 cilium 并启用 Egress Gateway。
  3. 准备 VPC 之外的 Egress 机器,主要要求网络与 TKE 集群所在 VPC 打通,并且 Linux 内核版本 >= 5.10。
  4. 新建一个注册节点池,建议 Labels 和 Taints 都打上(Taints 示例 egress-node=true:NoSchedule,可避免普通 Pod 被调度到该节点上,因为注册节点无法使用 VPC-CNI 网络插件,无法分配到 Pod IP,只能使用 HostNetwork)。
  5. 进入新建的注册节点池,点击新建节点,按照提示复制注册脚本并在 VPC 之外的 Egress 机器上执行,让该机器作为节点加入到 TKE 集群中。
  6. 按需配置 CiliumEgressGatewayPolicy,让指定的外访流量走该 VPC 之外的机器出去,示例(注意替换节点名称和 egressIP 的值):
    apiVersion: cilium.io/v2
    kind: CiliumEgressGatewayPolicy
    metadata:
    name: egress-test
    spec:
    selectors:
    - podSelector: # 指定该 egress 策略针对哪些 Pod 生效
    matchLabels:
    app: nginx # 指定带 app=nginx 标签的 Pod
    io.kubernetes.pod.namespace: test # 指定 default 命名空间
    destinationCIDRs:
    - "0.0.0.0/0"
    - "::/0"
    egressGateway:
    nodeSelector:
    matchLabels:
    kubernetes.io/hostname: node-10.111.128.148 # egress 注册节点名称
    # 重要:经测试在 TKE 环境这里必须指定使用 egress 节点的内网 IP,
    # 用于决定 egress 节点转发外访流量时使用什么源 IP,不管是转发内网
    # 还是公网流量,出 egress 节点时使用的源 IP 都是使用节点的内网 IP。
    egressIP: 10.111.128.148

参考资料