1. VxLAN
1.1 VxLan简介
首先VxLAN跟IPIP模式一样,都是属于Overlay网络的一种。
其次,VxlAN和IPIP在封装上有两个区别:
- VxLAN的每个数据包开销都比IPIP大,因为header更大。
- VxLAN的实现不使用BGP,而IPIP使用Calico node节点之间的BGP。
最后,VXLAN(Virtual Extensible LAN)是一种基于 UDP 的隧道技术,通过在原始数据包外封装一个新的 VXLAN 头部和 UDP 头部,实现跨子网的通信。
1.2 配置方式
1.2.1 方式一:通过环境变量配置
在 calico.yaml
中,通过环境变量 CALICO_IPV4POOL_VXLAN
配置 VXLAN 模式(默认处于关闭状态),如下:
……省略部分内容
- name: CALICO_IPV4POOL_VXLAN # 注意:VXLAN模式和IPIP模式只能二选一
value: "Always"
……省略部分内容
1.2.1.1 该方式特点
- 作用范围
该配置会在 Calico 安装时自动创建一个默认的 IPPool,并启用 VXLAN 模式。- 灵活性
配置较为简单,但灵活性较低,无法对 IPPool 进行更细粒度的控制(如 CIDR、NAT 等)。- 适用场景
适合快速部署和简单场景,不需要对 IPPool 进行定制化配置。
1.2.1.2 生成的默认IPPool
这个ippools资源,如果在部署calico.yaml的时候没有显示的指定,则Calico 会根据环境变量生成一个默认的ippools,类似于以下内容:
1.2.2 方式二:通过IPPool配置
直接通过 Kubernetes 资源(ippools
)配置 VXLAN 模式,如下:
apiVersion: projectcalico.org/v3
kind: IPPool
metadata:
name: ippool-vxlan-1
spec:
cidr: 192.168.0.0/16
vxlanMode: Always # 或者CrossSubnet
natOutgoing: true
1.2.2.1 该方式特点
- 作用范围
直接定义 IPPool 资源,可以精确控制 IP 池的 CIDR、VXLAN 模式、NAT 等参数。- 灵活性
配置灵活,支持更复杂的场景(如多 IPPool、跨子网配置等)。- 适用场景
适合需要定制化 IPPool 的场景,或者对网络有更高要求的部署。
1.2.3 两种方式的主要区别
特性 | 环境变量配置 | IPPool资源配置 |
配置方式 | 通过环境变量 | 通过Kubernetes资源 |
灵活性 | 较低,仅支持简单配置 | 较高,支持定制化配置 |
CIDR控制 | 仅使用默认CIDR(如192.168.0.0/16) | 可自定义CIDR |
NAT配置 | 默认启用NAT | 可自定义是否启用NAT |
适用场景 | 快速部署、简单使用 | 复杂场景、有定制化需求 |
1.2.4 两种方式的注意事项
- 冲突问题
如果同时使用环境变量和 IPPool 资源配置,可能会导致冲突。建议只选择一种方式。- 修改配置后
修改 IPPool 配置后,需要重启 Calico 节点或重新应用配置。- 默认 IPPool
如果使用环境变量配置,Calico 会自动创建一个默认 IPPool。如果需要修改默认 IPPool,建议直接使用 IPPool 资源配置。
1.2.5 使用vxlan模式的注意事项(必看)
calico.yaml默认是ipip模式,使用vxlan模式的话,由于本身并不涉及到BGP相关的东西,所以要有两个和bgp相关的配置需要移除:
- 移除和bird相关的配置(calico.yaml)。
calico_backend: "bird" 改成 calico_backend: "vxlan"
- 移除bird的存活探测和就绪探测(calico.yaml)。
这里还有一点需要注意,部署calico的时候(跟单独的模式无关),如果服务器上有多张网卡在用,并且都在使用,而且第一张网卡不叫eth0或者要使用的网卡不是第一张,那么我们需要手动添加一项配置来指定网卡,否则calico-node会启动失败:
- IP_AUTODETECTION_METHOD=first-found:这个是默认值,自动选择第一个网卡。
- IP_AUTODETECTION_METHOD=interface=eth.*:这个配置就是选择指定网卡,可以指定具体的网卡名称如eth0,也支持正则表达式。注意:不同版本的calico配置方式不同,一定要根据对应版本的calico官方文档来改!比如我3.23的配置方式如下:
执行命令:calico会从指定正则表达式中,选一个匹配的接口来选择IP地址。kubectl set env daemonset/calico-node -n kube-system IP_AUTODETECTION_METHOD=interface=eth.*
执行完后,ds calico-node中就会生成这个配置:
还有很多其他的配置方法,比如排除不需要的接口,详细的还是看官方文档。
1.3 kind部署k8s集群
1.3.1 调整相关配置
root@superadmin-virtual-machine:~/wcni-kind/LabasCode/calico/03-calico-vxlan# grep image 1-setup-env.sh
#cat <<EOF | kind create cluster --name=calico-vxlan --image=kindest/node:v1.27.3 --config=-
cat <<EOF | kind create cluster --name=calico-vxlan --image=registry.cn-beijing.aliyuncs.com/sanhua-k8s/kindest_node:v1.27.3 --config=-
root@superadmin-virtual-machine:~/wcni-kind/LabasCode/calico/03-calico-vxlan# grep 'image:' calico.yaml
#image: 192.168.2.100:5000/calico/cni:v3.23.2
image: registry.cn-beijing.aliyuncs.com/sanhua-k8s/calico_cni:v3.23.2
#image: 192.168.2.100:5000/calico/cni:v3.23.2
image: registry.cn-beijing.aliyuncs.com/sanhua-k8s/calico_cni:v3.23.2
#image: 192.168.2.100:5000/calico/node:v3.23.2
image: registry.cn-beijing.aliyuncs.com/sanhua-k8s/calico_node:v3.23.2
#image: 192.168.2.100:5000/calico/node:v3.23.2
image: registry.cn-beijing.aliyuncs.com/sanhua-k8s/calico_node:v3.23.2
#image: 192.168.2.100:5000/calico/kube-controllers:v3.23.2
image: registry.cn-beijing.aliyuncs.com/sanhua-k8s/calico_kube-controllers:v3.23.2
root@superadmin-virtual-machine:~/wcni-kind/LabasCode/calico/03-calico-vxlan# grep 'image:' cni.yaml
#- image: 192.168.2.100:5000/nettool
- image: image: registry.cn-beijing.aliyuncs.com/sanhua-k8s/nettool
1.3.2 部署集群
root@superadmin-virtual-machine:~/wcni-kind/LabasCode/calico/03-calico-vxlan# ./1-setup-env.sh
1.3.3 部署测试容器
root@superadmin-virtual-machine:~/wcni-kind/LabasCode/calico/03-calico-vxlan# cat cni.yaml
apiVersion: apps/v1
kind: DaemonSet
#kind: Deployment
metadata:
labels:
app: wluo
name: wluo
spec:
#replicas: 2
selector:
matchLabels:
app: wluo
template:
metadata:
labels:
app: wluo
spec:
containers:
#- image: 192.168.2.100:5000/nettool
- image: registry.cn-beijing.aliyuncs.com/sanhua-k8s/nettool
name: nettoolbox
env:
- name: NETTOOL_NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
securityContext:
privileged: true
---
apiVersion: v1
kind: Service
metadata:
name: wluo
spec:
type: NodePort
selector:
app: wluo
ports:
- name: wluo
port: 80
targetPort: 80
nodePort: 32000
root@superadmin-virtual-machine:~/wcni-kind/LabasCode/calico/03-calico-vxlan# kubectl apply -f cni.yaml
daemonset.apps/wluo created
service/wluo created
root@superadmin-virtual-machine:~/wcni-kind/LabasCode/calico/03-calico-vxlan# kubectl get po -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
wluo-5sj9m 1/1 Running 0 77s 10.244.110.69 calico-vxlan-control-plane <none> <none>
wluo-ndt6n 1/1 Running 0 77s 10.244.177.65 calico-vxlan-worker <none> <none>
wluo-rvmll 1/1 Running 0 77s 10.244.70.193 calico-vxlan-worker2 <none> <none>
1.4 VxLAN同节点通信(vxlanMode: Always)
这个和ipip的同节点通信一样的,直接走路由,所以这里不抓包看了。
1.5 VxLAN跨节点通信(同子网,vxlanMode: Always)
1.5.1 简单分析
控制节点查看路由
root@superadmin-virtual-machine:~# docker exec -it calico-vxlan-control-plane bash
root@calico-vxlan-control-plane:/# route -n
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 172.18.0.1 0.0.0.0 UG 0 0 0 eth0
10.244.70.192 10.244.70.192 255.255.255.192 UG 0 0 0 vxlan.calico
10.244.110.64 0.0.0.0 255.255.255.192 U 0 0 0 *
10.244.110.65 0.0.0.0 255.255.255.255 UH 0 0 0 cali350dec42006
10.244.110.66 0.0.0.0 255.255.255.255 UH 0 0 0 calidcbc1979adc
10.244.110.67 0.0.0.0 255.255.255.255 UH 0 0 0 calicaeaa1160e2
10.244.110.68 0.0.0.0 255.255.255.255 UH 0 0 0 cali9dfe92e663d
10.244.110.69 0.0.0.0 255.255.255.255 UH 0 0 0 calib5fd138717b
10.244.177.64 10.244.177.64 255.255.255.192 UG 0 0 0 vxlan.calico
172.18.0.0 0.0.0.0 255.255.0.0 U 0 0 0 eth0
calico-vxlan-control-plane上的pod,请求calico-vxlan-worker上的pod时,控制节点的pod还是先路由匹配,目的pod地址为:10.244.177.65。那么对应的路由就是:
10.244.177.64 10.244.177.64 255.255.255.192 UG 0 0 0 vxlan.calico
问题来了:去10.244.177.65需要通过10.244.177.64这个网关,那10.244.177.64是哪个节点?或者换句话说,这个10.244.177.64,在哪个节点上。对应mac地址又是什么?
查mac地址
root@superadmin-virtual-machine:~# docker exec -it calico-vxlan-control-plane arp -n|grep 10.244.177.64
10.244.177.64 ether 66:aa:ae:c0:d0:7a CM vxlan.calico
通过上述结果,得知了10.244.177.64对应的mac是66:aa:ae:c0:d0:7a,设备名称就叫vxlan.calico。
那么报文从控制节点发往10.244.177.64时,dst_mac=66:aa:ae:c0:d0:7a(每一跳都会变)。
那10.244.177.64到底是哪个节点呢?或者说,我控制节点,是怎么获取到这个信息(就上面的arp信息)的?答:FDB表。
FDB(Forwarding Database Table)表,即转发数据库表,也被称为 MAC 地址表。主要用于记录 (自动学习)MAC 地址与网络设备端口之间的映射关系。
当收到一个数据帧时,会根据帧中的目的 MAC 地址查找 FDB 表,从而确定将该数据帧从哪个接口转发到那个目的地址。 但是注意:mac和目的地址的关系并不是绑定的,有可能是属于目标节点的其他网卡,不过这个会自动识别,只要对应mac的网卡在目标节点存在就行。
查fdb表里面的信息。
root@superadmin-virtual-machine:~# docker exec -it calico-vxlan-control-plane bridge fdb show|grep '66:aa:ae:c0:d0:7a'
66:aa:ae:c0:d0:7a dev vxlan.calico dst 172.18.0.4 self permanent
这条信息表示:当要将数据帧发送到 Dst_MAC=66:aa:ae:c0:d0:7a
时,数据帧会通过 VXLAN 隧道封装(vxlan.calico
)并发送到outer_ip=172.18.0.4(该地址并不一定是7a这个mac的网卡)
这个目标 IP 地址所在的节点。
那么可以验证一下这个信息对不对:
那么有了上述fdb表中的信息后,我们就能明确的知道,10.244.177.64这个网关地址,对应的就是172.18.0.4这个node节点。
然后通过路由查询,找到去172.18.0.4的路由,以及对应的出接口,一般都从eth0接口出去了。
1.5.2 抓包分析
这里我在源端和目的端同时抓的包
1.5.2.1 源pod eth0
从上图可以看到,此时的报文结构:
Src_ip:10.244.110.69 Src_mac:fa:cf:92:6e:e7:24
Dst_ip:10.244.177.65 Dst_mac:ee:ee:ee:ee:ee:ee
为什么Dst_mac是全e呢?因为它的第一跳一定是发给对应的calic*接口的
1.5.2.2 源宿主机 cali*
从上图可以看到,pod的请求到宿主机cali*接口的时候,报文结构基本没有变化。
接下来,会先查询路由,来确定下一跳该怎么走:
可以看到,要去177.64的话,必须要通过vxlan.calico。
1.5.2.3 源宿主机 vxlan.calico
可以看到,请求到vxlan.calico时,src ip和dst ip不变,但是对应的src mac和dst mac都变了:
Src_Mac:66:40:d4:7a:17:07(源宿主机vxlan.calico)
Dst_Mac:66:aa:ae:c0:d0:7a(目标宿主机vxlan.calico)
这里上面讲过了,系统会从宿主机arp表中获取到177.64对应的mac地址,然后用这个mac在当前宿主机的FDB表中,获得与之相关的目的节点IP,来确定怎么到达对端。如下:
从上述结果中可以看到,下一跳为172.18.0.4,具体路由为:
那么下一跳就为eth0。
1.5.2.4 源宿主机eth0
可以看到,报文从vxlan.cali到源宿主机的eth0后,发生了很明显的变化:
inner_ip_src:10.244.110.69(源pod) inner_mac_src:源宿主机vxlan.calico 66:40:d4:7a:17:07
inner_ip_dst:10.244.177.65(目的pod) inner_mac_dst:目标宿主机vxlan.calico 66:aa:ae:c0:d0:7a
outer_ip_src:172.18.0.2 outer_mac_src:02:42:ac:12:00:02(源宿主机eth0)
outer_ip_dst:172.18.0.4 outer_mac_dst:02:42:ac:12:00:04(目标宿主机eth0)
具体的vxlan报文结构为:
https://support.huawei.com/enterprise/zh/doc/EDOC1100218020/f95c6e68
一文带你搞懂VXLAN报文格式,网工先收藏!-阿里云开发者社区
1.5.2.5 目标宿主机eth0
通过上图的对比,可以看到报文到eth0后,是没有变化的
1.5.2.6 目标宿主机vxlan.calio
到了vxlan.calio后,解封装去掉了外层ip和外层mac。
但内层ip和内层的mac都没变。
1.5.2.7 目标宿主机calic*
到了cali*后,mac地址又发生了变化:
- Src_Mac:目标宿主机cali*的mac
- Dst_Mac:目标pod eth0接口的mac
1.5.2.8 目标pod eth0
从cali到pod eth0的报文,就不会再发生改变了。
1.5.3 vxlan跨节点通信总结
报文会在以下几个节点发生改变:
- 源宿主机cali*接口到源宿主机vxlan.calico接口
○ 源mac和目的mac会发生改变,分别为双方的vxlan.calico接口的mac。- 源宿主机vxlan.calico接口到源宿主机eth0接口
○ 发给eth0的报文会进行VXLAN封装。
○ 在原有内层IP(双方pod)和内层mac(双方vxlan.calico)的基础上,额外封装外层IP(双方宿主机)和外层mac(双方宿主机)- 目标宿主机eth0接口到目标宿主机vxlna.calico接口
○ 解封装报文,只保留内层IP和内层MAC,内层报文内容不变。
2. VxLAN CrossSubnet
这里的CrossSubnet和IPIP的一样,也是跨子网才封装。
同子网的节点间的请求,直接路由转发了。
不同子网节点间的请求,需要通过vxlan封装。
2.1 实验需求
本次实验一共需要4个节点:
2.2 安装k8s集群
2.2.1 清理环境
root@superadmin-virtual-machine:~# kind get clusters
calico-vxlan
root@superadmin-virtual-machine:~# kind delete clusters calico-vxlan
Deleted nodes: ["calico-vxlan-worker" "calico-vxlan-worker2" "calico-vxlan-control-plane"]
Deleted clusters: ["calico-vxlan"]
2.2.2 调整配置文件
root@superadmin-virtual-machine:~# grep 'image' /root//wcni-kind/LabasCode/calico/04-calico-vxlan-crosssubnet/1-setup-env.sh
#cat <<EOF | kind create cluster --name=calico-vxlan-crosssubnet --image=kindest/node:v1.27.3 --config=-
cat <<EOF | kind create cluster --name=calico-vxlan-crosssubnet --image=registry.cn-beijing.aliyuncs.com/sanhua-k8s/kindest_node:v1.27.3 --config=-
root@superadmin-virtual-machine:~# grep 'image' /root//wcni-kind/LabasCode/calico/04-calico-vxlan-crosssubnet/2-setup-clab.sh
#image: 192.168.2.100:5000/vyos/vyos:1.4.9
image: registry.cn-beijing.aliyuncs.com/sanhua-k8s/vyos:1.4.9
#image: 192.168.2.100:5000/nettool
image: registry.cn-beijing.aliyuncs.com/sanhua-k8s/nettool
#image: 192.168.2.100:5000/nettool
image: registry.cn-beijing.aliyuncs.com/sanhua-k8s/nettool
#image: 192.168.2.100:5000/nettool
image: registry.cn-beijing.aliyuncs.com/sanhua-k8s/nettool
#image: 192.168.2.100:5000/nettool
image: registry.cn-beijing.aliyuncs.com/sanhua-k8s/nettool
root@superadmin-virtual-machine:~# grep 'image' /root//wcni-kind/LabasCode/calico/04-calico-vxlan-crosssubnet/calico.yaml
#image: 192.168.2.100:5000/calico/cni:v3.23.2
image: registry.cn-beijing.aliyuncs.com/sanhua-k8s/calico_cni:v3.23.2
imagePullPolicy: IfNotPresent
#image: 192.168.2.100:5000/calico/cni:v3.23.2
image: registry.cn-beijing.aliyuncs.com/sanhua-k8s/calico_cni:v3.23.2
imagePullPolicy: IfNotPresent
#image: 192.168.2.100:5000/calico/node:v3.23.2
image: registry.cn-beijing.aliyuncs.com/sanhua-k8s/calico_node:v3.23.2
imagePullPolicy: IfNotPresent
#image: 192.168.2.100:5000/calico/node:v3.23.2
image: registry.cn-beijing.aliyuncs.com/sanhua-k8s/calico_node:v3.23.2
imagePullPolicy: IfNotPresent
#image: 192.168.2.100:5000/calico/kube-controllers:v3.23.2
image: registry.cn-beijing.aliyuncs.com/sanhua-k8s/calico_kube-controllers:v3.23.2
imagePullPolicy: IfNotPresent
root@superadmin-virtual-machine:~# grep 'image' /root//wcni-kind/LabasCode/calico/04-calico-vxlan-crosssubnet/cni.yaml
#- image: 192.168.2.100:5000/nettool
- image: image: registry.cn-beijing.aliyuncs.com/sanhua-k8s/nettool
2.2.3 安装k8s集群
root@superadmin-virtual-machine:~/wcni-kind/LabasCode/calico/04-calico-vxlan-crosssubnet# ./1-setup-env.sh
2.2.4 启用containerlab环境
2.2.4.1 清理之前遗留的环境
root@superadmin-virtual-machine:~/wcni-kind/LabasCode/calico/02-calico-ipip-crosssubnet# containerlab destroy --topo clab.yaml
INFO[0000] Parsing & checking topology file: clab.yaml
INFO[0000] Parsing & checking topology file: clab.yaml
INFO[0000] Destroying lab: calico-ipip-crosssubnet
INFO[0000] Removed container: clab-calico-ipip-crosssubnet-server3
INFO[0000] Removed container: clab-calico-ipip-crosssubnet-server2
INFO[0000] Removed container: clab-calico-ipip-crosssubnet-server1
INFO[0000] Removed container: clab-calico-ipip-crosssubnet-server4
INFO[0000] Removed container: clab-calico-ipip-crosssubnet-gw0
INFO[0000] Removing containerlab host entries from /etc/hosts file
INFO[0000] Removing ssh config for containerlab nodes
2.2.4.2 启用新环境
root@superadmin-virtual-machine:~/wcni-kind/LabasCode/calico/04-calico-vxlan-crosssubnet# ./2-setup-clab.sh
安装成功后,k8s节点就有IP地址了
2.2.5 创建测试pod
root@superadmin-virtual-machine:~/wcni-kind/LabasCode/calico/04-calico-vxlan-crosssubnet# cat cni.yaml
apiVersion: apps/v1
kind: DaemonSet
#kind: Deployment
metadata:
labels:
app: wluo
name: wluo
spec:
#replicas: 2
selector:
matchLabels:
app: wluo
template:
metadata:
labels:
app: wluo
spec:
containers:
#- image: 192.168.2.100:5000/nettool
- image: registry.cn-beijing.aliyuncs.com/sanhua-k8s/nettool
name: nettoolbox
env:
- name: NETTOOL_NODE_NAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
securityContext:
privileged: true
---
apiVersion: v1
kind: Service
metadata:
name: wluo
spec:
type: NodePort
selector:
app: wluo
ports:
- name: wluo
port: 80
targetPort: 80
nodePort: 32000
root@superadmin-virtual-machine:~/wcni-kind/LabasCode/calico/04-calico-vxlan-crosssubnet# kubectl apply -f cni.yaml
2.2.3 同子网请求
2.2.3.1 终端一:control-plane节点抓包
root@calico-vxlan-crosssubnet-control-plane:~# tcpdump -pne -i net0
2.2.3.2 终端二:control-plane节点发起icmp请求
2.2.3.3 终端一:control-plane节点分析报文
通过上述报文,很明显的能看出,到net0(实际工作中,一般是eth0)的报文,是没有vxlan封装的。
src_mac:源pod所在宿主机的net0网卡。
dst_mac:?可以通过arp -n 确认,如下:
然后再通过路由查询,就发给目标宿主机了。
2.2.4 跨子网请求
2.2.4.1 终端一:control-plane节点抓包
2.2.4.2 终端二:control-plane节点发起icmp请求
2.2.4.3 终端一:control-plane节点分析报文
如上图,可以看到很明显的vxlan封装特征。