13. 使用tun设备封包@flannel

introduction

我们先说说比较好理解的 udp 模式吧, 在 udp 模式下运行的 flanneld 会打开一个 /dev/net/tun (不是吧.. 这东西真的有人用wtf?) , 简单回顾一下, 这东西是用来发包的, 你读写这个东西, 而不是读写 socket 对象, 就允许那一头打开这个文件的人来修改你的包, 然后由他帮你发包.

那么这个人是谁? 这个人就是 flanneld, 你封出来的包使用的是虚拟IP, 发不出去的, 他帮你再封出一个IP头, 上面写对方宿主机IP, 这样你的包才能被发出去, 对方宿主机上的flanneld 会收到这个包, 然后由他帮你递给他的容器

# flanneld 帮你创建了一个类型为 tun 的网卡, 名为 flannel0
$ ip -d link show flannel0
5. flannel0: <POINTTOPOINT> MTU=1472 tun

# flanneld 自己正在监听 8285 端口
$ netstat -ulnp |  grep flanneld
udp     0     0    172.16.130.140:8285

快递员flanneld

现在详细聊聊如何做到跨宿主机的通信, 实验条件如下, 假设现在容器A想要Ping容器B, 他俩的环境是:

# ------------------------------------------------  容器-A

# 容器A的IP (也就是容器A下, eth0网卡绑定的IP, 这玩意本质上是veth)
10.244.1.96     
# 容器A的路由表
0.0.0.0      10.244.1.1   0.0.0.0        eth0     
10.244.0.0   10.244.1.1   255.255.0.0    eth0

# 宿主机A的IP
172.16.130.140  
# 宿主机A的路由表
10.244.0.0    0.0.0.0     255.255.255.0  flannel0 
10.244.1.0    0.0.0.0     255.255.255.0  cni0
172.16.130.0  0.0.0.0     255.255.255.0  eth0

# ------------------------------------------------  容器-B

# 容器B的IP
10.244.2.194    
# 宿主机B的IP
172.16.130.164  
# 宿主机B的路由表
10.244.2.0   0.0.0.0    255.255.255.0   cni0
  • 容器A先发出 ICMP 报文, 容器A封出来的包IP为 → [10.244.1.96(源) , 10.244.2.194(目的)]

  • 这个包怎么发? 查容器A的路由表, 目的IP(10.244.2.194) 匹配中第二条, 发送给容器A的eth0(一个veth), 继而到达veth的另一头, 宿主机A的 cni0 网桥 (类似docker0, 也是一个网桥)

  • cni0 网桥拿到这个包以后, 比对宿主机A的路由表, 匹配中第一条, 发给 flannel0网卡

  • 这个 flannel0 网卡本质上是一个 tun设备, 因此这个包继而就会被 flanneld 拿到并开始二次封包, 经过查询etcd能得知两个容器的IP对应的宿主机IP, 因此封上一个20字节的IP头, 与8字节的 udp 头, 此时封出来的包:

    • IP报头为 → [172.16.130.140(源宿主机) , 172.16.130.164(目的宿主机)]

    • UDP报头为 → [8285, 8285]

  • flanneld 处理完了要把这个包发出去, 继续查询宿主机A的路由表, 命中第三条, 此时应该走宿主机的eth0

  • 到达宿主机B的8285端口, 这个端口是 flanneld 监听的, flanneld会拆掉最外层的 udp报头以及 ip报头, 露出真实的ip报头, 然后发出, 经过检查宿主机B上的路由表, 这个包应该发给 cni0 网桥

  • 宿主机B上的 cni0 网桥将这个包转发给容器B, 完成本次通信

flanneld 都起着什么作用

总结一下这个过程, flanneld 在投递的整个过程中, 负责了拆包与封包, 如果这个flanneld 封装了这个包, 这个包就会被另一个 flanneld 拆开, 除此之外, 宿主机之所以能把包交给 flanneld , 也是因为 flanneld 在刷新宿主机的路由表. 在这个过程中:

  • cni0处在内核态下, 然后发给 flannel0 (tun设备), 将数据从内核态里复制到用户态

  • flanneld 封好包以后再经由 eth0 发出, 需要再进入一次内核态

这种进出用户态/内核态的做法, 成为 udp 模式比较蛋疼的地方

Last updated

Was this helpful?