xiaohanliang
Docker
Docker
  • hi
  • STORAGE DRIVER
    • 1. 什么叫联合挂载
    • 2. 镜像ID是从哪来的
    • 3. 如何组装出一个镜像
    • 4. 为什么有这么多fs@aufs
    • 5. 为什么有这么多fs@overlay
    • 6. 为什么有这么多fs@dm
    • 7. 正确安装devicemapper
  • EXEC DRIVER
    • 1. 资源限制-cgroup
    • 2. 制造小型监狱
    • 3. 切换根目录是什么概念
    • 4. 标准极简容器runC
    • 5. 尝试安装runc工具
  • KUBERNETES NETWORK
    • 0. [WIP]我想串联容器网络的故事
    • 1. 容器网络是什么样的网络
    • 2. cnm认为应该怎样让容器组网
    • 3. 为什么会有一大堆cni插件
    • 4. 为什么要设计出pod
    • 5. 怎样形成一个服务
    • 6. Service就是iptables规则
    • 7. IPVS也能实现Service
    • 8. 从集群外访问服务-nodeport
    • 9. 从集群外访问服务-ingress
    • 10. 想要把域名变成IP
    • 11. DNS是怎么实现的
    • 12. 最常见的方案@flannel
    • 13. 使用tun设备封包@flannel
    • 14. 使用vxlan设备封包@flannel
    • 15. 可不可以不封包@flannel
Powered by GitBook
On this page
  • introduction
  • flannel-host-gw/直发模型
  • Calico的真直连

Was this helpful?

  1. KUBERNETES NETWORK

15. 可不可以不封包@flannel

introduction

在之前的描述中我们把(网桥+veth) 描述成一种默认, 这是不是说容器网络方案里, 网桥是一个必选项? 大家嘴里说的大二层网络是怎样的一种网络? 首先想一下, 网桥是不是一种必须的设备?

有没有可能 veth 直接搭在宿主机上, 只要宿主机能在它的路由表上直接说明 [容器IP & 使用某个veth设备接收] , 这样的对应关系能说明就可以了吧? 唯一的缺点是宿主机上的路由表可能会比较长, 但是至少理论上是可行的吧?

flannel-host-gw/直发模型

虽然此时 flannel 还是需要一个网桥, 但是此时已经开始出现一些不一样的特性了: 那就是我们不需要再宿主机上设置一个设备, 来封装容器的IP包了, 容器发出去什么包, 宿主机就发出去什么包. 我们回忆一下为什么之前宿主机需要封包: 因为容器的IP外网上没人认识, 因此你发出去不知道由谁来接收.

我们现在重新想一下这句话是不是绝对正确的: 设想一种网络环境, 在某个Kubernetes集群下, 所有宿主机都在同一个子网下, 意思就是不同宿主机之间发出的IP包不需要经过路由器就可以送达给对方, 宿主机 be like oh 他跟我在同一个网段下因此我直接通过mac地址就可以送达给他. 那么在这种前提假设下, 宿主机会在自己的路由表里添加如下规则:

$ ip route
10.244.1.0/24 via 10.168.0.3 dev eth0

以上这段话的含义是:

  • 任何发往 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) 设备发出.

10.233.2.3 dev cali5863f3 scope link

虽然大家都是[没有拆封包的过程] , 但是除了没有网桥以外, calico 相比于 flannel/hostgw 最大的区别是, 它的转发信息表, 是通过BGP协议维护的. BGP我们叫它边界网关协议, 它起源于这样一个问题: 如果消息要跨越局域网传递, 那么它必须从我们的路由器出发, 抵达对面局域网的路由器后, 经由交换机交付给收件人, ok我们都知道这一点, 那我们的路由器怎么知道这个消息应该交给哪个路由器? 这是不是代表路由器必须维护一个 [ IP <-> 路由器 ] 的对应关系表? 然后有新的机器加入的时候也应该及时通知到诸多路由器.

那么我们有某种方式, 让不同路由器之间相互聊天, 彼此告知自己所下辖的IP, 让对方记录下, 好让以后有自己的消息的时候交给自己. 他们这种聊天的方式我们叫边界网关协议, 他们聊天的内容(也就是BGP消息正文)可能是这样的:

[BGP消息]
我是宿主机192.168.1.3
10.233.2.0/24网段的容器都在我这里
这些容器的下一跳地址是我

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怎么发, 通过转发给对方路由器进而发给对方宿主机.

Previous14. 使用vxlan设备封包@flannel

Last updated 4 years ago

Was this helpful?