滴普技术荟 - 云原生基座 OpenKube 开放容器实践(五):linux 配置跨主机容器通信

在本章主要完成以下几件事情:
1. 配置 linux 主机路由完成跨主机容器通信
2. 配置 linux ip tunnel 完成跨主机容器通信
3. 配置 linux vxlan 完成跨主机容器通信
主机路由
前面我们已经介绍了几种从容器到主机通信的配置方式,接下来我们通过配置主机路由来打通不同主机的容器,首先在两台主机上分别创建一个新的 NS 然后配置到各自主机的通信,环境如下:
---------------------------------
host1:
host-name:worker2
ip:10.57.4.20
pod-cidr:192.168.10.0/24
---------------------------------
host2:
host-name:worker3
ip:10.57.4.21
pod-cidr:192.168.11.0/24
--------------------------------
首先在 host1 上执行:

然后在 host2 上执行:

如无意外,两台主机和各自的 ns1 应该是可以相互 ping 通的了,这时候的状态如下:

要让两台主机的 n1 相互通信,只需要在两台主机上再加一条路由即可:
host1:

host2:

如无意外,这时候两台主机的 NS1 已经可以相互 ping 通了,但这里的意外太多了:
1. 首先,如果你的两台主机不在同一个网段,这是不通的,主机所在网络的路由器不知道如何转发 192.168.0.0/16 网段的流量,数据包应该会被丢弃;
2. 其次,如果你是在阿里云或腾讯云的主机上试,也是不通的,因为这两大云平台的主机都不是二层直连的,当数据从 host1 离开的时候,目的地址为 192.168.11.10,这个地址 ECS 所在的 VPC 的路由表不知道怎么转发,也会丢弃,所以要去主机所在网络的路由表中添加两条到 192.168.10.0/24 和 192.168.11.0/24 两个网段的条目;
3. 如果是在华为云的 ECS 上,则要关闭 ECS 的网卡的“源/目的检查”;
4. 最后,还要记得在 ECS 所属的安全组里添加对于这两个网段的来源信任。
一个 CNI 插件通常包含两部分,一个 binary 和一个 daemon,那个 daemon 的其中一个功能就是一直监听来自 api-server 的 node 新增、删除事件,把相关的 pod-cidr 和 node-ip 添加到各个节点的主机路由条目上。
IP tunnel
ip tunnel 的方式就是在各个主机上建立一个 one-to-many 的 ip tunnel,然后把其它节点的 pod-cidr 加到主机路由上,只不过 dev 就不再是 eth0 了,而是新建的 ip tunnel 设备,我们接着上面的环境继续操作:
首先删除在两台主机上增加的主机路由
host1:

host2:

然后在两台主机上分别创建一个 one-to-many 的 ip tunnel(所谓的 one-to-many,就是配置 ip tunnel 时,不指定 remote address)
host1:

host2:

此时的状态:

这时候两个 ns1 应该已经可以相互 ping 通了,而且这次不管什么云平台不管安全组开没开不管什么源目的检查,应该都能通了,因为 ip tunnel 的原理是在原来的 ip 头上再加一层 ip 头,外层的 ip 头用的源目的是主机的 ip,让主机所在的网络感觉这是正常的主机流量,在 host1 中执行:

在 host2 的 eth0 用 tcpdump 打印一下流量,就能看到有两层 ip 头:

vxlan
vxlan 的方式有点像 ip tunnel 的方式,也是在两个主机建立一个 overlay 的网络,只不过 vxlan 是 overlay 在二层,我们接着上面的环境继续往下做,先把 mustang.tun0 删除
在两个节点上执行:

接下来在 host1 上执行如下命令创建 vxlan 设备:

然后查看 vxlan0 的设备的 mac 地址,记下来:

记住如下信息 A:(192.168.10.0/24 下一跳 192.168.10.1、 下一跳 MAC 02:40:94:ce:82:cc 、下一跳的 MAC 所在主机 IP 10.57.4.21)
在 host2 上执行:

查看 host2 的 vxlan0 设备的 mac,记下来:

记住如下信息 B:(192.168.11.0/24 下一跳 192.168.11.1 、 下一跳 MAC 02:3f:39:67:7d:f9 、下一跳的 MAC 所在主机 IP 10.57.4.20)
回到 host1 上执行如下命令:这里用的是记录的信息 A,第一条命令加主机路由,第二条命令加邻居表,第三条加 FDB

host2 也一样,只不过用的是记录的信息 B:

这时候两台主机的 NS1 应该可以相互 ping 通了,在 host2 的容器里 ping 一下 host1 的容器,在 host1 打开网卡监听,拦截的数据如下:

可以看到也是两层包头,外层包头显示这是 otv(overlay transport virtualization)包,对于 otv,用一句话解释:
OTV is a "MAC in IP" technique to extend Layer 2 domains over any transport。
对比
用主机路由的方式,因为没有封包拆包的性能损耗,所以速度是最快的,但因为受限的场景较多,反而使用得并不广泛;ip tunnel 和 vxlan 因为要封包拆包,性能会比主机路由的方式差一些,但没有场景限制,基本上只要节点是通的,容器就能通,所以使用比较广泛。
虽然同是 overlay 模式,vxlan 和 ip tunnel 之间的性能还是有比较大的差异。vxlan 在外层是依赖两台主机的 UDP 通信,而且第一层是到了 MAC 层才开始再次封包,查 FDB 和 ARP 表,相对于 ip tunnel 只是在 IP 头的外面再加一层 IP 头来说性能损耗多了很多,我们通过实测发现 vxlan 会比 ip tunnel 性能下降 20%左右。
总结
通过前面一系列的文章,我们可以了解到,跨主机容器通信的方式与同主机容器间通信方式是分开的,你可以用纯 veth/bridge/macvlan/ipvlan 等方式实现同主机容器通信,可以用 host-gw/ip tunnel/vxlan/弹性网卡来实现跨主机的容器通信,现在主流的 cni 基本上就是在这几种选择中组合。
上面的三种跨主机容器通信的方式,其中的主机路由就是 flannel 的 host-gw 和 calico 的 BGP 模式的原理,而 ip tunnel 就是 calico 的 IPIP 模式的原理(flannel 的最新版也应该很快会支持 IPIP,看代码已经加上了),vxlan 方式就是 flannel 的 vxlan 模式的原理。
这个系列的文章出发点是为了剖析 k8s 常用的 CNI 组件的原理,到这一步我们已经把主流的几个 cni 的几种模式的原理都覆盖了,但还差一个,就是 flannel 最原始的 udp 模式,这种模式自从 flannel 出了 vxlan 后就很少人再用了,因为性能损耗更大,flannel 的 vxlan 用的就是 udp 协议,跟 flannel 的 udp 模式有哪些区别呢?下一章将单独讲 flannel 的 udp 模式。
了解更多信息请登录:https://www.deepexi.com/bbs/developer-bbs
版权声明: 本文为 InfoQ 作者【滴普科技2048实验室】的原创文章。
原文链接:【http://xie.infoq.cn/article/d70636a4719e29b8852d4b64a】。文章转载请联系作者。
评论