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

准备工作

三台CentOS 7.9主机如下:

1
2
3
192.168.100.101    node1
192.168.100.102    node2
192.168.100.103    node3

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

1
yum 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也是部署在这三台主机上。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
etcdctl version
etcdctl version: 3.4.16
API version: 3.4

export ETCDCTL_API=3
etcdctl   \
   --cert /etc/etcd/ssl/client.crt \
   --key /etc/etcd/ssl/client.key \
   --cacert /etc/etcd/ssl/ca.crt \
   --endpoints https://192.168.100.101:2379,https://192.168.100.102:2379,https://192.168.100.103:2379 \
member list
9e954b89cd9805e, started, node2, https://192.168.100.102:2380, https://192.168.100.102:2379, false
1e6d17829ae1a0b0, started, node1, https://192.168.100.101:2380, https://192.168.100.101:2379, false
cff40374b94e6f44, 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的二进制文件:

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

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

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

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

安装calicoctl

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

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

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

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

检查配置是否成功:

1
calicoctl 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:

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

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

部署calico-felix服务:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
cat > /etc/systemd/system/calico-felix.service << EOF
[Unit]
Description=Calico Felix agent
After=syslog.target network.target

[Service]
User=root
EnvironmentFile=/etc/calico/calico.env
ExecStartPre=/usr/bin/mkdir -p /var/run/calico
ExecStart=/usr/local/bin/calico-node -felix
KillMode=process
Restart=on-failure
LimitNOFILE=32000

[Install]
WantedBy=multi-user.target
EOF

systemctl enable calico-felix
systemctl start calico-felix
systemctl status calico-felix

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

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

---
- apiVersion: projectcalico.org/v3
  kind: Node
  metadata:
    name: node2
  spec:
    bgp:
      ipv4Address: 192.168.100.102/24

---
- apiVersion: projectcalico.org/v3
  kind: Node
  metadata:
    name: node3
  spec:
    bgp:
      ipv4Address: 192.168.100.103/24

EOF

创建默认的IPPool:

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

部署calico confd

部署calico-confd服务:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
cat > /etc/systemd/system/calico-confd.service << EOF
[Unit]
Description=Calico Confd
After=syslog.target network.target

[Service]
User=root
EnvironmentFile=/etc/calico/calico.env
ExecStartPre=/usr/bin/mkdir -p /var/run/calico
ExecStart=/usr/local/bin/calico-node -confd
KillMode=process
Restart=on-failure
LimitNOFILE=32000

[Install]
WantedBy=multi-user.target
EOF

systemctl enable calico-confd
systemctl start calico-confd
systemctl status calico-confd

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

部署bird

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

1
2
3
4
calicoctl node status
Calico process is running.

None of the BGP backend processes (BIRD or GoBGP) are running.

接下来部署bird服务。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
cat > /etc/systemd/system/bird.service << EOF
[Unit]
Description=BIRD internet routing daemon
After=syslog.target network.target

[Service]
User=root
EnvironmentFile=/etc/calico/calico.env
ExecStartPre=/usr/bin/mkdir -p /var/run/calico
ExecStart=/usr/local/bin/bird -R -s /var/run/calico/bird.ctl -d -c /etc/calico/confd/config/bird.cfg
KillMode=process
Restart=on-failure
LimitNOFILE=32000

[Install]
WantedBy=multi-user.target
EOF

systemctl enable bird
systemctl start bird
systemctl 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信息了。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
systemctl stop bird
bird -R -s /var/run/calico/bird.ctl -d -c /etc/calico/confd/config/bird.cfg &
calicoctl node status

Calico process is running.

IPv4 BGP status
+----------------+-------------------+-------+----------+-------------+
|  PEER ADDRESS  |     PEER TYPE     | STATE |  SINCE   |    INFO     |
+----------------+-------------------+-------+----------+-------------+
| 192.168.100.102 | node-to-node mesh | up    | 21:47:43 | Established |
| 192.168.100.103 | node-to-node mesh | up    | 21:47:43 | Established |
+----------------+-------------------+-------+----------+-------------+

INFO: BIRDv6 process: 'bird6' is not running.

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

总结

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

参考