15. 可不可以不封包@flannel
introduction
在之前的描述中我们把(网桥+veth) 描述成一种默认, 这是不是说容器网络方案里, 网桥是一个必选项? 大家嘴里说的大二层网络是怎样的一种网络? 首先想一下, 网桥是不是一种必须的设备?
有没有可能 veth 直接搭在宿主机上, 只要宿主机能在它的路由表上直接说明 [容器IP & 使用某个veth设备接收] , 这样的对应关系能说明就可以了吧? 唯一的缺点是宿主机上的路由表可能会比较长, 但是至少理论上是可行的吧?
flannel-host-gw/直发模型
虽然此时 flannel 还是需要一个网桥, 但是此时已经开始出现一些不一样的特性了: 那就是我们不需要再宿主机上设置一个设备, 来封装容器的IP包了, 容器发出去什么包, 宿主机就发出去什么包. 我们回忆一下为什么之前宿主机需要封包: 因为容器的IP外网上没人认识, 因此你发出去不知道由谁来接收.
我们现在重新想一下这句话是不是绝对正确的: 设想一种网络环境, 在某个Kubernetes集群下, 所有宿主机都在同一个子网下, 意思就是不同宿主机之间发出的IP包不需要经过路由器就可以送达给对方, 宿主机 be like oh 他跟我在同一个网段下因此我直接通过mac地址就可以送达给他. 那么在这种前提假设下, 宿主机会在自己的路由表里添加如下规则:
以上这段话的含义是:
任何发往
10.244.1.0/24
网段下的包 : 对方宿主机下辖容器网段请使用eth0设备发往10.168.0.3 : 对方宿主机IP
"下一跳"这个概念对应着IP包头中的mac地址, 代表下一个收到这个包的人是谁, 如果收件人在同一子网下, 我们会在包头上写上 [容器IP+宿主机Mac] / 如果收件人不在同一子网下, 那么就要经过路由器, 那么此时IP包头会写成 [容器IP + 路由器Mac] . 在我们的这个案例中因为在同一子网下, 因此我方发件人(宿主机)在封IP包头的时候写的是 [容器IP + 宿主机Mac]. 这个包顺着物理链路, 经过交换机的转发到达了对方的宿主机, 对方拆开以后, 发现收件人是10.244
开头的, 对比自己的路由表, 经由 veth → 网桥 → veth 的路径发给容器.
这样, 我们就做到了, 在没有任何封包/拆包步骤下实现了容器通信. 因为没有宿主机封包/拆包过程, 因此效率还是蛮不错的. 宿主机上的flanneld负责watch-etcd并实时更新宿主机上的路由表即可.
但是回顾整个通信过程, 需要的一个大前提是双方宿主机都在同一个子网下, 所有通讯过程都是不经过路由器的, 靠mac地址就能互达, 我们叫它二层互通, 这个其实是一个蛮高的要求, 因为明明在K8s模型下我们只要求了三层互通, 即靠IP互通就可以了. 但如果不是同一个子网, 却坚持要使用host-gw方案, 那有什么办法呢?
改一改路由器: 让路由器看到收件人是容器IP, 知道应该发给那个宿主机, 不过一般公有云是不可能让你这么改路由器的, 私有环境下试试吧
设置转发层: 路由器改不了, 也就是路由器是无法识别容器IP了, 因此我们现在要把IP报头设置成对方宿主机IP, 那还是引入了主机的拆包/封包过程, 跟vxlan方案也没啥区别了
Calico的真直连
到了 Calico 这里就真的没有网桥了, 按照我们说的所有veth直接贴在宿主机上, 这样虽然会造成宿主机的路由表比较长, 但至少是可能的, 至少我们不需要网桥了. 因此每次有新的容器创建, 宿主机的路由表就会多出一条类似这样的路由规规则, 下面这句话的意思是: 发往 10.233.2.3 (容器IP)的包, 请经由 cali586(veth) 设备发出.
虽然大家都是[没有拆封包的过程] , 但是除了没有网桥以外, calico 相比于 flannel/hostgw 最大的区别是, 它的转发信息表, 是通过BGP协议维护的. BGP我们叫它边界网关协议, 它起源于这样一个问题: 如果消息要跨越局域网传递, 那么它必须从我们的路由器出发, 抵达对面局域网的路由器后, 经由交换机交付给收件人, ok我们都知道这一点, 那我们的路由器怎么知道这个消息应该交给哪个路由器? 这是不是代表路由器必须维护一个 [ IP <-> 路由器 ] 的对应关系表? 然后有新的机器加入的时候也应该及时通知到诸多路由器.
那么我们有某种方式, 让不同路由器之间相互聊天, 彼此告知自己所下辖的IP, 让对方记录下, 好让以后有自己的消息的时候交给自己. 他们这种聊天的方式我们叫边界网关协议, 他们聊天的内容(也就是BGP消息正文)可能是这样的:
ok现在切换回Calico网络里, 在这种网络模型下, "下辖网段"对应着宿主机上的容器IP段, 需要彼此聊天并交换信息的就是宿主机, 宿主机彼此通过BGP协议聊天, 互相告知自己身上所有容器的 IP 信息. 于是这样就能封出一个[容器IP+宿主机Mac]的包, 并经由mac地址送给对方宿主机. 为了实现这种网络. Calico网络方案包含以下部分:
BIRD: 聊天客户端
Felix: 在获知路由信息以后, 维护本宿主机上的转发表
Calico的CNI插件, 负责配置容器内网络栈
hostgw的问题到这里依旧存在, 如果跨局域网, 那么宿主机在封IP报头的时候, 需要填写的IP=容器IP, 而mac地址就变成了路由器的mac地址, 但等路由器拿到这个IP包的时候就懵逼了, 因为它看不懂这个容器IP到底是什么. 这种情况下, 我们就要打开Calico的IPIP模式
在之前的Linux网络基础中我们提过 ipip 隧道就是在 ip 报头上再封一个ip报头, 里层是容器IP, 外层是宿主机IP, 这样这个报文的 IP报头就是 [对方宿主机IP + 路由器mac], 路由器拿到这个包的时候知道这个宿主机IP怎么发, 通过转发给对方路由器进而发给对方宿主机.
Last updated
Was this helpful?