calico 学习笔记
Calico 是针对容器、虚机、物理服务器的网络解决方案
- 提供 CNI plugin 与 k8s mesos 集成
- 提供 Libnetwork plugin 与 docker 集成
- 以 neutron plugin 形式与 openstack 集成
- 支持 BGP、IPIP 两种方案
Flannel 方案中,master node 上的 flanneld 进程需要始终检测 k8s api 哪些资源发生了变化并同步 etcd,规模较大时,会造成压力。
calico 与 k8s 集成时,使用 BGP 缓解 k8s api 和 etcd 的压力;并且提供了 network policy,实现多个用户之间的网络隔离等。
k8s 集成时的基本架构
- etcd 作为存储,可以共用 k8s,也可以使用独立的 etcd
- master node:运行 calico-kube-controller pod 用来同步 k8s 数据和 etcd 数据
- worker node:运行 calico-node ,配合 CNI plugin 工作来配置网络,这个容器中运行了四个进程
- runsvdir 作为主进程,会检测并重启以下 4 个进程
- Felix:核心组件
- 管理 calico 相关的网卡,处理arp、检测网卡状态等
- 配置 worker node 上的路由表
- 配置 network policy
- 上报节点状态
- Brid:独立的项目,实现了 BGP 协议,运行两个进程,分别管理 ipv4 和 ipv6
- confd:独立项目,用来从 etcd 同步配置文件,并重启相应的 bird 进程来时配置生效(自动化了 BGP 的配置)
calico 中使用的是 IBGP,部署反射器
安装
Pure BGP
以 IBGP 的方式传递 pod 的路由,可能会与物理网络地址空间冲突。
安装及配置
1 | # 参考 |
下载 calicoctl,curl -x 可以配置代理服务器
curl -O -L https://github.com/projectcalico/calicoctl/releases/download/v3.15.0/calicoctl
添加可执行权限(可以移动到系统变量目录中)
1
2chmod +x calicoctl
mv ./calicoctl /usr/local/bin/添加配置文件,让 calico 连接 k8s 的 etcd
1
2
3
4
5
6
7
8# more /etc/calico/calicoctl.cfg
apiVersion: projectcalico.org/v3
kind: CalicoAPIConfig
metadata:
spec:
datastoreType: "kubernetes"
# 修改路径为当前 k8s 的配置路径
kubeconfig: "/home/x/.kube/config"获取当前 calico 节点
1
2
3
4x@x-vm:~$ calicoctl get nodes -o wide
NAME ASN IPV4 IPV6
worker (64512) 192.168.121.139/24
x-vm (64512) 192.168.121.137/24查看 BGP peer
1
2
3
4
5
6
7
8
9x@x-vm:~$ sudo calicoctl node status
Calico process is running.
IPv4 BGP status
+-----------------+-------------------+-------+----------+-------------+
| PEER ADDRESS | PEER TYPE | STATE | SINCE | INFO
|
+-----------------+-------------------+-------+----------+-------------+
| 192.168.121.139 | node-to-node mesh | up | 03:59:44 | Established |
+-----------------+-------------------+-------+----------+-------------+配置 BGP(默认已经有了,如上显示的 ASN 为 64512)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16# 通过 yaml 配置,vim calico_bgp.yml
apiVersion: projectcalico.org/v3
kind: BGPConfiguration
metadata:
name: default
spec:
logSeverityScreen: Info
nodeToNodeMeshEnabled: true
asNumber: 63400
# 应用配置
calicoctl create -f calico_bgp.yml
# 查看 node,ASN 已经变为配置文件的
x@x-vm:~$ calicoctl get nodes -o wide
NAME ASN IPV4 IPV6
worker (63400) 192.168.121.139/24
x-vm (63400) 192.168.121.137/24基本原理
veth pair 没有连接任何 Linuxbridge,所有数据转发依靠内核路由表查找;所有 pod 网络数据都直接走三层转发。
- pod 内的网络
pod 内只有 169.254.1.1 的默认路由,即访问任何数据都三层转发到 veth 设备。如何转发?通过主机 arp proxy 来实现。1
2
3
4
5
6
7/ # ip route
default via 169.254.1.1 dev eth0
169.254.1.1 dev eth0 scope link
/ # arping 169.254.1.1
ARPING 169.254.1.1 from 11.0.82.3 eth0
Unicast reply from 169.254.1.1 [ee:ee:ee:ee:ee:ee] 0.015ms
Unicast reply from 169.254.1.1 [ee:ee:ee:ee:ee:ee] 0.097ms
pod 的 veth pair 一边连接 pod ,另一边挂在主机的网络命名空间,通过配置主机网络命名空间的 proxy 选项,为 pod 提供三层转发的 arp 应答,这样 pod 的数据包转发到了主机命名空间,挂在主机命名空间的网卡收到数据包后,calico 配置开启了这个网卡的 forwarding 选选项,这样就会查找主机的路由表。1
2
3
4
5
6x@x-vm:~$ cat /proc/sys/net/ipv4/conf/cali99c376db89a/proxy_arp
1
x@x-vm:~$ cat /proc/sys/net/ipv4/neigh/cali99c376db89a/proxy_delay
0
x@x-vm:~$ cat /proc/sys/net/ipv4/conf/cali99c376db89a/forwarding
1
实操
分别在两个node上各创建一个临时的 pod
1 | kubectl run -it --rm --restart=Never test1 --image busybox --overrides='{"apiVersion": "v1","spec": {"nodeSelector": {"kubernetes.io/hostname": "x-vm"}}}' sh |
在主机上 brctl show ,只有一个 docker0 网桥,未挂载任何网卡
在 pod 内发送 arp 请求,在 node 上抓包
查看 master node 的路由表
查看 worker node 的路由表
在 pod 上 traceroute 查看数据转发路径(跨 node )
在 pod 上 traceroute 查看转发路径(同 node)
IPIP
Pure BGP 实现的时候,同一个 node 网卡出来的数据包会有多个源 IP,而且 pod 网络可能会与物理网络冲突。
Calico 使用 IP in IP 封装协议来实现,外层 IP Header 协议号是 4,也是一种隧道技术,但是封装简单,无法实现多租户(GRE 可以)。
实现方式与 flannel 类似,会新增一个 ipip 类型的 netdev 设备,路由表中将远端 node 的路由指向 ipip 设备,进行封装。
同 work node pod 通信,与 pure BGP 一样,直接通过主机明细通信;跨 work node 通信,多了一层 ipip 的封装。
安装配置
修改 calico 配置文件,打开 ipip 选项,可以不用重新部署,直接在之前 pure bgp 的基础之上,导出相关的配置文件进行修改。
将当前的 ipPool 配置导出,修改 ipip 选项为 always
1 | # 查看当前的 ipPool 配置 |
查看网卡详细信息,新增了 tunnel 0,占用了一个 pod 地址,类型为 ipip,remote 为 any,即可以向任意远端发送数据包。
1 | ip -d addr |
实操
分别在两个node上各创建一个临时的 pod
1 | kubectl run -it --rm --restart=Never test1 --image busybox --overrides='{"apiVersion": "v1","spec": {"nodeSelector": {"kubernetes.io/hostname": "x-vm"}}}' sh |
查看 pod 信息,分布在不同的 node 上
pod test1 去 ping test2,在物理网卡上抓包,指定 ip proto 4,可以看到封装的报文
在 tunl0 上抓包,可以看到两个 pod 的原始数据包
pod test1 上 traceroute test2,可以看到路径,第一跳到了当前 node ,第二跳到了远端 node 的 tunl0,最终到达 test2