Calico-基本安装、IPIP同节点通信和Proxy_ARP实践 Day01
1. Calico基础介绍
1.1 简介
Calico是一个网络和安全解决方案,它使Kubernetes工作负载和非Kubernetes/遗留工作负载能够无缝安全地通信。
1.2 网络模式
1.2.1 BGP 模式
- 作用:使用 BGP(边界网关协议)动态路由协议,节点之间通过 BGP 交换路由信息。
- 优势:适合大规模部署,提供灵活的网络拓扑。
- 场景:本地数据中心、多租户环境。
1.2.2 IPIP 模式
- 作用:使用 IP-in-IP 封装协议,将一个 IP 数据包封装在另一个 IP 数据包中。
- 优势:适用于跨子网通信,但会增加额外的封装和解封开销。
- 场景:跨可用区云环境。
1.2.3 VXLAN 模式
- 作用:使用 VXLAN(虚拟可扩展局域网)封装协议(MAC in UDP),通过 UDP 封装数据包。
- 优势:适用于需要跨越多个数据中心或需要二层互联的场景。
- 场景:传统网络环境。
1.3 节点要求
- X86-64、arm64、ppc64le或s390x处理器。
- 2CPU、2GB内存。
- 10GB可用磁盘空间。
- RedHat Enterprise Linux 7. x+、CentOS 7. x+、Ubuntu 16.04+、or Debian 9.x+。
- 确保Calico能够管理主机上的cali和tunl接口。
2. Calico环境准备
2.0 服务器多网卡部署calico的注意事项
如果服务器上有多张网卡在用,并且都在使用,而且第一张网卡不叫eth0或者要使用的网卡不是第一张,那么我们需要手动添加一项配置来指定网卡,否则calico-node会启动失败:
- IP_AUTODETECTION_METHOD=first-found:这个是默认值,自动选择第一个网卡。
- IP_AUTODETECTION_METHOD=interface=eth.*:这个配置就是选择指定网卡,可以指定具体的网卡名称如eth0,也支持正则表达式。注意:不同版本的calico配置方式不同,一定要根据对应版本的calico官方文档来改!比如我3.23的配置方式如下:
执行命令(calico-node已经部署完了):calico会从指定正则表达式中,选一个匹配的接口来选择IP地址。
kubectl set env daemonset/calico-node -n kube-system IP_AUTODETECTION_METHOD=interface=eth.*
执行完后,ds calico-node中就会生成这个配置(calico-node部署前,可以手动添加该配置):
还有很多其他的配置方法,比如排除不需要的接口,详细的还是看官方文档。
2.1 安装集群
直接用kind快速部署
# clone相关文件后,主要改image 配置,还有需要多加一个node节点
root@localhost:~/wcni-kind/LabasCode/calico/01-calico-ipip# cat 1-setup-env.sh
……省略部分输出
nodes:
- role: control-plane
- role: worker
- role: worker # 多加一个
……省略部分输出
root@localhost:~/wcni-kind/LabasCode/calico/01-calico-ipip# grep 'image' 1-setup-env.sh
#cat <<EOF | kind create cluster --name=calico-ipip --image=kindest/node:v1.27.3 -v=9 --config=-
cat <<EOF | kind create cluster --name=calico-ipip --image=registry.cn-beijing.aliyuncs.com/sanhua-k8s/kindest_node:v1.27.3 -v=9 --config=-
root@localhost:~/wcni-kind/LabasCode/calico/01-calico-ipip# grep 'image:' cni.yaml
- image: registry.cn-beijing.aliyuncs.com/sanhua-k8s/nettool
root@localhost:~/wcni-kind/LabasCode/calico/01-calico-ipip# 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/kube-controllers:v3.23.2
image: registry.cn-beijing.aliyuncs.com/sanhua-k8s/calico_kube-controllers:v3.23.2
# 安装集群
root@localhost:~/wcni-kind/LabasCode/calico/01-calico-ipip# sh 1-setup-env.sh
# 安装完毕
root@localhost:~/wcni-kind/LabasCode/calico/01-calico-ipip# kubectl get no
NAME STATUS ROLES AGE VERSION
calico-ipip-control-plane Ready control-plane 3m40s v1.27.3
calico-ipip-worker Ready <none> 3m17s v1.27.3
calico-ipip-worker2 Ready <none> 3m16s v1.27.3
root@localhost:~/wcni-kind/LabasCode/calico/01-calico-ipip# kubectl get po -A
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system calico-kube-controllers-8658f6b5f5-x7kkf 1/1 Running 0 10m
kube-system calico-node-7tvfs 1/1 Running 0 10m
kube-system calico-node-9lgjg 1/1 Running 0 10m
kube-system coredns-5d78c9869d-bvlzv 1/1 Running 0 11m
kube-system coredns-5d78c9869d-bxrhv 1/1 Running 0 11m
kube-system etcd-calico-ipip-control-plane 1/1 Running 0 11m
kube-system kube-apiserver-calico-ipip-control-plane 1/1 Running 0 11m
kube-system kube-controller-manager-calico-ipip-control-plane 1/1 Running 0 11m
kube-system kube-proxy-66299 1/1 Running 0 11m
kube-system kube-proxy-dzwqd 1/1 Running 0 11m
kube-system kube-scheduler-calico-ipip-control-plane 1/1 Running 0 11m
local-path-storage local-path-provisioner-6bc4bddd6b-qwrv2 1/1 Running 0 11m
2.2 安装calicoctl
注意版本
root@localhost:/home/xiets# wget https://github.com/projectcalico/calico/releases/download/v3.23.2/calicoctl-linux-amd64
root@localhost:/home/xiets# chmod +x calicoctl-linux-amd64
root@localhost:/home/xiets# mv calicoctl-linux-amd64 /usr/local/bin/calicoctl
root@localhost:/home/xiets# calicoctl version
Client Version: v3.23.2
Git commit: a52cb86db
Cluster Version: v3.23.2
Cluster Type: k8s,bgp,kubeadm,kdd
2.3 查看k8s集群中的ip地址池
2.3.1 kubectl查看
root@localhost:/home/xiets# kubectl get ippool -o yaml
apiVersion: v1
items:
- apiVersion: crd.projectcalico.org/v1
kind: IPPool
metadata:
annotations:
projectcalico.org/metadata: '{"uid":"d3ce5646-cbc8-4c8d-b98f-a4f5c7b84335","creationTimestamp":"2025-02-01T08:37:42Z"}'
creationTimestamp: "2025-02-01T08:37:42Z"
generation: 1
name: default-ipv4-ippool
resourceVersion: "669"
uid: c4f5db22-0cbf-4c8a-b2af-66865885ad96
spec:
allowedUses:
- Workload
- Tunnel
blockSize: 26
cidr: 10.244.0.0/16 # 指定Pod IP 池的地址范围。
ipipMode: Always # 无论Pod是否位于同一子网内,所有Pod之间的通信都将通过 IPIP(IP-in-IP)封装进行。
natOutgoing: true
nodeSelector: all() # all(),表示cidr地址池适用于所有node节点,也可以通过正则来选择node。
vxlanMode: Never # 禁用 VXLAN(虚拟可扩展局域网)封装模式,它和ipipMode只能有一个是Always。
kind: List
metadata:
resourceVersion: ""
2.3.2 calicoctl查看
root@localhost:/home/xiets# calicoctl get ippool -owide
NAME CIDR NAT IPIPMODE VXLANMODE DISABLED DISABLEBGPEXPORT SELECTOR
default-ipv4-ippool 10.244.0.0/16 true Always Never false false all()
# json格式显示
root@localhost:/home/xiets# calicoctl get ippool -ojson
{
"kind": "IPPoolList",
"apiVersion": "projectcalico.org/v3",
"metadata": {
"resourceVersion": "5998"
},
"items": [
{
"kind": "IPPool",
"apiVersion": "projectcalico.org/v3",
"metadata": {
"name": "default-ipv4-ippool",
"uid": "c4f5db22-0cbf-4c8a-b2af-66865885ad96",
"resourceVersion": "669",
"creationTimestamp": "2025-02-01T08:37:42Z"
},
"spec": {
"cidr": "10.244.0.0/16",
"vxlanMode": "Never",
"ipipMode": "Always",
"natOutgoing": true,
"blockSize": 26,
"nodeSelector": "all()",
"allowedUses": [
"Workload",
"Tunnel"
]
}
}
]
}
2.4 创建cni
root@localhost:~/wcni-kind/LabasCode/calico/01-calico-ipip# 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:
# 1.Share IPC
#hostIPC: true
# 2.Share host Network
#hostNetwork: true
# 3.Inherit the process ID from the host
#hostPID: true
volumes:
- name: host
hostPath:
path: /
type: Directory
containers:
- image: registry.cn-beijing.aliyuncs.com/sanhua-k8s/nettool
name: nettoolbox
volumeMounts:
- name: host
mountPath: /host
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@localhost:~/wcni-kind/LabasCode/calico/01-calico-ipip# kubectl apply -f cni.yaml
daemonset.apps/wluo created
service/wluo created
root@localhost:~/wcni-kind/LabasCode/calico/01-calico-ipip# kubectl get po
NAME READY STATUS RESTARTS AGE
wluo-2276d 1/1 Running 0 14s
wluo-2fczc 1/1 Running 0 14s
2.4.1 检查nodeport
root@localhost:~/wcni-kind/LabasCode/calico/01-calico-ipip# curl 172.18.0.2:32000
PodName: wluo-2fczc | PodIP: eth0 10.244.79.1/32
root@localhost:~/wcni-kind/LabasCode/calico/01-calico-ipip# curl 172.18.0.2:32000
PodName: wluo-2276d | PodIP: eth0 10.244.51.197/32
2.5 calico安装完毕后,产生的虚拟网卡
- cali*接口:这些接口是 Calico 创建的虚拟接口,用于连接 Pod 和宿主机网络。每个 Pod 都会有一个对应的
cali*
接口。- tunl0接口:在 IPIP 模式下,Calico 会创建一个
tunl0
接口,用于封装和解封装 IP-in-IP 数据包。
3. Ip-in-Ip模式下同节点pod通信
3.1 Enable IPIP配置介绍
root@localhost:~/wcni-kind/LabasCode/calico/01-calico-ipip# vim calico.yaml
…………省略部分内容
# Enable IPIP
- name: CALICO_IPV4POOL_IPIP
value: "Always"
…………省略部分内容
上述配置含义:
CALICO_IPV4POOL_IPIP:是 Calico中的一个环境变量,用于配置 IPv4 地址池的 IP-in-IP 封装模式。
- Always:此设置会强制 Calico 对所有流量使用 IP-in-IP 封装,即使节点在同一个子网中也会进行封装。
除了Always的其他可选值:
CrossSubnet:仅在跨子网的节点之间启用 IP-in-IP 封装,同子网的直接路由了。
Never
:完全禁用 IP-in-IP 封装(仅适用于节点在同一子网中的情况)。
3.2 创建测试pod
root@localhost:~/wcni-kind/LabasCode/calico/01-calico-ipip# kubectl run cc --image=registry.cn-beijing.aliyuncs.com/sanhua-k8s/nettool --restart=Never
pod/cc created
root@localhost:~/wcni-kind/LabasCode/calico/01-calico-ipip# kubectl get po -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
cc 1/1 Running 0 22s 10.244.244.66 calico-ipip-worker2 <none> <none>
wluo-mlbkd 1/1 Running 0 3m29s 10.244.244.65 calico-ipip-worker2 <none> <none>
wluo-txtb4 1/1 Running 0 3m29s 10.244.51.193 calico-ipip-control-plane <none> <none>
wluo-wq6q4 1/1 Running 0 3m29s 10.244.79.5 calico-ipip-worker <none> <none>
可以看到新建的cc和另一个pod都在worker2,接下来可以测试了。
3.3 pod与虚拟网卡关系对应查看
3.3.1 源pod
3.3.2 目的pod
3.3.3 宿主机route -n 查看
这种办法更加简单粗暴
3.3.4 网络拓扑(同节点pod通信过程)
同节点pod通信过程:
- 源pod先查自己的路由表,发现只能通过默认路由走eth0出去,由默认的网关地址转发请求。
- pod eth0请求到达宿主机calixx虚拟网卡。
- 进行宿主机路由匹配,然后到达目的pod对应的calixx虚拟网卡。
- 转发请求到目的pod的eth0网卡。
3.4 同节点pod通信抓包
3.4.1 源pod中抓包
3.4.1.1 tcpdump抓包(终端一)
cc~$tcpdump -pne -i eth0
3.4.1.2 ping目的pod的ip(终端二)
cc~$ping 10.244.244.67 -c 1
3.4.1.3 报文分析(终端一)
10:23:33.015191 32:1e:73:88:fc:7b > ee:ee:ee:ee:ee:ee, ethertype IPv4 (0x0800), length 98: 10.244.244.68 > 10.244.244.67: ICMP echo request, id 85, seq 0, length 64
- 32:1e:73:88:fc:7b:源pod的mac地址。
- 10.244.244.68 > 10.244.244.67:源pod ip地址和目的pod的ip地址。
ee:ee:ee:ee:ee:ee是啥?默认路由中的169.254.1.1是啥?
这里可以查calico官网的FAQ
4. Calico Proxy ARP实践
4.1 介绍
4.1.1 基本概念
Proxy ARP 是 ARP(地址解析协议)的一种扩展应用。ARP 协议主要用于在局域网环境中,将 IP 地址解析为对应的 MAC 地址,以便设备能基于 MAC 地址进行数据帧的封装和转发,实现通信。而 Proxy ARP 则是让一台网络设备(比如路由器、具有特定功能的主机等)代替其他设备来回应 ARP 请求。
4.1.2 为什么要用Proxy ARP
当一个数据包的目的地址不是本机,所以需要查询路由表,当查到路由表中的网关之后,需要获取网关的MAC地址,并将数据包的MAC地址修改成网关地址,然后发送到对应的网卡。
那么问题来了: 在容器里的网关是169.254.1.1,那网关的MAC地址是什么?
正常情况下,内核会对外发送ARP请求,去询问整个二层网络中谁拥有169.254.1.1这个IP地址,拥有这个IP地址的设备会将自己的MAC返回。但是现在的情况是,对于容器和主机,都没有169.254.1.1这个IP,甚至,在主机上的端口calixxx网卡,MAC地址也是一个无用的ee:ee:ee:ee:ee:ee。
所以,如果仅仅是目前的状况,容器和主机网络根本就无法通信!
Calico利用了网卡的proxy_arp功能,让DEV(虚拟网卡)设备就会看起来像一个网关,会响应所有的ARP请求,并将自己的MAC地址告诉客户端。
也就是说,当容器发送ARP请求时,主机DEV设备会告诉容器,我拥有169.254.1.1这个IP,我的MAC地址是XXX,这样,容器就可以顺利的将数据包发出来了,于是网络就通了。
4.2 开启Proxy ARP配置
4.2.1 开启方式
echo 1 > /proc/sys/net/ipv4/conf/虚拟网卡/proxy_arp # 1表示开启
4.2.2 开启后宿主机上的体现
root@calico-ipip-worker2:/proc/sys/net/ipv4/conf# ls -l |grep cali
dr-xr-xr-x 1 root root 0 Feb 6 02:23 cali70648170f23 # 这俩虚拟网卡,随便进一个都可以看到
dr-xr-xr-x 1 root root 0 Feb 6 02:17 calidf868d6c510
root@calico-ipip-worker2:/proc/sys/net/ipv4/conf# cat cali70648170f23/proxy_arp
1
# 除了cali虚拟网卡,其他网卡是不会开启的
root@calico-ipip-worker2:/proc/sys/net/ipv4/conf# cat eth0/proxy_arp
0
root@calico-ipip-worker2:/proc/sys/net/ipv4/conf# cat tunl0/proxy_arp
0
4.3 手动实现Proxy ARP
4.3.1 创建名称空间和虚拟网卡
# 创建一个名为ns1的网络名称空间
root@localhost:~# ip netns add ns1
# 创建虚拟网卡
## ip link add:创建新的网络设备
## veht:创建一个名为veth的虚拟网卡。
## type veth:虚拟网卡的类型是veth。
## peer name c-eth0:指定了与veth配对的另一个虚拟网卡c-eth0。这样它们之间就能传输数据了。
root@localhost:~# ip link add veth type veth peer name c-eth0
4.3.2 查看创建结果
4.3.3 启动虚拟网卡并将c-eth0添加到新建的ns中
# 启动虚拟网卡
root@localhost:~# ip link set veth up
root@localhost:~# ip link set c-eth0 up # 这里启动后,等会儿加入另外的网络名称空间后,还需要再启动一次
# 添加到ns1中
## 这里操作了过后,宿主机上就看不到c-eth0这个虚拟网卡了,因为已经不在当前ns中了。
root@localhost:~# ip link set c-eth0 netns ns1
root@localhost:~# ip a |egrep "c-eth0|veth@"
13: veth@if12: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state LOWERLAYERDOWN group default qlen 1000
# 切换到ns1网络名称空间查看c-eth0
## 编号12的就是刚刚创建的虚拟网卡,但注意它的状态是DOWN的。
root@localhost:~# ip netns exec ns1 bash
root@localhost:~# ip a
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: tunl0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN group default qlen 1000
link/ipip 0.0.0.0 brd 0.0.0.0
12: c-eth0@if13: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000
link/ether de:a4:c4:65:18:17 brd ff:ff:ff:ff:ff:ff link-netnsid 0
# 但是还需要再启动一下这个网卡
root@localhost:~# ip link set c-eth0 up
root@localhost:~# ip a
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: tunl0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN group default qlen 1000
link/ipip 0.0.0.0 brd 0.0.0.0
12: c-eth0@if13: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether de:a4:c4:65:18:17 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet6 fe80::dca4:c4ff:fe65:1817/64 scope link
valid_lft forever preferred_lft forever
# 增加ip地址
root@localhost:~# ip add add 1.1.1.2/24 dev c-eth0
root@localhost:~# ip a
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: tunl0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN group default qlen 1000
link/ipip 0.0.0.0 brd 0.0.0.0
12: c-eth0@if13: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
link/ether de:a4:c4:65:18:17 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 1.1.1.2/24 scope global c-eth0
valid_lft forever preferred_lft forever
inet6 fe80::dca4:c4ff:fe65:1817/64 scope link
valid_lft forever preferred_lft forever
4.3.4 在ns1中添加路由
注意:此时依然在ns1这个NET NS中。
这里为了避免出错,直接通过ip netns exec ns1来操作
root@localhost:~# ip r
1.1.1.0/24 dev c-eth0 proto kernel scope link src 1.1.1.2
root@localhost:~# ip netns exec ns1 ip route add 169.254.1.1 dev c-eth0 scope link
# 添加默认路由(注意默认路由一定要最后加)
root@localhost:~# ip netns exec ns1 ip route add default via 169.254.1.1 dev c-eth0
root@localhost:~# ip netns exec ns1 ip route
default via 169.254.1.1 dev c-eth0
1.1.1.0/24 dev c-eth0 proto kernel scope link src 1.1.1.2
169.254.1.1 dev c-eth0 scope link
# 最后再宿主机上添加一条回城路由,否则到达宿主机的包没办法回去
root@localhost:~# ip route add 1.1.1.0/24 dev veth scope link
4.3.4.1 路由含义
- 169.254.1.1 dev c-eth0 scope link:当请求的目的地址是169.254.1.1的,都通过c-eth0设备出去,scope link表示该路由的作用范围仅适用于能与本地(本机)网络直连的设备(本地链路)。换句话说:系统认为
169.254.1.1
是与c-eth0
所在的本地链路直接相连的地址,不需要通过其他网关或路由器进行转发,数据包可以直接通过c-eth0
发送到目标设备。- 1.1.1.0/24 dev c-eth0 proto kernel scope link src 1.1.1.2
- 1.1.1.0/24:目标网络地址段。
- 通过c-eth0出去。
- proto kernel:表示该路由为内核自动生成。
- scope link:本地链路,说明1.1.1.0/24和c-eth0是直连,不需要通过其他网关或路由转发。
- src 1.1.1.2:表示源地址,也就是当前ns1的ip。- default via 169.254.1.1 dev c-eth0:其余匹配不到的路由请求,都通过c-eth0发给169.254.1.1这个网关,由169.254.1.1网关进一步转发数据包。
4.3.4.2 为什么要加两条包含169.254.1.1的路由
上图表面看,只需要加一条默认路由即可,但实际不可以,必须现加第三条路由,需要先告诉网关地址怎么走,然后才能添加默认路由。
简单来说:
- 默认路由说匹配不上的路由都通过c-eth0发给169.254.1.1。
- 但是怎么到达169.254.1.1呢?它不管。
- 于是第三条路由的作用就体现出来了,表明了c-eth0和169的地址能直达,直接请求就完了。而且该路由必须在添加默认路由之前添加。
4.3.5 开启新增veth的proxy_arp功能
4.3.5.1 退出ns1 NET_NS
root@localhost:~# exit
exit
4.3.5.2 开启proxy_arp功能
root@localhost:~# cd /proc/sys/net/ipv4/conf/veth/
root@localhost:/proc/sys/net/ipv4/conf/veth# cat proxy_arp # 默认0,没有开启
0
# 开启proxy_arp
root@localhost:/proc/sys/net/ipv4/conf/veth# echo 1 > proxy_arp
root@localhost:/proc/sys/net/ipv4/conf/veth# cat proxy_arp
1
4.3.5.3 检查配置结果
能返回mac,说明c-eth0和veth就是关联上了。
4.3.6 让ns1出外网
root@localhost:/proc/sys/net/ipv4/conf/veth# iptables -t nat -A POSTROUTING -s 1.1.1.0/24 -j MASQUERADE