配置 Egress Gateway
关于 Egress Gateway
Egress Gateway 用于控制 Pod 的外部网络访问,使用一组静态地址,具有以下特性:
- 通过 ECMP 实现 Active-Active 高可用,支持水平吞吐量扩展
- 通过 BFD 实现快速故障切换(<1秒)
- 支持 IPv6 和双栈
- 通过 NamespaceSelector 和 PodSelector 实现细粒度路由控制
- 允许通过 NodeSelector 灵活调度 Egress Gateway
同时,Egress Gateway 具有以下限制:
- 使用 macvlan 作为底层网络连接,需底层网络支持 Underlay
- 多实例 Gateway 模式下需要多个 Egress IP
- 当前仅支持 SNAT,不支持 EIP 和 DNAT
- 当前不支持记录源地址转换关系
实现细节
每个 Egress Gateway 由多个具有多个网络接口的 Pod 组成。
每个 Pod 有两个网络接口:一个加入虚拟网络,用于 VPC 内通信,
另一个通过 Macvlan 连接到底层物理网络,用于外部网络通信。
虚拟网络流量最终通过 Egress Gateway 实例内的 NAT 访问外部网络。
每个 Egress Gateway 实例在 OVN 路由表中注册其地址。
当 VPC 内的 Pod 需要访问外部网络时,
OVN 使用源地址哈希将流量转发到多个 Egress Gateway 实例地址,
实现负载均衡。随着 Egress Gateway 实例数量增加,
吞吐量也能水平扩展。
OVN 使用 BFD 协议探测多个 Egress Gateway 实例。
当某个 Egress Gateway 实例故障时,OVN 将对应路由标记为不可用,
实现快速故障检测和恢复。
前提条件
集群中必须安装 Alauda Container Platform Networking for Multus,才能使用 Egress Gateway。
安装 Alauda Container Platform Networking for Multus,请参考安装 Multus CNI。
使用方法
创建 Network Attachment Definition
Egress Gateway 使用多个 NIC 访问内网和外网,
因此需要创建 Network Attachment Definition 以连接外部网络。
下面示例展示了使用 macvlan 插件和 Kube-OVN 提供的 IPAM:
apiVersion: k8s.cni.cncf.io/v1
kind: NetworkAttachmentDefinition
metadata:
name: eth1
namespace: default
spec:
config: '{
"cniVersion": "0.3.0",
"type": "macvlan",
"master": "eth1",
"mode": "bridge",
"ipam": {
"type": "kube-ovn",
"server_socket": "/run/openvswitch/kube-ovn-daemon.sock",
"provider": "eth1.default"
}
}'
---
apiVersion: kubeovn.io/v1
kind: Subnet
metadata:
name: macvlan1
spec:
protocol: IPv4
provider: eth1.default
cidrBlock: 172.17.0.0/16
gateway: 172.17.0.1
excludeIps:
- 172.17.0.2..172.17.0.10
- 连接外部网络的宿主机接口。
- provider 名称,格式为
<network attachment definition name>.<namespace>。
- 用于标识外部网络的 provider 名称,必须与 NetworkAttachmentDefinition 中一致。
- 外部网络的 CIDR。
- 外部网络的网关。
- 排除自动分配的 IP 范围,详情参考使用 Kube-OVN Overlay 网络的 Subnet CR 示例。
TIP
你可以使用任意 CNI 插件创建 Network Attachment Definition 来访问对应网络。
上例的替代方案是使用 Kube-OVN 的 Underlay 子网代替 macvlan。
创建 VPC Egress Gateway
创建 VPC Egress Gateway 资源,示例如下:
apiVersion: kubeovn.io/v1
kind: VpcEgressGateway
metadata:
name: gateway1
namespace: default
spec:
replicas: 1
externalSubnet: macvlan1
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 200m
memory: 256Mi
ephemeral-storage: 2Gi
nodeSelector:
- matchExpressions:
- key: kubernetes.io/hostname
operator: In
values:
- kube-ovn-worker
- kube-ovn-worker2
tolerations:
- key: node-role.kubernetes.io/control-plane
operator: Exists
effect: NoSchedule
selectors:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: default
policies:
- snat: true
subnets:
- subnet1
- snat: false
ipBlocks:
- 10.18.0.0/16
- 创建 VPC Egress Gateway 实例的命名空间。
- VPC Egress Gateway 实例副本数。
- 连接外部网络的外部子网。
- 每个 VPC Egress Gateway 实例的资源请求和限制,未指定时使用控制器默认值。
- 用于调度 VPC Egress Gateway 实例的节点选择器。
- 用于调度 VPC Egress Gateway 实例的容忍度。
- 用于选择通过 VPC Egress Gateway 访问外部网络的 Pod 的命名空间选择器和 Pod 选择器。
- VPC Egress Gateway 的策略,包括 SNAT 及应用的子网/IP 块。
- 是否启用该策略的 SNAT。
- 策略应用的子网。
- 策略应用的 IP 块。
上述资源创建了一个名为 gateway1 的 VPC Egress Gateway,位于 default 命名空间,
以下 Pod 将通过 macvlan1 子网访问外部网络:
- default 命名空间中的 Pod
- subnet1 子网下的 Pod
- IP 属于 CIDR 10.18.0.0/16 的 Pod
NOTE
匹配 .spec.selectors 的 Pod 将启用 SNAT 访问外部网络。
创建完成后,查看 VPC Egress Gateway 资源:
$ kubectl get veg gateway1
NAME VPC REPLICAS BFD ENABLED EXTERNAL SUBNET PHASE READY AGE
gateway1 ovn-cluster 1 false macvlan1 Completed true 13s
查看更多信息:
kubectl get veg gateway1 -o wide
NAME VPC REPLICAS BFD ENABLED EXTERNAL SUBNET PHASE READY INTERNAL IPS EXTERNAL IPS WORKING NODES AGE
gateway1 ovn-cluster 1 false macvlan1 Completed true ["10.16.0.12"] ["172.17.0.11"] ["kube-ovn-worker"] 82s
查看工作负载:
$ kubectl get deployment -l ovn.kubernetes.io/vpc-egress-gateway=gateway1
NAME READY UP-TO-DATE AVAILABLE AGE
gateway1 1/1 1 1 4m40s
$ kubectl get pod -l ovn.kubernetes.io/vpc-egress-gateway=gateway1 -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
gateway1-b9f8b4448-76lhm 1/1 Running 0 4m48s 10.16.0.12 kube-ovn-worker <none> <none>
查看 Pod 中的 IP 地址、路由和 iptables 规则:
$ kubectl exec gateway1-b9f8b4448-76lhm -c gateway -- ip address show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: net1@if13: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether 62:d8:71:90:7b:86 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.17.0.11/16 brd 172.17.255.255 scope global net1
valid_lft forever preferred_lft forever
inet6 fe80::60d8:71ff:fe90:7b86/64 scope link
valid_lft forever preferred_lft forever
17: eth0@if18: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1400 qdisc noqueue state UP group default
link/ether 36:7c:6b:c7:82:6b brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 10.16.0.12/16 brd 10.16.255.255 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::347c:6bff:fec7:826b/64 scope link
valid_lft forever preferred_lft forever
$ kubectl exec gateway1-b9f8b4448-76lhm -c gateway -- ip rule show
0: from all lookup local
1001: from all iif eth0 lookup default
1002: from all iif net1 lookup 1000
1003: from 10.16.0.12 iif lo lookup 1000
1004: from 172.17.0.11 iif lo lookup default
32766: from all lookup main
32767: from all lookup default
$ kubectl exec gateway1-b9f8b4448-76lhm -c gateway -- ip route show
default via 172.17.0.1 dev net1
10.16.0.0/16 dev eth0 proto kernel scope link src 10.16.0.12
10.17.0.0/16 via 10.16.0.1 dev eth0
10.18.0.0/16 via 10.16.0.1 dev eth0
172.17.0.0/16 dev net1 proto kernel scope link src 172.17.0.11
$ kubectl exec gateway1-b9f8b4448-76lhm -c gateway -- ip route show table 1000
default via 10.16.0.1 dev eth0
$ kubectl exec gateway1-b9f8b4448-76lhm -c gateway -- iptables -t nat -S
-P PREROUTING ACCEPT
-P INPUT ACCEPT
-P OUTPUT ACCEPT
-P POSTROUTING ACCEPT
-N VEG-MASQUERADE
-A PREROUTING -i eth0 -j MARK --set-xmark 0x4000/0x4000
-A POSTROUTING -d 10.18.0.0/16 -j RETURN
-A POSTROUTING -s 10.18.0.0/16 -j RETURN
-A POSTROUTING -j VEG-MASQUERADE
-A VEG-MASQUERADE -j MARK --set-xmark 0x0/0xffffffff
-A VEG-MASQUERADE -j MASQUERADE --random-fully
在 Gateway Pod 中抓包验证网络流量:
$ kubectl exec -ti gateway1-b9f8b4448-76lhm -c gateway -- bash
nobody@gateway1-b9f8b4448-76lhm:/kube-ovn$ tcpdump -i any -nnve icmp and host 172.17.0.1
tcpdump: data link type LINUX_SLL2
tcpdump: listening on any, link-type LINUX_SLL2 (Linux cooked v2), snapshot length 262144 bytes
06:50:58.936528 eth0 In ifindex 17 92:26:b8:9e:f2:1c ethertype IPv4 (0x0800), length 104: (tos 0x0, ttl 63, id 30481, offset 0, flags [DF], proto ICMP (1), length 84)
10.17.0.9 > 172.17.0.1: ICMP echo request, id 37989, seq 0, length 64
06:50:58.936574 net1 Out ifindex 2 62:d8:71:90:7b:86 ethertype IPv4 (0x0800), length 104: (tos 0x0, ttl 62, id 30481, offset 0, flags [DF], proto ICMP (1), length 84)
172.17.0.11 > 172.17.0.1: ICMP echo request, id 39449, seq 0, length 64
06:50:58.936613 net1 In ifindex 2 02:42:39:79:7f:08 ethertype IPv4 (0x0800), length 104: (tos 0x0, ttl 64, id 26701, offset 0, flags [none], proto ICMP (1), length 84)
172.17.0.1 > 172.17.0.11: ICMP echo reply, id 39449, seq 0, length 64
06:50:58.936621 eth0 Out ifindex 17 36:7c:6b:c7:82:6b ethertype IPv4 (0x0800), length 104: (tos 0x0, ttl 63, id 26701, offset 0, flags [none], proto ICMP (1), length 84)
172.17.0.1 > 10.17.0.9: ICMP echo reply, id 37989, seq 0, length 64
OVN Logical Router 上自动创建路由策略:
$ kubectl ko nbctl lr-policy-list ovn-cluster
Routing Policies
31000 ip4.dst == 10.16.0.0/16 allow
31000 ip4.dst == 10.17.0.0/16 allow
31000 ip4.dst == 100.64.0.0/16 allow
30000 ip4.dst == 172.18.0.2 reroute 100.64.0.4
30000 ip4.dst == 172.18.0.3 reroute 100.64.0.3
30000 ip4.dst == 172.18.0.4 reroute 100.64.0.2
29100 ip4.src == $VEG.8ca38ae7da18.ipv4 reroute 10.16.0.12
29100 ip4.src == $VEG.8ca38ae7da18_ip4 reroute 10.16.0.12
29000 ip4.src == $ovn.default.kube.ovn.control.plane_ip4 reroute 100.64.0.3
29000 ip4.src == $ovn.default.kube.ovn.worker2_ip4 reroute 100.64.0.2
29000 ip4.src == $ovn.default.kube.ovn.worker_ip4 reroute 100.64.0.4
29000 ip4.src == $subnet1.kube.ovn.control.plane_ip4 reroute 100.64.0.3
29000 ip4.src == $subnet1.kube.ovn.worker2_ip4 reroute 100.64.0.2
29000 ip4.src == $subnet1.kube.ovn.worker_ip4 reroute 100.64.0.4
- VPC Egress Gateway 用于转发 .spec.policies 指定 Pod 流量的 Logical Router 策略。
- VPC Egress Gateway 用于转发 .spec.selectors 指定 Pod 流量的 Logical Router 策略。
若需启用负载均衡,修改 .spec.replicas,示例如下:
$ kubectl scale veg gateway1 --replicas=2
vpcegressgateway.kubeovn.io/gateway1 scaled
$ kubectl get veg gateway1
NAME VPC REPLICAS BFD ENABLED EXTERNAL SUBNET PHASE READY AGE
gateway1 ovn-cluster 2 false macvlan Completed true 39m
$ kubectl get pod -l ovn.kubernetes.io/vpc-egress-gateway=gateway1 -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
gateway1-b9f8b4448-76lhm 1/1 Running 0 40m 10.16.0.12 kube-ovn-worker <none> <none>
gateway1-b9f8b4448-zd4dl 1/1 Running 0 64s 10.16.0.13 kube-ovn-worker2 <none> <none>
$ kubectl ko nbctl lr-policy-list ovn-cluster
Routing Policies
31000 ip4.dst == 10.16.0.0/16 allow
31000 ip4.dst == 10.17.0.0/16 allow
31000 ip4.dst == 100.64.0.0/16 allow
30000 ip4.dst == 172.18.0.2 reroute 100.64.0.4
30000 ip4.dst == 172.18.0.3 reroute 100.64.0.3
30000 ip4.dst == 172.18.0.4 reroute 100.64.0.2
29100 ip4.src == $VEG.8ca38ae7da18.ipv4 reroute 10.16.0.12, 10.16.0.13
29100 ip4.src == $VEG.8ca38ae7da18_ip4 reroute 10.16.0.12, 10.16.0.13
29000 ip4.src == $ovn.default.kube.ovn.control.plane_ip4 reroute 100.64.0.3
29000 ip4.src == $ovn.default.kube.ovn.worker2_ip4 reroute 100.64.0.2
29000 ip4.src == $ovn.default.kube.ovn.worker_ip4 reroute 100.64.0.4
29000 ip4.src == $subnet1.kube.ovn.control.plane_ip4 reroute 100.64.0.3
29000 ip4.src == $subnet1.kube.ovn.worker2_ip4 reroute 100.64.0.2
29000 ip4.src == $subnet1.kube.ovn.worker_ip4 reroute 100.64.0.4
启用基于 BFD 的高可用
基于 BFD 的高可用依赖 VPC BFD LRP 功能,
因此需要修改 VPC 资源以启用 BFD Port。
下面示例为默认 VPC 启用 BFD Port:
apiVersion: kubeovn.io/v1
kind: Vpc
metadata:
name: ovn-cluster
spec:
bfdPort:
enabled: true
ip: 10.255.255.255
nodeSelector:
matchLabels:
kubernetes.io/os: linux
- 是否启用 BFD Port。
- BFD Port 的 IP 地址,必须是有效且不与任何其他 IP/子网冲突的地址。
- 用于选择运行 BFD Port 的节点的节点选择器,BFD Port 绑定选中节点的 OVN HA Chassis Group,以 Active/Backup 模式工作。
启用 BFD Port 后,OVN Logical Router 上会自动创建专用的 BFD LRP:
$ kubectl ko nbctl show ovn-cluster
router 0c1d1e8f-4c86-4d96-88b2-c4171c7ff824 (ovn-cluster)
port bfd@ovn-cluster
mac: "8e:51:4b:16:3c:90"
networks: ["10.255.255.255"]
port ovn-cluster-join
mac: "d2:21:17:71:77:70"
networks: ["100.64.0.1/16"]
port ovn-cluster-ovn-default
mac: "d6:a3:f5:31:cd:89"
networks: ["10.16.0.1/16"]
port ovn-cluster-subnet1
mac: "4a:09:aa:96:bb:f5"
networks: ["10.17.0.1/16"]
- OVN Logical Router 上创建的 BFD Port。
之后,在 VPC Egress Gateway 中将 .spec.bfd.enabled 设置为 true,示例如下:
apiVersion: kubeovn.io/v1
kind: VpcEgressGateway
metadata:
name: gateway2
namespace: default
spec:
vpc: ovn-cluster
replicas: 2
internalSubnet: ovn-default
externalSubnet: macvlan1
bfd:
enabled: true
minRX: 100
minTX: 100
multiplier: 5
policies:
- snat: true
ipBlocks:
- 10.18.0.0/16
- Egress Gateway 所属的 VPC。
- Egress Gateway 实例连接的内部子网。
- Egress Gateway 实例连接的外部子网。
- 是否启用 Egress Gateway 的 BFD。
- BFD 的最小接收间隔,单位毫秒。
- BFD 的最小发送间隔,单位毫秒。
- BFD 的乘数,决定丢包多少次后判定故障。
本例创建了一个名为 gateway2 的 VPC Egress Gateway,副本数为 2,启用了 BFD。
当某个实例故障时,BFD 会话断开,OVN 快速检测到故障并停止转发流量到故障实例,
所有流量将转发到健康实例,确保访问外部网络不中断。
故障切换时间取决于 BFD 配置,
计算公式为:故障切换时间 = (multiplier + 1) * max(minRX, minTX)。
本例约需 500~600 毫秒检测故障并重路由流量。
NOTE
故障切换期间,现有连接可能会中断,需要重新建立连接,但新连接不会受影响,可正常建立。
查看 VPC Egress Gateway 信息:
$ kubectl get veg gateway2 -o wide
NAME VPC REPLICAS BFD ENABLED EXTERNAL SUBNET PHASE READY INTERNAL IPS EXTERNAL IPS WORKING NODES AGE
gateway2 vpc1 2 true macvlan Completed true ["10.16.0.102","10.16.0.103"] ["172.17.0.13","172.17.0.14"] ["kube-ovn-worker","kube-ovn-worker2"] 58s
$ kubectl get pod -l ovn.kubernetes.io/vpc-egress-gateway=gateway2 -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
gateway2-fcc6b8b87-8lgvx 1/1 Running 0 2m18s 10.16.0.103 kube-ovn-worker2 <none> <none>
gateway2-fcc6b8b87-wmww6 1/1 Running 0 2m18s 10.16.0.102 kube-ovn-worker <none> <none>
$ kubectl ko nbctl lr-policy-list ovn-cluster
Routing Policies
31000 ip4.dst == 10.16.0.0/16 allow
31000 ip4.dst == 10.17.0.0/16 allow
31000 ip4.dst == 100.64.0.0/16 allow
30000 ip4.dst == 172.18.0.2 reroute 100.64.0.4
30000 ip4.dst == 172.18.0.3 reroute 100.64.0.3
30000 ip4.dst == 172.18.0.4 reroute 100.64.0.2
29100 ip4.src == $VEG.8ca38ae7da18.ipv4 reroute 10.16.0.102, 10.16.0.103 bfd
29100 ip4.src == $VEG.8ca38ae7da18_ip4 reroute 10.16.0.102, 10.16.0.103 bfd
29090 ip4.src == $VEG.8ca38ae7da18.ipv4 drop
29090 ip4.src == $VEG.8ca38ae7da18_ip4 drop
29000 ip4.src == $ovn.default.kube.ovn.control.plane_ip4 reroute 100.64.0.3
29000 ip4.src == $ovn.default.kube.ovn.worker2_ip4 reroute 100.64.0.2
29000 ip4.src == $ovn.default.kube.ovn.worker_ip4 reroute 100.64.0.4
29000 ip4.src == $subnet1.kube.ovn.control.plane_ip4 reroute 100.64.0.3
29000 ip4.src == $subnet1.kube.ovn.worker2_ip4 reroute 100.64.0.2
29000 ip4.src == $subnet1.kube.ovn.worker_ip4 reroute 100.64.0.4
$ kubectl ko nbctl list bfd
_uuid : 223ede10-9169-4c7d-9524-a546e24bfab5
detect_mult : 5
dst_ip : "10.16.0.102"
external_ids : {af="4", vendor=kube-ovn, vpc-egress-gateway="default/gateway2"}
logical_port : "bfd@ovn-cluster"
min_rx : 100
min_tx : 100
options : {}
status : up
_uuid : b050c75e-2462-470b-b89c-7bd38889b758
detect_mult : 5
dst_ip : "10.16.0.103"
external_ids : {af="4", vendor=kube-ovn, vpc-egress-gateway="default/gateway2"}
logical_port : "bfd@ovn-cluster"
min_rx : 100
min_tx : 100
options : {}
status : up
查看 BFD 连接状态:
$ kubectl exec gateway2-fcc6b8b87-8lgvx -c bfdd -- bfdd-control status
There are 1 sessions:
Session 1
id=1 local=10.16.0.103 (p) remote=10.255.255.255 state=Up
$ kubectl exec gateway2-fcc6b8b87-wmww6 -c bfdd -- bfdd-control status
There are 1 sessions:
Session 1
id=1 local=10.16.0.102 (p) remote=10.255.255.255 state=Up
NOTE
如果所有网关实例均不可用,应用了 VPC Egress Gateway 的出口流量将被丢弃。
配置参数
VPC BFD Port
VPC Egress Gateway
注意事项
任何导致 Egress Gateway 实例被删除/重建的操作,可能触发出口流量的临时网络故障切换,包括但不限于:
- 修改副本数;
- 修改部分配置,如内外部 IP、节点选择器、BFD 配置等;
- 升级/降级 Kube-OVN(当 .spec.image 未指定时);
- 手动删除 Egress Gateway 实例 Pod。
相关资源