calico getting start
简介
Calico是一个纯3层虚拟网络,可以为夸主机的容器/虚拟机等提供网络访问,同时支持IPv4和IPv6。并且可以根据策略提供访问控制能力。
Calico的工作原理主要依靠linux本身提供的ip转发机制,不需要虚拟交换设备或者overlay支持。每一个主机都会将自己的路由信息告知数据中心网络中的其他主机:在小型网络中,直接通过BGP协议交换信息;在大型网络中,通过BGP route reflectors完整路由信息的交换。
Calico为各个不同的云环境提供了不同的插件来使用calico网络,kubernetes和mesos通过CNI插件形式使用calico,docker使用libnetwork插件使用calico网络进行跨主机容器间通信。OpenStack使用Neutron插件使用calico。
入门实践
环境搭建
这里根据官网教程使用vagrant进行docker+calico初始环境搭建。官方文档还提供了手动环境搭建的流程,但是由于一些坑(跨主机容器不能相互访问)放弃了,后来在vagrant上解决了也懒得重新解决一遍了。
搭建命令如下
1 | 需要在主机上有git/vagrant/virtualbox |
上诉能ping通且sudo calico node status
能显示对端的信息说明环境搭建成功。
测试工作
安装官方文档的说明,跨主机的容器通信很简单。只要给docker创建calico的network,然后创建container时设置这个network就可以。具体如下:
1 | 进入calico-01,创建docker网络 |
按照官方文档的说明,这里应该是能直接ping通的,但是这里就出现了上面一开始就说到的坑,并不能ping通,只有通一个主机的两个container直接可以ping。这个坑我首先在自己的azure上手动安装环境时就遇到了,同事帮忙查看了好久,看了iproute, iptables的各种信息,并用tcpdump进行抓包,但是都无法解决,认为可能是azure的问题。于是我又在自己的机器上安装使用virtualbox安装了两个虚拟机,结果还是一样。最后在Mac上直接使用vagrant也无效。这个问题直接浪费了我至少两天时间。最近同事终于发现原来是iptables会把这些包给drop掉。。。而解决这个问题的方法是需要设置calico的profile和policy对象,使得出入流量可以在两个主机之间互通。
1 | 配置net1网络。在docker创建net1时就创建了这个profile,这里我们更改一些属性! |
通过上面的设置,终于可以愉快的跨主机通信了^_^。
查看ip route
可以发现,本地的ip直接走本地的calico创建的veth设备,其他主机的ip通过网卡直接路由到目标主机ip,然后交给目标主机的路由表处理。
1 | default via 10.0.2.2 dev enp0s3 |
ipip 隧道通信
calico支持两个container之间通过ipip隧道通信。在这种模式下,calico会为我们在主机上创建tunl0设备。使用这个设备进行ip封装和拆解。
在打开ipip选项之前,可以先试用ip link
看一下机器上的设备被没有tunl0设备。通过下面步骤打开
- 执行
calicoctl config set ipip on
, 打开设置 - 按照这里的教程打开ipPool的ipip
1 | calicoctl get ipPool -o yaml > pool.yaml |
通过上诉设置,就打开了calico的ipip支持。查看ip route
可以看到和原来的不同:
1 | default via 10.0.2.2 dev enp0s3 |
现在跨主机的通信通过tunl0设备而不是原来的enp0s8设备了。
这样ping容器时进行tcpdump -i tunl0 icmp
可以看到有icmp包。tcpdump -i cali445d36d1804 icmp
可以看到流量。
tcpdump -i enp0s8 | grep ipip
同样可以看到ipip报文,内容如图所示:
1 | 06:35:48.321025 IP 172.17.8.102 > 172.17.8.101: IP 192.168.50.97 > 192.168.84.208: ICMP echo request, id 2304, seq 0, length 64 (ipip-proto-4) |
性能测试
这里简单的测试了calico网络的性能。测试环境是MacBook上创建的两个虚拟机。calico网络使用networkstatic/iperf3进行性能测试,host之间也适用iperf3测试。测试结果如下:
测试项 | 命令 | Bandwidth | Retr |
---|---|---|---|
主机网络 | iperf3 -c 172.17.8.101 -n 10000M -O 3 |
2.25 Gbits/sec | 55672 |
docker host | docker run --rm -it --net host networkstatic/iperf3 -c 172.17.8.101 -n 10000M -O 3 |
2.22 Gbits/sec | 55285 |
calico | docker run --rm -it --net net1 networkstatic/iperf3 -c 192.168.84.209 -n 10000M -O 3 |
2.09 Gbits/sec | 36470 |
calico-ipip | docker run --rm -it --net net1 networkstatic/iperf3 -c 192.168.84.208 -n 10000M -O 3 |
2.15 Gbits/sec | 4516 |
可以看到ipip的性能还要稍微好于非ipip模式,且在ipip模式下TCP的重传较少。总体来看,对比host模式,calico的性能损失不大。
对于上面的测试结果(ipip模式比非ipip模式要好)存在疑惑,因为实际上相比于非ipip模式,ipip模式下需要多经历一次ip包的封装。针对这个疑惑我在社区提了个issue。根据回复中的建议在iperf命令中加入了-M 1440
设置mtu参数,结果显示非ipip模式实际是要比较好的,这比较符合常理。(注:1440是calico的tunl0的mtu值)
自己动手
calico实际上就是对本机容器ip在主机上建立路由,并将这些路由通过bgp协议告知其他主机。通过路由表的信息,达到主机垮主机的容器通信。
下面使用同事给的demo使用linux的ip命令工具来模拟calico的非ipip网络和ipip网络。
- 首先创建两个虚拟机host1(dev enp0s8:172.17.8.101)和host2(dev enp0s8:172.17.8.102),检查是否可以相互ping通
在host1上执行以下命令创建容器网络
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18netns内部ip, 假设本机上所有container的网段在192.168.41.0/24
ip=192.168.41.2
ctn=ctn1
ip netns add $ctn
ip li add dev veth_host type veth peer name veth_sbx
ip link set dev veth_sbx netns $ctn
ip netns exec $ctn ip ad add $ip dev veth_sbx
ip netns exec $ctn ip link set dev veth_sbx up
ip netns exec $ctn ip route add 169.254.1.1 dev veth_sbx
ip netns exec $ctn ip route add default via 169.254.1.1 dev veth_sbx
ip netns exec $ctn ip ad
ip netns exec $ctn ip link set dev veth_sbx up
ip link set dev veth_host up
ip ad show veth_host
ip netns exec $ctn ip neigh add 169.254.1.1 dev veth_sbx lladdr `cat /sys/class/net/veth_host/address`
ip route add $ip dev veth_host
打开ip_forward
echo 1 > /proc/sys/net/ipv4/ip_forward将上诉脚本的ip地址改为192.168.42.2,在host2执行
执行以下步骤添加路由表项以达到跨主机container访问
1
2
3
4# 在host2执行下面命令
ip route add 192.168.41.0/24 via 172.17.8.101 dev enp0s8
# 在host1执行下面命令
ip route add 192.168.42.0/24 via 172.17.8.102 dev enp0s8这样就可以在主机或者容器网络空间内ping跨主机的container ip了
1
2# 在host1 ping host2的container
ip netns exec ctn1 ping 192.168.42.2
通过上诉步骤,就模拟了calico的默认网络。
通过之前的描述,calico实际还支持ipip模式的跨主机通信,原理就是通过tunnel设备对原始ip报文进行封装。只需要对上述脚本做一些修改就可以:
- 在两台主机上将上诉过程中的第4步添加的路由规则去掉
在host1上执行以下脚本
1
2
3
4
5
6
7ipip=192.168.41.3
这个命令会生成一个tunl0设备
modprobe ipip
ip link set tunl0 up
ip a add $ipip brd + dev tunl0
注意这里的路由规则,前面已经提到过,使用tunl0设备!最后一个参数onlink是必须的,具体作用参考[这里](http://lartc.vger.kernel.narkive.com/XgcjFTGM/aw-onlink-option-for-ip-route)
ip r add 192.168.42.0/24 via 172.17.8.102 dev tunl0 proto bird onlink在host2上执行类似上诉步骤
- 重新进入网络空间ping跨主机container ip
可以在对端的enp0s8网卡上使用tcpdump进行截包
1
2
3
4
5
6
7
8tcpdump -vvnneSs 0 -i enp0s8
07:41:03.985150 08:00:27:d5:4b:b1 > 08:00:27:29:bc:de, ethertype IPv4 (0x0800), length 118: (tos 0x0, ttl 63, id 55151, offset 0, flags [DF], proto IPIP (4), length 104)
172.17.8.101 > 172.17.8.103: (tos 0x0, ttl 63, id 16085, offset 0, flags [DF], proto ICMP (1), length 84)
192.168.41.2 > 192.168.43.2: ICMP echo request, id 15973, seq 30, length 64
07:41:03.985243 08:00:27:29:bc:de > 08:00:27:d5:4b:b1, ethertype IPv4 (0x0800), length 118: (tos 0x0, ttl 63, id 32595, offset 0, flags [none], proto IPIP (4), length 104)
172.17.8.103 > 172.17.8.101: (tos 0x0, ttl 63, id 42711, offset 0, flags [none], proto ICMP (1), length 84)
192.168.43.2 > 192.168.41.2: ICMP echo reply, id 15973, seq 30, length 64
ipip相对于非ipip模式会有一些性能的损失,但是好处在于ipip模式下可以进行跨网段的主机间容器通信!将上诉的脚本的两个不同网段的主机上测试可以验证这个结果。