k8s那些事之容器网络
运行环境情况千差万别,所以容器网络方案也是五花八门,官方都列出一堆网络模型:https://kubernetes.io/zh/docs/concepts/cluster-administration/networking/
最常见的网络插件就是flanneld和calico,最常见的模式是隧道模式或者三层网络模型,下面来从CNI插件来分析下容器网络模型
Vxlan
UDP模式因为性能问题被废弃,因为其仅在发出包的过程中,就要经过三次用户态和内核态的数据拷贝。目前使用较多的就是vxlan模式。
VXLAN,即 Virtual Extensible LAN(虚拟可扩展局域网),是 Linux 内核本身就支持的一种网络 虚似化技术。所以说,VXLAN 可以完全在内核态实现封装和解封装的工作,从而通过相似的“隧道”机制,构建出覆盖网络(Overlay Network)。
以flanneld为例通信流程:
┌───────────────────────────────────────────┐
│ │
│ 10.168.0.2/24 ▼ 10.168.0.3/24
┌───────────┬─────────┬──────────────┐ ┌───────────┬─────────┬───────────────┐
│ │ eth0 │ │ │ │ eth0 │ │
│ └─────────┘ │ │ └─────────┘ │
│ ▲ │ │ │ │
│ │ │ │ ▼ │
│ ┌─────────────┐ │ │ ┌─────────────┐ │
│ VTEP │ flannel.1 │10.1.15.0/32│ │ VTEP │ flannel.1 │10.1.16.0/32 │
│ └─────────────┘ │ │ └─────────────┘ │
│ ▲ │ │ │ │
│ │ │ │ ▼ │
│ ┌─────────────┐ │ │ ┌─────────────┐ │
│ │ cni0 │10.1.15.1/24│ │ │ cni0 │10.1.16.1/24 │
│ └─────────────┘ │ │ └─────────────┘ │
│ ▲ │ │ │ │
│ ┌───────┴──────────┐ │ │ ┌───────┴──────────┐ │
│ │ │ │ │ ▼ ▼ │
│ ┌──────────┐ ┌──────────┐ │ │ ┌──────────┐ ┌──────────┐ │
│ │container1│ │container2│ │ │ │container1│ │container2│ │
│ └──────────┘ └──────────┘ │ │ └──────────┘ └──────────┘ │
│ 10.1.15.1 │ │ 10.1.16.3 │
│ Node1 │ │ Node2 │
└────────────────────────────────────┘ └─────────────────────────────────────┘
flanneld会在宿主机上新建一个VTEP设备flannel.1,既有IP又有MAC地址,当一个节点加入集群的时候,flanneld会把上面节点定义的子网网段写入每个节点的路由,以上图来举例,node2加入集群的时候,node1上会写入一条路由
|
|
这样node1就知道了凡是发往 10.1.16.0/24 网段的 IP 包,都需要经过 flannel.1 设备发出,并且,它最后被发往的网关地址是:10.1.16.0, 并且10.1.16.0 正是 Node 2 上的 VTEP 设 备(也就是 flannel.1 设备)的 IP 地址
那vxlan又是怎么模拟二层通信的呢?
node2的flanneld启动的时候,会在node1的ARP表上加一条数据
|
|
这条记录的意思非常明确,即:IP 地址 10.1.16.0,对应的 MAC 地址是 5e:f8:4f:00:e3:37,
然后去一个叫作 FDB(Forwarding Database)的转发数据库查找MAC地址和目的宿主机IP地址的关系
|
|
最后node1的内核就会把node2的mac地址、IP地址和一个vxlan表示VNI封到数据包头,伪装成一个二层包,然后内核把这个数据帧封装到UDP包里发出去
目的宿主机收到包之后根据VNI进行解包操作,然后宿主机拿到目的容器的MAC地址之后,在本身的CAM表(即交换机通过 MAC 地址学习维护的端口和 MAC 地址的对应表)里面找到端口对应的veth pair,将数据发往这个veth pair,veth pair往往是一对,另一端到容器内部,所以数据就发到了容器内部
三层网络
vxlan模式通用性强,但是因为要封包解包,存在网络消耗,所以不少网络插件提供三层网络路由直通方式
flanneld有host-gw模式,calico有BGP模式
flannel host-gw
当你设置 Flannel 使用 host-gw 模式之后,flanneld 会在宿主机上创建这样一条规则,以上面的node 1 为例
|
|
就是设置对应网段的IP包,经过本机的eth0,发到下一跳的IP地址(其实就是node2的eth0的IP)
当 IP 包从网络层进入链路层封装成帧的时候,eth0 设备 就会使用下一跳地址对应的 MAC 地址,这个数据帧就会从 Node 1 通过宿主机的二层网络顺利到达 Node 2 上
Node 2 的内核网络栈从二层数据帧里拿到 IP 包后,会“看到”这个 IP 包的目的Pod IP 地址,然后转发到CNI0网桥
host-gw 模式能够正常工作的核心,就在于 IP 包在封 装成帧发送出去的时候,会使用路由表里的“下一跳”来设置目的 MAC 地址。这样,它就会经 过二层网络到达目的宿主机。
Flannel 子网和主机的信息,都是保存在 Etcd 当中的。flanneld 只需要 WACTH 这些数 据的变化,然后实时更新路由表即可,当然用host-gw模式要求二层是联通的。
calico
calico也是比较类似的,不过calico设置这个路由是通过BGP来做的
每个node上运行一个软路由软件bird,并且被设置成BGP Speaker,与其它node通过BGP协议交换路由信息。
可以简单理解为,每一个node都会向其它node通知这样的信息:
我是X.X.X.X,某个IP或者网段在我这里,它们的下一跳地址是我。
这里最核心的“下一跳”路由规则,就是由 Calico 的 Felix 进程负责维护的。这些路由 规则信息,则是通过 BGP Client 也就是 BIRD 组件,使用 BGP 协议传输而来的。
calico与Flannel 的 host-gw 模式的另一个不同之处, 就是它不会在宿主机上创建任何网桥设备。
┌─────────────────────────────────────────────┐
│ │
│ ▼
┌───────────┬─────────┬──────────────┐ ┌───────────┬─────────┬──────────────┐
│ │ eth0 │ │ │ │ eth0 │ │
│ └─────────┘ │ │ └─────────┘ │
│ ▲ │ │ │ │
│ │ │ │ ▼ │
│ ┌─────────────┐ │ │ ┌─────────────┐ │
│ │ routes │ │ │ │ routes │ │
│ └─────────────┘ │ │ └─────────────┘ │
│ ▲ │ │ │ │
│ ┌───────┴──────────┐ │ │ ┌───────┴──────────┐ │
│ │ │ │ │ ▼ ▼ │
│ ┌───────────┐ ┌───────────┐ │ │ ┌───────────┐ ┌───────────┐ │
│ │cali9c02e56│ │cali20949C │ │ │ │caliacsdfe5│ │cali20234g │ │
│ └───────────┘ └───────────┘ │ │ └───────────┘ └───────────┘ │
│ ▲ ▲ │ │ │ │ │
│ │ │ │ │ │ │ │
│ │ │ │ │ ▼ ▼ │
│ ┌──┬────┬───┐ ┌──┬────┬──┐ │ │ ┌──┬────┬───┐ ┌──┬────┬──┐ │
│ │ │eth0│ │ │ │eth0│ │ │ │ │ │eth0│ │ │ │eth0│ │ │
│ │ └────┘ │ │ └────┘ │ │ │ │ └────┘ │ │ └────┘ │ │
│ │ │ │ │ │ │ │ │ │ │ │
│ └───────────┘ └──────────┘ │ │ └───────────┘ └──────────┘ │
│ Node1 │ │ Node2 │
└────────────────────────────────────┘ └────────────────────────────────────┘
Calico 的 CNI 插件会为每个容器设置一个 Veth Pair 设备,然后把其中的一端放置 在宿主机上(它的名字以 cali 前缀开头)
此外,由于 Calico 没有使用 CNI 的网桥模式,Calico 的 CNI 插件还需要在宿主机上为每个容 器的 Veth Pair 设备配置一条路由规则,用于接收传入的 IP 包。比如,宿主机 Node 2 上的 Container 2 对应的路由规则
10.1.16.3 dev cali20234g scope link
即发往 10.233.2.3 的 IP 包,应该进入cali20234g设备,这样数据就能通了
注意Calico也要求二层联通,如果不能联通就需要打开IPIP模式,IPIP模式设置的路由也和BGP有所不同
10.233.2.0/24 via 192.168.2.2 tunl0
尽管这条规则的下一跳地址仍然是 Node 2 的 IP 地址,但这一次,要负责将 IP 包 发出去的设备,变成了 tunl0,Calico 使用的这个 tunl0 设备,是一个 IP 隧道(IP tunnel)设备。IP 包进入 IP 隧道设备之后,就会被 Linux 内核的 IPIP 驱动接管。IPIP 驱动 会将这个 IP 包直接封装在一个宿主机网络的 IP 包中,这样原先从容器到 Node 2 的 IP 包,就被伪装成了一个从 Node 1 到 Node 2 的 IP 包
node2拿到之后内核通过IPIP驱动进行解包,从而拿到原始的 IP 包。然后,原始 IP 包就会经过路由规则和 Veth Pair 设备到达目的容器内部。