8

利用 VXLAN 打通多集群网络

 1 year ago
source link: https://cloudnative.to/blog/vxlan-calico-l2-network/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

利用 VXLAN 打通多集群网络

发布于 2023-03-14

字数 4136

阅读时长 19 分钟

点击查看目录

近期由于产品需求需要打通多个 K8s 集群的容器网络,要求在一个集群的容器内,通过 Pod IP直接访问另外一个集群的容器,因此笔者对相关网络技术进行了一番学习。本文将解释整体的组网思路,简单分析数据包的流转过程,最后给出详细的实验步骤。

Kubernetes 中常见的网络插件如 cni、fannel 等的实现中每个宿主机管理一个特定的容器网段,如主机 A 管理容器 223.17.8.0/24 网段,主机 B 管理 223.17.9.0/24 网段,将每个宿主机作为其上运行的容器的网关,并通过 vxlan,IPIP 等隧道技术,将包直接发往宿主机,再通过对应主机的路由到达容器中。因此我们也可以使用这种思路,将多个 K8s 集群的网络打通。

整体架构图

整体架构图

上图以安装 calico 网络插件的集群为例子,展示了如何利用 vxlan 打通多集群网络,我们需要做的是:为所有节点创建 vxlan 虚拟网卡,将其它集群每个节点管理 Pod 网段的路由信息添加到当前集群节点中,并且指定下一跳地址为其它集群节点的 vxlan 虚拟网卡的 ip 地址。为每个节点的 vxlan 网卡配置 FDB 表,FDB 表的内容是除当前节点外,其他节点 vxlan 网卡的 mac 地址以及他们的原生网络的 ip 地址映射。当数据包从容器出来,经过路由表转发到 vxlan 虚拟机网卡中,vxlan 虚拟网卡对数据包进行封包操作,以便在宿主机网络中传输。远端机器接收到数据后进行拆包,取出原始数据包,经过宿主机路由到达目标容器。

VXLAN

VXLAN 是 linux 内核支持的一种隧道技术,通过隧道技术可以在宿主机网络上构建一个二层的虚拟网络。VXLAN 通过将二层的数据帧封装成四层的 UDP 数据包,然后在三层的网络中传输,效果就像在同一个网段中传包一样,在宿主机网络的层面看实际上是跨网段的,但却感知不到。经过 VXLAN 封装后的数据格式如下。

vxlan数据包格式

vxlan数据包格式

笔者以集群 1 中的容器 A 访问集群 2 中容器 D 为例子,介绍 VXLAN 是怎么工作的。容器 A 发往容器 D 的包经过 calico 网卡出现在宿主机内核中。访问的目标地址是 223.18.10.11,因此内层 IP 包的目标地址就是 223.18.10.11,然后会被这条路由规则处理。

223.18.10.0/24 via 241.119.2.100 dev vxlan

这条规则的含义是访问 223.18.10.0/24 网段的 IP 包应该经过本机的 vxlan 网卡出去,下一跳地址是 241.119.2.100,即目标节点的 vxlan 网卡的地址。因为下一跳地址是 241.119.2.100,NodA 会通过 ARP 协议查询到 241.119.2.100 的 mac 地址 c6:c3:41:15:32:c8,然后作为到内层 mac 头目标地址。VXLAN Header 中有一个重要标志 VNI,用于做二层网络隔离用的,创建 vxlan 网卡时会指定该值。然后将数据包加上 UDP 头封装成一个四层的包,UDP 的端口创建 VXLAN 虚拟网卡时会设置。接下来会通过内层 mac 地址查找 vxlan 网卡的 FDB 表,可以查到

$ bridge fdb show dev vxlan | grep c6:c3:41:15:32:c8
c6:c3:41:15:32:c8 dst 10.71.11.46

宿主机将外层 IP 头的目的地址填写为 10.71.11.46,即容器 D 宿主机的 ip 地址。由于 NodeA 和 NodeC 不再同一个网段,因此目标主机的 MAC 地址填写的就是 NodeA 网络中网关节点的 MAC 地址。完成数据包封装后,包经过宿主机网络传输最终到达了 NodeC。NodeC 收到包后发现是一个 VXLAN 协议的包,内核进行拆包以后,取出内部数据帧,然后根据 VNI 的值交给 vxlan 网卡处理,vxlan 网卡拆包以后,取出原始的 ip 包,最后经过宿主机的路由到达目标容器。

笔者在移动云订购了 6 台 2 核 4G 的虚拟机,通过 kubekey 部署了 3 个集群每个集群 2 个节点,集群信息如下。

test-cluster1

  • 集群版本:1.23.10
  • 集群 pod 网段:10.234.64.0/18
  • 集群 service 网段:10.234.0.0/18
  • 网络插件:calico
节点名称 节点ip vxlan-test虚拟网卡IP(规划) vxlan-test虚拟网卡mac(规划) 节点管理pod网段
multicluster-test-001 192.168.0.21 179.17.1.21 c2:6a:9b:e5:39:8d 10.234.111.0/24
multicluster-test-002 192.168.0.31 179.17.1.31 7a:3a:fe:45:a3:76 10.234.118.0/24

test-cluster2

  • 集群版本:1.23.10
  • 集群 pod 网段:10.235.64.0/18
  • 集群 service 网段:10.235.0.0/18
  • 网络插件:calico
节点名称 节点ip vxlan-test虚拟网卡IP(规划) vxlan-test虚拟网卡mac(规划) 节点管理pod网段
multicluster-test-003 192.168.0.16 179.17.1.16 36:f9:67:c0:70:1c 10.235.88.0/24
multicluster-test-004 192.168.0.44 179.17.1.44 12:d1:df:44:3d:aa 10.235.113.0/24

test-cluster3

  • 集群版本:1.23.10
  • 集群pod网段:10.236.64.0/18
  • 集群service网段:10.236.0.0/18
  • 网络插件:calico
节点名称 节点ip vxlan-test虚拟网卡IP(规划) vxlan-test虚拟网卡mac(规划) 节点管理pod网段
multicluster-test-005 192.168.0.15 179.17.1.15 ba:a7:54:aa:bc:ce 10.236.115.0/24
multicluster-test-006 192.168.0.13 179.17.1.13 4e:ac:f5:6f:4e:76 10.236.106.0/24

Calico配置

Calico 为了实现容器访问外部网络,会添加一条 iptable 规则,当源地址是 calico 管理的 IP 且目标地址不是 calico 管理的 IP 地址,会将源 IP 地址 SNAT 为宿主机的 IP 地址,这样包发出去以后,才能够根据宿主机的 IP 地址发回来。Calico 的进程会确保这条规则永远是最高优先级。

Chain cali-nat-outgoing (1 references)
target     prot opt source               destination         
MASQUERADE  all  --  anywhere             anywhere             /* cali:flqWnvo8yq4ULQLa */ match-set cali40masq-ipam-pools src ! match-set cali40all-ipam-pools dst random-fully

为了防止容器访问其他集群容器的包不被 snat,我们需要将其他集群的 pod 网段,加入到 calico 的 IPool 中。具体的配置方法如下:

test-cluster1

cat <<EOF | kubectl apply -f -
apiVersion: crd.projectcalico.org/v1
kind: IPPool
metadata:
  name: svc-test-cluster2
spec:
  cidr: 10.235.0.0/18
  natOutgoing: false
  disabled: true
---
apiVersion: crd.projectcalico.org/v1
kind: IPPool
metadata:
  name: pod-test-cluster2
spec:
  cidr: 10.235.64.0/18
  natOutgoing: false
  disabled: true
---
apiVersion: crd.projectcalico.org/v1
kind: IPPool
metadata:
  name: svc-test-cluster3
spec:
  cidr: 10.236.0.0/18
  natOutgoing: false
  disabled: true
---
apiVersion: crd.projectcalico.org/v1
kind: IPPool
metadata:
  name: pod-test-cluster3
spec:
  cidr: 10.236.64.0/18
  natOutgoing: false
  disabled: true
EOF

test-cluster2

cat <<EOF | kubectl apply -f -
apiVersion: crd.projectcalico.org/v1
kind: IPPool
metadata:
  name: svc-test-cluster1
spec:
  cidr: 10.234.0.0/18
  natOutgoing: false
  disabled: true
---
apiVersion: crd.projectcalico.org/v1
kind: IPPool
metadata:
  name: pod-test-cluster1
spec:
  cidr: 10.234.64.0/18
  natOutgoing: false
  disabled: true
---
apiVersion: crd.projectcalico.org/v1
kind: IPPool
metadata:
  name: svc-test-cluster3
spec:
  cidr: 10.236.0.0/18
  natOutgoing: false
  disabled: true
---
apiVersion: crd.projectcalico.org/v1
kind: IPPool
metadata:
  name: pod-test-cluster3
spec:
  cidr: 10.236.64.0/18
  natOutgoing: false
  disabled: true
EOF

test-cluster3

cat <<EOF | kubectl apply -f -
apiVersion: crd.projectcalico.org/v1
kind: IPPool
metadata:
  name: svc-test-cluster1
spec:
  cidr: 10.234.0.0/18
  natOutgoing: false
  disabled: true
---
apiVersion: crd.projectcalico.org/v1
kind: IPPool
metadata:
  name: pod-test-cluster1
spec:
  cidr: 10.234.64.0/18
  natOutgoing: false
  disabled: true
---
apiVersion: crd.projectcalico.org/v1
kind: IPPool
metadata:
  name: svc-test-cluster2
spec:
  cidr: 10.235.0.0/18
  natOutgoing: false
  disabled: true
---
apiVersion: crd.projectcalico.org/v1
kind: IPPool
metadata:
  name: pod-test-cluster2
spec:
  cidr: 10.235.64.0/18
  natOutgoing: false
  disabled: true
EOF

利用vxlan构建二层虚拟机网络

利用 vxlan 建立隧道使得它们在一个二层虚拟机网络中。首先为每台机器配置 vxlan 网卡,以及 ip 地址,所有节点 vxlan-test 网卡的地址必须是同一个网段。

登陆multicluster-test-001执行

ip link add vxlan-test type vxlan id 10001 dstport 4899 local 192.168.0.21 dev eth0 nolearning
ip addr add 179.17.1.21/24 dev vxlan-test
ip link set dev vxlan-test address c2:6a:9b:e5:39:8d
ip link set vxlan-test  up

登陆multicluster-test-002执行

ip link add vxlan-test type vxlan id 10001 dstport 4899 local 192.168.0.31 dev eth0 nolearning
ip link set dev vxlan-test address 7a:3a:fe:45:a3:76
ip addr add 179.17.1.31/24 dev vxlan-test
ip link set vxlan-test up

登陆multicluster-test-003执行

ip link add vxlan-test type vxlan id 10001 dstport 4899 local 192.168.0.16 dev eth0 nolearning
ip link set dev vxlan-test address 36:f9:67:c0:70:1c
ip addr add 179.17.1.16/24 dev vxlan-test
ip link set vxlan-test up

登陆multicluster-test-004执行

ip link add vxlan-test type vxlan id 10001 dstport 4899 local 192.168.0.44 dev eth0 nolearning
ip link set dev vxlan-test address 12:d1:df:44:3d:aa
ip addr add 179.17.1.44/24 dev vxlan-test
ip link set vxlan-test up

登陆multicluster-test-005执行

ip link add vxlan-test type vxlan id 10001 dstport 4899 local 192.168.0.15 dev eth0 nolearning
ip link set dev vxlan-test address ba:a7:54:aa:bc:ce
ip addr add 179.17.1.15/24 dev vxlan-test
ip link set vxlan-test up

登陆multicluster-test-006执行

ip link add vxlan-test type vxlan id 10001 dstport 4899 local 192.168.0.13 dev eth0 nolearning
ip link set dev vxlan-test address 4e:ac:f5:6f:4e:76 
ip addr add 179.17.1.13/24 dev vxlan-test
ip link set vxlan-test up

接下来配置ARP表和FDB表,使得所有节点的vxlan-test网卡之间可以互通。

登陆multicluster-test-001执行

#配置ARP表
arp -s 179.17.1.31 7a:3a:fe:45:a3:76
arp -s 179.17.1.16 36:f9:67:c0:70:1c
arp -s 179.17.1.44 12:d1:df:44:3d:aa
arp -s 179.17.1.15 ba:a7:54:aa:bc:ce
arp -s 179.17.1.13 4e:ac:f5:6f:4e:76
#配置FDB表
bridge fdb append 7a:3a:fe:45:a3:76 dst 192.168.0.31 dev vxlan-test
bridge fdb append 36:f9:67:c0:70:1c dst 192.168.0.16 dev vxlan-test
bridge fdb append 12:d1:df:44:3d:aa dst 192.168.0.44 dev vxlan-test
bridge fdb append ba:a7:54:aa:bc:ce dst 192.168.0.15 dev vxlan-test
bridge fdb append 4e:ac:f5:6f:4e:76 dst 192.168.0.13 dev vxlan-test

登陆multicluster-test-002执行

#配置ARP表
arp -s 179.17.1.21 c2:6a:9b:e5:39:8d
arp -s 179.17.1.16 36:f9:67:c0:70:1c
arp -s 179.17.1.44 12:d1:df:44:3d:aa
arp -s 179.17.1.15 ba:a7:54:aa:bc:ce
arp -s 179.17.1.13 4e:ac:f5:6f:4e:76
#配置FDB表
bridge fdb append c2:6a:9b:e5:39:8d dst 192.168.0.21 dev vxlan-test
bridge fdb append 36:f9:67:c0:70:1c dst 192.168.0.16 dev vxlan-test
bridge fdb append 12:d1:df:44:3d:aa dst 192.168.0.44 dev vxlan-test
bridge fdb append ba:a7:54:aa:bc:ce dst 192.168.0.15 dev vxlan-test
bridge fdb append 4e:ac:f5:6f:4e:76 dst 192.168.0.13 dev vxlan-test

登陆multicluster-test-003执行

#配置ARP表
arp -s 179.17.1.21 c2:6a:9b:e5:39:8d
arp -s 179.17.1.31 7a:3a:fe:45:a3:76
arp -s 179.17.1.44 12:d1:df:44:3d:aa
arp -s 179.17.1.15 ba:a7:54:aa:bc:ce
arp -s 179.17.1.13 4e:ac:f5:6f:4e:76
#配置FDB表
bridge fdb append c2:6a:9b:e5:39:8d dst 192.168.0.21 dev vxlan-test
bridge fdb append 7a:3a:fe:45:a3:76 dst 192.168.0.31 dev vxlan-test
bridge fdb append 12:d1:df:44:3d:aa dst 192.168.0.44 dev vxlan-test
bridge fdb append ba:a7:54:aa:bc:ce dst 192.168.0.15 dev vxlan-test
bridge fdb append 4e:ac:f5:6f:4e:76 dst 192.168.0.13 dev vxlan-test

登陆multicluster-test-004执行

#配置ARP表
arp -s 179.17.1.21 c2:6a:9b:e5:39:8d
arp -s 179.17.1.31 7a:3a:fe:45:a3:76
arp -s 179.17.1.16 36:f9:67:c0:70:1c
arp -s 179.17.1.15 ba:a7:54:aa:bc:ce
arp -s 179.17.1.13 4e:ac:f5:6f:4e:76
#配置FDB表
bridge fdb append c2:6a:9b:e5:39:8d dst 192.168.0.21 dev vxlan-test
bridge fdb append 7a:3a:fe:45:a3:76 dst 192.168.0.31 dev vxlan-test
bridge fdb append 36:f9:67:c0:70:1c dst 192.168.0.16 dev vxlan-test
bridge fdb append ba:a7:54:aa:bc:ce dst 192.168.0.15 dev vxlan-test
bridge fdb append 4e:ac:f5:6f:4e:76 dst 192.168.0.13 dev vxlan-test

登陆multicluster-test-005执行

#配置ARP表
arp -s 179.17.1.21 c2:6a:9b:e5:39:8d
arp -s 179.17.1.31 7a:3a:fe:45:a3:76
arp -s 179.17.1.16 36:f9:67:c0:70:1c
arp -s 179.17.1.44 12:d1:df:44:3d:aa
arp -s 179.17.1.13 4e:ac:f5:6f:4e:76
#配置FDB表
bridge fdb append c2:6a:9b:e5:39:8d dst 192.168.0.21 dev vxlan-test
bridge fdb append 7a:3a:fe:45:a3:76 dst 192.168.0.31 dev vxlan-test
bridge fdb append 36:f9:67:c0:70:1c dst 192.168.0.16 dev vxlan-test
bridge fdb append 12:d1:df:44:3d:aa dst 192.168.0.44 dev vxlan-test
bridge fdb append 4e:ac:f5:6f:4e:76 dst 192.168.0.13 dev vxlan-test

登陆multicluster-test-006执行

#配置ARP表
arp -s 179.17.1.21 c2:6a:9b:e5:39:8d
arp -s 179.17.1.31 7a:3a:fe:45:a3:76
arp -s 179.17.1.16 36:f9:67:c0:70:1c
arp -s 179.17.1.44 12:d1:df:44:3d:aa
arp -s 179.17.1.15 ba:a7:54:aa:bc:ce
#配置FDB表
bridge fdb append c2:6a:9b:e5:39:8d dst 192.168.0.21 dev vxlan-test
bridge fdb append 7a:3a:fe:45:a3:76 dst 192.168.0.31 dev vxlan-test
bridge fdb append 36:f9:67:c0:70:1c dst 192.168.0.16 dev vxlan-test
bridge fdb append 12:d1:df:44:3d:aa dst 192.168.0.44 dev vxlan-test
bridge fdb append ba:a7:54:aa:bc:ce dst 192.168.0.15 dev vxlan-test

配置路由规则打通多集群pod网络

为每个节点,添加到其他集群节点管理pod网段的路由信息,不同CNI网络插件查询集群节点管理的pod网段的方式不同,Calico可以通过kubectl get blockaffinity -o yaml 命令查询。

在test-cluster1的所有节点执行

route add -net 10.235.88.0/24 gw 179.17.1.16 dev vxlan-test
route add -net 10.235.113.0/24 gw 179.17.1.44 dev vxlan-test
route add -net 10.236.115.0/24 gw 179.17.1.15 dev vxlan-test
route add -net 10.236.106.0/24 gw 179.17.1.13 dev vxlan-test

在test-cluster2的所有节点执行

route add -net 10.234.111.0/24 gw 179.17.1.21 dev vxlan-test
route add -net 10.234.118.0/24 gw 179.17.1.31 dev vxlan-test
route add -net 10.236.115.0/24 gw 179.17.1.15 dev vxlan-test
route add -net 10.236.106.0/24 gw 179.17.1.13 dev vxlan-test

在test-cluster2的所有节点执行

route add -net 10.234.111.0/24 gw 179.17.1.21 dev vxlan-test
route add -net 10.234.118.0/24 gw 179.17.1.31 dev vxlan-test
route add -net 10.235.88.0/24 gw 179.17.1.16 dev vxlan-test
route add -net 10.235.113.0/24 gw 179.17.1.44 dev vxlan-test

网络联通测试

测试从集群test-cluster1中的容器访问test-cluster2中的容器

#查看test-cluster2中容器ip地址
[root@multicluster-test-003 ~]#  kubectl get pod -n test -o wide
NAME                     READY   STATUS    RESTARTS   AGE   IP             NODE                        NOMINATED NODE   READINESS GATES
nginx-65f6dd9c98-8ffmt   1/1     Running   0          21d   10.235.113.5   multicluster-test-004   <none>           <none>
nginx-65f6dd9c98-ljgzn   1/1     Running   0          21d   10.235.88.2    multicluster-test-003   <none>           <none>
#从tesst-cluster1中测试访问10.235.113.5
[root@multicluster-test-001 ~]#  kubectl get pod -n test -owide
NAME                     READY   STATUS    RESTARTS   AGE   IP             NODE                        NOMINATED NODE   READINESS GATES
nginx-6d488455cf-n5gc4   1/1     Running   0          19d   10.234.118.9   multicluster-test-002   <none>           <none>
[root@multicluster-test-001 ~]#  kubectl exec -it nginx-6d488455cf-n5gc4 /bin/sh -n test
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
# curl 10.235.113.5 -v
*   Trying 10.235.113.5:80...
* Connected to 10.235.113.5 (10.235.113.5) port 80 (#0)
> GET / HTTP/1.1
> Host: 10.235.113.5
> User-Agent: curl/7.74.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Server: nginx/1.23.3
< Date: Wed, 22 Feb 2023 14:09:58 GMT
< Content-Type: text/html
< Content-Length: 615
< Last-Modified: Tue, 13 Dec 2022 15:53:53 GMT
< Connection: keep-alive
< ETag: "6398a011-267"
< Accept-Ranges: bytes
.....

从集群test-cluster1中的容器访问test-cluster3中的容器

#查看test-cluster3中容器ip地址
[root@multicluster-test-005 ~]#  kubectl get pod -n test -o wide
NAME                     READY   STATUS    RESTARTS   AGE   IP             NODE                        NOMINATED NODE   READINESS GATES
nginx-65f6dd9c98-bqp88   1/1     Running   0          21d   10.236.115.1   multicluster-test-005   <none>           <none>
nginx-65f6dd9c98-nv9c9   1/1     Running   0          21d   10.236.106.4   multicluster-test-006   <none>           <none>
#从test-cluster的容器中测试访问10.236.115.1
[root@multicluster-test-001 ~]#  kubectl exec -it nginx-6d488455cf-n5gc4 /bin/sh -n test
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
# curl -v 10.236.115.1
*   Trying 10.236.115.1:80...
* Connected to 10.236.115.1 (10.236.115.1) port 80 (#0)
> GET / HTTP/1.1
> Host: 10.236.115.1
> User-Agent: curl/7.74.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Server: nginx/1.23.3
< Date: Wed, 22 Feb 2023 14:14:25 GMT
< Content-Type: text/html
< Content-Length: 615
< Last-Modified: Tue, 13 Dec 2022 15:53:53 GMT
< Connection: keep-alive
< ETag: "6398a011-267"
< Accept-Ranges: bytes
<
...

本文介绍了利用 vxlan 打通多个 K8s 集群网络的思路,并对包对流转做了简单分析。从流转过程可以看出,利用 vxlan 进行组网,并不会比单集群网络性能损失太多,主要体现在 vxlan 隧道和 ipip 隧道的性能差异。最后本文给出了在 6 节点 3 个集群的详细组网步骤,主要涉及创建 vxlan 网卡,配置路由表以及 FDB 表和 ARP 表。实际上如果在一个二层互通的环境下,直接配置路由表就可以完成网络的拉通,只有三层互通的环境才需要用到 vxlan 隧道。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK