前面几节内容学习了容器网络接口CNI及CNI规范的一些基础知识,从本节开始学习开源虚拟网络方案Calico。 Calico是一个可用于容器、虚拟机和本机主机工作负载的开源网络和网络安全解决方案。 Calico支持广泛的平台,包括Kubernetes, OpenShift, Docker EE, OpenStack和裸金属服务。 Calico同时实现并提供了calico网络CNI插件,因此可以将Calico用作容器网络插件。 本文将回到原始时代,从零开始纯手工以二进制形式部署Calico,这样能先从整体上理解Calico的各个组件。

准备工作

三台CentOS 7.9主机如下:

1192.168.100.101    node1
2192.168.100.102    node2
3192.168.100.103    node3

关闭各个主机的防火墙,或者参考[https://docs.projectcalico.org/getting-started/bare-metal/requirements]的Network requirements网络需求部分开放相关端口及协议。 需要确保各个主机节点上已经安装了下面的依赖,这些依赖可以在文档https://github.com/projectcalico/node#linux-dependencies中找到相关的说明:

1yum install -y conntrack net-tools iptables procps kmod iptables-utils ipset

calico集群需要在每个主机节点上部署一个calico-node,在部署calico-node之前还要确保已经部署了calico的datastore服务。 calico支持etcdkubernetes两种datastore,使用etcd存储时calico将直接连接etcd集群,使用kubernetes存储时,calico将连接k8s的api server。

既然回到了"原始时代",因此选择etcd作为calico的datastore。 这里省略etcd集群的部署过程。在本实验环境中,3个节点etcd也是部署在这三台主机上。

 1etcdctl version
 2etcdctl version: 3.4.16
 3API version: 3.4
 4
 5export ETCDCTL_API=3
 6etcdctl   \
 7   --cert /etc/etcd/ssl/client.crt \
 8   --key /etc/etcd/ssl/client.key \
 9   --cacert /etc/etcd/ssl/ca.crt \
10   --endpoints https://192.168.100.101:2379,https://192.168.100.102:2379,https://192.168.100.103:2379 \
11member list
129e954b89cd9805e, started, node2, https://192.168.100.102:2380, https://192.168.100.102:2379, false
131e6d17829ae1a0b0, started, node1, https://192.168.100.101:2380, https://192.168.100.101:2379, false
14cff40374b94e6f44, started, node3, https://192.168.100.103:2380, https://192.168.100.103:2379, false

接下来需要准备calico-node的二进制文件。 因为当前版本的calico推荐以容器方式部署,且更多的场景是将calico部署在kubernetes集群内并作为kubernetes pod网络插件, 所以calico官方目前只提供了calico-node的容器镜像,而并未提供二进制文件。

需要从容器镜像中将二进制文件拷贝出来。 当前nerdctl(v0.10)还未提供类似docker cp命令,这里单独找一台安装了docker的机器,使用docker cp命令拷贝calico-node的二进制文件:

 1docker pull calico/node:v3.19.1
 2docker create --name container calico/node:v3.19.1
 3docker cp container:/bin/calico-node calico-node
 4docker cp container:/usr/bin/bird bird
 5docker cp container:/usr/bin/bird bird6
 6docker cp container:/etc/calico/confd ./
 7docker rm container
 8chmod +x calico-node
 9chmod +x bird
10chmod +x bird6

将拷贝出来的calico-node, bird, bird6文件分发到各个主机节点的/usr/local/bin目录下。 将拷贝出来的confd模板配置文件夹confd分发到各个主机节点的/etc/calico下。

将etcd的客户端证书分发到各个节点的/etc/calico/ssl目录下:

1ls /etc/calico/ssl
2ca.crt  client.crt  client.key

安装calicoctl

calicoctl是calico的命令行工具用来管理calico。 建议在所有节点上安装calicoctl:

1cd /usr/local/bin
2curl -o calicoctl -O -L  "https://github.com/projectcalico/calicoctl/releases/download/v3.19.1/calicoctl" 
3chmod +x calicoctl

创建/etc/calico/calicoctl.cfg配置文件,配置calicoctl连接calico的etcd datastore:

 1cat > /etc/calico/calicoctl.cfg << EOF
 2apiVersion: projectcalico.org/v3
 3kind: CalicoAPIConfig
 4metadata:
 5spec:
 6  etcdEndpoints: https://192.168.100.101:2379,https://192.168.100.102:2379,https://192.168.100.103:2379
 7  etcdKeyFile: /etc/calico/ssl/client.key
 8  etcdCertFile: /etc/calico/ssl/client.crt
 9  etcdCACertFile: /etc/calico/ssl/ca.crt
10EOF

检查配置是否成功:

1calicoctl get nodes

当前还没有部署calico node,上面的命令没有错误输出说明calicoctl配置成功。

部署calico node

每个主机节点都是一个calico node,每个calico node上需要部署以下3个组件:

  • felix: 每个calico node上都要运行一个felix组件,该组件负责配置路由及ACL等信息,并确保该主机上endpoints(接入到calico网络中的网卡称为endpoint)的连通性。felix从calico-node二进制文件启动。
  • bird: 每个calico node上都要运行一个bird,bird从felix获取路由,并分发给网络上其他的BGP peers。简单理解bird就是BGP守护进程负责将路由信息分发给其他节点。bird从bird二进制文件启动。
  • confd: 每个calico node上都要运行一个confd,confd监听calico datastore中的配置变化并更新bird的配置文件。confd从calico-node二进制文件启动。

部署calico felix

创建calico的环境变量配置文件/etc/calico/calico.env:

 1echo $HOSTNAME > /var/lib/calico/nodename
 2cat > /etc/calico/calico.env << EOF
 3NODENAME=$HOSTNAME
 4DATASTORE_TYPE=etcdv3
 5ETCD_ENDPOINTS=https://192.168.100.101:2379,https://192.168.100.102:2379,https://192.168.100.103:2379
 6ETCD_CA_CERT_FILE=/etc/calico/ssl/ca.crt
 7ETCD_CERT_FILE=/etc/calico/ssl/client.crt
 8ETCD_KEY_FILE=/etc/calico/ssl/client.key
 9CALICO_IPV4POOL_IPIP=Always
10CALICO_IPV4POOL_CIDR=10.89.0.0/16
11CALICO_NETWORKING_BACKEND=bird
12CLUSTER_TYPE=
13IP=autodetect
14IP_AUTODETECTION_METHOD=interface=eth0
15EOF

注意上面的环境变量NODENAME是根据各个节点设置对应的节点名称,CALICO_IPV4POOL_CIDR配置的是calico IPAM的IP地址池,容器的IP地址将从此池中分配。CALICO_IPV4POOL_IPIP配置为Always表示开启是IPIP。 具体可配置的环境变量可以参考https://docs.projectcalico.org/reference/node/configuration

部署calico-felix服务:

 1cat > /etc/systemd/system/calico-felix.service << EOF
 2[Unit]
 3Description=Calico Felix agent
 4After=syslog.target network.target
 5
 6[Service]
 7User=root
 8EnvironmentFile=/etc/calico/calico.env
 9ExecStartPre=/usr/bin/mkdir -p /var/run/calico
10ExecStart=/usr/local/bin/calico-node -felix
11KillMode=process
12Restart=on-failure
13LimitNOFILE=32000
14
15[Install]
16WantedBy=multi-user.target
17EOF
18
19systemctl enable calico-felix
20systemctl start calico-felix
21systemctl status calico-felix

按照上面的步骤在3个节点上都完成felix的按照和启动后,还需要为每个felix节点在calico datastore中创建Node资源:

 1calicoctl apply -f - <<EOF
 2- apiVersion: projectcalico.org/v3
 3  kind: Node
 4  metadata:
 5    name: node1
 6  spec:
 7    bgp:
 8      ipv4Address: 192.168.100.101/24
 9
10---
11- apiVersion: projectcalico.org/v3
12  kind: Node
13  metadata:
14    name: node2
15  spec:
16    bgp:
17      ipv4Address: 192.168.100.102/24
18
19---
20- apiVersion: projectcalico.org/v3
21  kind: Node
22  metadata:
23    name: node3
24  spec:
25    bgp:
26      ipv4Address: 192.168.100.103/24
27
28EOF

创建默认的IPPool:

 1calicoctl apply -f - <<EOF
 2- apiVersion: projectcalico.org/v3
 3  kind: IPPool
 4  metadata:
 5    name: default-ipv4-ippool
 6  spec:
 7    cidr: 10.89.0.0/16
 8    ipipMode: Always
 9    vxlanMode: Never
10    natOutgoing: true
11    disabled: false
12    nodeSelector: all()
13EOF

部署calico confd

部署calico-confd服务:

 1cat > /etc/systemd/system/calico-confd.service << EOF
 2[Unit]
 3Description=Calico Confd
 4After=syslog.target network.target
 5
 6[Service]
 7User=root
 8EnvironmentFile=/etc/calico/calico.env
 9ExecStartPre=/usr/bin/mkdir -p /var/run/calico
10ExecStart=/usr/local/bin/calico-node -confd
11KillMode=process
12Restart=on-failure
13LimitNOFILE=32000
14
15[Install]
16WantedBy=multi-user.target
17EOF
18
19systemctl enable calico-confd
20systemctl start calico-confd
21systemctl status calico-confd

部署后可重启calico-felix和calico-confd,看状态是否正常,是否有错误日志。

部署bird

此时如果使用calicoctl node status命令查看calico node的状态,会打印None of the BGP backend processes (BIRD or GoBGP) are running.的信息。

1calicoctl node status
2Calico process is running.
3
4None of the BGP backend processes (BIRD or GoBGP) are running.

接下来部署bird服务。

 1cat > /etc/systemd/system/bird.service << EOF
 2[Unit]
 3Description=BIRD internet routing daemon
 4After=syslog.target network.target
 5
 6[Service]
 7User=root
 8EnvironmentFile=/etc/calico/calico.env
 9ExecStartPre=/usr/bin/mkdir -p /var/run/calico
10ExecStart=/usr/local/bin/bird -R -s /var/run/calico/bird.ctl -d -c /etc/calico/confd/config/bird.cfg
11KillMode=process
12Restart=on-failure
13LimitNOFILE=32000
14
15[Install]
16WantedBy=multi-user.target
17EOF
18
19systemctl enable bird
20systemctl start bird
21systemctl status bird

通过查看bird的状态和日志,calico已经建立了node to node的mesh。 而此时运行calicoctl node status命令查看calico node的状态,仍然会打印None of the BGP backend processes (BIRD or GoBGP) are running.的信息,可是bird已经启动了啊,查看当前calicoctl的源码发现https://github.com/projectcalico/calicoctl/blob/4cfe90c44c1f085004d5213e29c01d96a2f85090/calicoctl/commands/node/status.go#L80,第80行就是calicoctl打印node status的逻辑if psContains([]string{"bird"}, processes) || psContains([]string{"bird6"}, processes)通过检查bird进程是否存在再决定是否打印,而第115行func psContains(proc []string, procList []*process.Process) bool函数的实现中判断是进程启动命令里需要等于bird,这里bird的systemd中是/usr/local/bin/bird,因此psContains返回false。

于是,在一个节点上停掉bird的systemd服务,直接bird -R -s /var/run/calico/bird.ctl -d -c /etc/calico/confd/config/bird.cfg手动启动后,再使用calicoctl node status查看节点状态,即可以正常打印IPv4 BGP status信息了。

 1systemctl stop bird
 2bird -R -s /var/run/calico/bird.ctl -d -c /etc/calico/confd/config/bird.cfg &
 3calicoctl node status
 4
 5Calico process is running.
 6
 7IPv4 BGP status
 8+----------------+-------------------+-------+----------+-------------+
 9|  PEER ADDRESS  |     PEER TYPE     | STATE |  SINCE   |    INFO     |
10+----------------+-------------------+-------+----------+-------------+
11| 192.168.100.102 | node-to-node mesh | up    | 21:47:43 | Established |
12| 192.168.100.103 | node-to-node mesh | up    | 21:47:43 | Established |
13+----------------+-------------------+-------+----------+-------------+
14
15INFO: BIRDv6 process: 'bird6' is not running.

通过上面的分析,说明是由于calicoctl内部代码实现的问题,导致calicoctl node status不支持以二进制部署并以systemd启动的bird。 因为这只是一个状态查看的命令,实际上我们已经完成了calico node的集群部署。

总结

本节我们完成了calico node集群的纯手工二进制部署,下节内容将在各个节点上部署calico cni插件, 并在两个主机节点上各启动一个连接到calico网络的containerd容器,测试两个容器网络是否是打通的。

参考