配置 Egress Gateway
关于 Egress Gateway
Egress Gateway 用于通过一组静态地址控制 Pod 的外部网络访问,并具有以下特性:
- 通过 ECMP 实现 Active-Active 高可用性,支持水平扩展吞吐量
- 通过 BFD 实现快速故障切换(<1s)
- 支持 IPv6 和双栈
- 通过 NamespaceSelector 和 PodSelector 实现精细的路由控制
- 通过 NodeSelector 灵活调度 Egress Gateway
同时,Egress Gateway 也有以下限制:
- 使用 macvlan 实现底层网络连接,需要底层网络支持 Underlay
- 在多实例 Gateway 模式下,需要配置多个 Egress IP
- 目前仅支持 SNAT,不支持 EIP 和 DNAT
- 目前不支持记录源地址转换关系
实现细节
每个 Egress Gateway 由多个 Pod 组成,每个 Pod 配有多个网络接口。
每个 Pod 有两个网络接口:一个加入虚拟网络,用于 VPC 内部通信;
另一个通过 Macvlan 连接到底层物理网络,用于外部网络通信。
虚拟网络流量最终通过 Egress Gateway 实例中的 NAT 访问外部网络。
每个 Egress Gateway 实例都会在 OVN 路由表中注册其地址。
当 VPC 内的 Pod 需要访问外部网络时,
OVN 会使用源地址哈希将流量转发到多个 Egress Gateway 实例地址,
从而实现负载均衡。随着 Egress Gateway 实例数量增加,
吞吐量也可以水平扩展。
OVN 使用 BFD 协议探测多个 Egress Gateway 实例。
当 Egress Gateway 实例发生故障时,OVN 会将对应路由标记为不可用,
从而实现快速故障检测和恢复。
前提条件
在使用 Egress Gateway 之前,必须在集群上安装 Alauda Container Platform Multi-Network(之前称为 Alauda Container Platform Networking for Multus)。
要安装 Alauda Container Platform Multi-Network(之前称为 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
TIP
您可以使用任意 CNI 插件创建 Network Attachment Definition,以访问相应的网络。
除了上述示例外,还可以使用 Kube-OVN underlay subnet 代替 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 controller 中定义的默认资源请求和限制。
- 用于调度 VPC Egress Gateway 实例的节点选择器。
- 用于调度 VPC Egress Gateway 实例的容忍度。
- 用于选择通过 VPC Egress Gateway 访问外部网络的 Pod 的命名空间选择器和 Pod 选择器。
- VPC Egress Gateway 的策略,包括要应用的 SNAT 和 subnets/ipBlocks。
- 是否为该策略启用 SNAT。
- 该策略适用的子网。
- 该策略适用的 IP 块。
上述资源会在 default 命名空间下创建名为 gateway1 的 VPC Egress Gateway,
以下 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 使用的 Logical Router Policy,用于转发来自 .spec.policies 指定 Pod 的流量。
- VPC Egress Gateway 使用的 Logical Router Policy,用于转发来自 .spec.selectors 指定 Pod 的流量。
如果需要启用负载均衡,请按照以下示例修改 .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。
以下示例展示了如何为 default 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 地址,且不能与任何其他 IP 或子网冲突。
- 用于选择以 Active-Backup 模式运行 BFD Port 的节点的节点选择器。
启用 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 会话将变为 down,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
如果所有 Gateway 实例都处于 down 状态,则应用了 VPC Egress
Gateway 的出站流量将被丢弃。
配置参数
VPC BFD 端口
VPC Egress Gateway
注意
任何会导致 Egress Gateway 实例被删除/重新创建的操作,都可能触发 Egress 流量的临时网络故障转移,包括但不限于:
- 更改副本数;
- 更改某些配置,例如内部/外部 IP、节点选择器、BFD 配置等;
- 升级/降级 Kube-OVN(如果未指定 .spec.image);
- 手动删除 Egress Gateway 实例 Pod。
其他资源