理解 K8s 三层网络,容器之间如何通信?
你好,我是秋意零。今天分享一篇 K8s 网络的工作流程。
K8s 中网络插件还挺多的。常用的也就那么一两个,例如:Flannel、Calico。以我个人接触 K8s 开始(2-3年),我也只使用过这两个。但据我所知,如今几乎都在使用 Calico 插件,因为 Calico 性能更强!这里可以简单说一说:
注意:网络插件作用是解决 K8s 集群容器之间跨主机通信的。
Calico 的工作流程不需要额外的进程处理,而 Flannel 插件是需要 Flannel0 进程处理 IP 包请求的,会在内核态和用户态来回流动导致性能损耗(UDP模式)。一般情况下是这样,因为不管是 Flannel 还是 Calico 都有自己的网络模式!
Flannel 网络模式:UDP、VXLAN、Host-GW。
Calico 网络模式:Host-GW、IPIP。
Flannel 和 Calico的 Host-GW 网络模式工作原理是一致的;
Calico 使用 IPIP 模式和 Flannel 使用 VXLAN 模式性能也差不多,Flannel 的 UDP 模式性能最低。
在正式开始之前,明确理解两个问题即可清楚捋清 K8s 网络的工作流程:
-
在同一台宿主机中,容器之间如何通信? -
在多台宿主机中,容器之间如何跨宿主机通信?
Flannel 的 host-gw 模式
纯三层(Pure Layer 3)网络方案,就是 Flannel 与 Calico 的 host-gw 模式。
这里的 cni0 是 K8s 中通过一个叫作 CNI 的接口,维护了一个单独的网桥来代替 docker0。这个网桥的名字就叫作:CNI 网桥,它在宿主机上的设备名称默认是:cni0。
Flannel 使用 host-gw 模式之后,flanneld 会在宿主机上创建这样一条规则。
Flannel 通过 Etcd 和宿主机上的 flanneld 来维护路由信息
$ ip route
...
10.244.1.0/24 via 10.168.0.3 dev eth0
这条路由规则含义是:目的地址 IP 属于 10.244.1.0/24 网段的 IP 包应该通过 eth0 转发出去;并且下一跳地址是 10.168.0.3。
从图中我们可以看到 Infra-container-1 访问 Infra-container-2 的流量走势,跨主机通信都是通过编写路由规则进行转发的。Calico 部分会刨析整个路由转发过程。
host-gw 模式的工作原理,其实就是将每个 Flannel 子网(Flannel Subnet,比如:10.244.1.0/24)的“下一跳”,设置成了该子网对应的宿主机的 IP 地址。
这子网是我们初始化 K8s 集群时所设置的 Pod 网段,也是这里的 cni0 网桥网段地址
Calico 网络插件
最开始时,介绍过 Flannel 和 Calico 的 Host-GW 网络模式工作原理是一致的。
Flannel 通过 flanneld 来维护路由表信息,不过 Calico 项目使用了一个“重型武器”来自动地在整个集群中分发路由信息。这个“重型武器”,就是 BGP。
BGP 介绍
BGP(Border Gateway Protocol,边界网关协议)是互联网上用于在不同的自治系统(Autonomous System, AS)之间交换路由信息的标准化外部网关协议。它是当前互联网的核心路由协议之一,主要用于大型网络或ISP(Internet Service Provider,互联网服务提供商)之间以及这些实体与它们的客户之间的路由选择。 BGP 也是在大规模网络中实现节点路由信息共享的一种协议。
自治系统(AS)指的是一个组织管辖下的所有 IP 网络和路由器的全体。可以把 AS 认为是一个小公司里面的所有主机(IP)和路由器。正常情况下,AS 之间不会有任何“来往”。但是,如果两个 AS 里的主机,要通过 IP 地址直接进行通信,我们就必须使用路由器把这两个 AS 连接起来。
图中这样把 AS 连接在一起的路由器,我们就把它形象地称为:边界网关。它跟普通路由器的不同之处在于,它的路由表里拥有其他自治系统里的主机路由信息。
host-gw 模式
在了解了 BGP 之后,Calico 项目的架构就非常容易理解了。它由三个部分组成:
-
Calico 的 CNI 插件,这是 Calico 与 Kubernetes 对接的部分。 -
Felix,它是一个 DaemonSet,负责在宿主机上插入路由规则,以及维护 Calico 所需的网络设备等工作。 -
BIRD,它就是 BGP 的客户端,专门负责在集群里分发路由规则信息。
除了对路由信息的维护方式之外,Calico 项目与 Flannel 的 host-gw 模式的另一个不同之处,就是它不会在宿主机上创建任何网桥设备。
可以看到,Calico 的 CNI 插件会为每个容器设置一个 Veth Pair 设备,然后把其中的一端放置在宿主机上(它的名字以 cali 前缀开头)。
此外,由于 Calico 没有使用 CNI 的网桥模式,Calico 的 CNI 插件还需要在宿主机上为每个容器的 Veth Pair 设备配置一条路由规则,用于接收传入的 IP 包。
比如,宿主机 Node 2 上的 Container 4 对应的路由规则,如下所示:
10.233.2.3 dev cali5863f3 scope link
即:发往 10.233.2.3 的 IP 包,应该进入 cali5863f3 设备。
有了这样的 Veth Pair 设备之后,容器发出的 IP 包就会经过 Veth Pair 设备出现在宿主机上。然后,宿主机网络栈就会根据路由规则的下一跳 IP 地址,把它们转发给正确的网关。接下来的流程就跟 Flannel host-gw 模式完全一致了。
其中,这里最核心的“下一跳”路由规则,就是由 Calico 的 Felix 进程负责维护的。这些路由规则信息,则是通过 BGP Client 也就是 BIRD 组件,使用 BGP 协议传输而来的。
Route Reflector 模式
Calico 维护的网络在默认配置下,是一个被称为“Node-to-Node Mesh”的模式。这时候,每台宿主机上的 BGP Client 都需要跟其他所有节点的 BGP Client 进行通信以便交换路由信息。但是,随着节点数量 N 的增加,这些连接的数量就会以 N²的规模快速增长,从而给集群本身的网络带来巨大的压力。
所以,Node-to-Node Mesh 模式一般推荐用在少于 100 个节点的集群里。而在更大规模的集群中,你需要用到的是一个叫作 Route Reflector 的模式。
在这种模式下,Calico 会指定一个或者几个专门的节点,来负责跟所有节点建立 BGP 连接从而学习到全局的路由规则。而其他节点,只需要跟这几个专门的节点交换路由信息,就可以获得整个集群的路由规则信息了。
这些专门的节点,就是所谓的 Route Reflector 节点,它们实际上扮演了“中间代理”的角色,从而把 BGP 连接的规模控制在 N 的数量级上。
Calico 的 IPIP 模式
在 Calico 的 IPIP 模式下,Felix 进程在 Node 1 上添加的路由规则,会稍微不同,如下所示:
10.233.2.0/24 via 192.168.2.2 tunl0
可以看到,尽管这条规则的下一跳地址仍然是 Node 2 的 IP 地址,但这一次,要负责将 IP 包发出去的设备,变成了 tunl0。注意,是 T-U-N-L-0,而不是 Flannel UDP 模式使用的 T-U-N-0(tun0),这两种设备的功能是完全不一样的。
Calico 使用的这个 tunl0 设备,是一个 IP 隧道(IP tunnel)设备。
在上面的例子中,IP 包进入 IP 隧道设备之后,就会被 Linux 内核的 IPIP 驱动接管。IPIP 驱动会将这个 IP 包直接封装在一个宿主机网络的 IP 包中,如下所示:
当 Calico 使用 IPIP 模式的时候,集群的网络性能会因为额外的封包和解包工作而下降。在实际测试中,Calico IPIP 模式与 Flannel VXLAN 模式的性能大致相当。所以,在实际使用时,如非硬性需求,我建议你将所有宿主机节点放在一个子网里,避免使用 IPIP。
Calico 网络在 K8s 中验证与梳理
在 K8s 环境中部署 busybox-1、busybox-2、busybox-3。其中 busybox-1、busybox-2 运行在 k8s-master-1 节点,busybox-3 运行在 k8s-node-1 节点
这里记住
-
10.244.196.12 的 IP 是 busybox-1 容器的; -
10.244.196.11 的 IP 是 busybox-2 容器的; -
10.244.196.75 的 IP 是 busybox-3 容器的。
同一主机,容器之间如何通信?
可以看到,图中进入到 busybox-1 Pod 是可以直接 ping 通 busybox-2 Pod 的 IP
要理解 busybox-1 访问 busybox-2 的访问流程。先来看看 k8s-master-1 节点的网络设备和路由规则。
1)网络设备
tunl0 这个设备(10.244.196.0/32)为了便于理解可以看作是一个桥接,但并不是桥接的工作方式,因为你使用 brctl show 命令查看时并没有任何桥接信息。
它的工作原理在Calico 网络 IPIP 模式下介绍过,实现的转发是封装、拆解包的过程。
2)路由规则
busybox-1 的 Pod 中,容器内部路由规则。可以看到我们在容器内部目的地址不管是什么最终都由容器内 eth0 设备转发
宿主机路由规则
根据图中指出的这条路由规则,可以发现只要目的地址是 10.244.196.11 的都会被转发到 cali74ee67614fb 接口,这接口是 Veth Pair 设备也是 busybox-2 的网络接口。
到这里,你应该能看出。在同一宿主机的情况下容器之间的通信是经过两个路由规则实现的转发通信的。
3)最后,同一主机,容器之间通信流程图如下:
容器之间如何跨宿主机通信?
可以看到,下图中进入到 busybox-1 Pod 也是可以直接 ping 通 busybox-3 Pod 的 IP。那么这个访问流量是怎么走的呢?
1)首先,还是经过容器内部的路由到达宿主机 k8s-master-1 节点
由于我们目的地址是 10.244.109.75(busybox-3)在宿主机中走下图指出的这条路由规则。同时经过 tunl0 设备封装同宿主机同网段的 IP,这时我们看网关地址是 k8s-node-1(192.168.200.16) 这是一个主机名。那么我们 IP 的目的地址就变成了 192.168.200.16 就会通过 ens33 接口的路由规则出去,到达 k8s-node-1。
10.244.109.64 这是一个 Overlay 网络子网,通常用于连接多个 Kubernetes 节点上的 Pod。这个地址是通过 Calico 的网络插件分配的,用于在节点之间传递流量。每个节点都会被分配这样一个 Overlay 网络以及这样的路由规则。
2)访问流量到达 k8s-node-1 之后,经过 tunl0 拆包得到目的地址 10.244.109.75 同时匹配下图路由规则,最终与我们 busybox-3 Pod 的容器通信。
3)最后,容器之间跨主机通信流程图如下:
总结
如果主机与主机之间的连通性不是直接通过二层是通过三层转发的。那么会使用 Calico IPIP 模式,之前的二层通信也会通过三层转发。
跨主机 容器与容器之间通信 通过路由表 走三层
参考:《深入剖析 Kubernetes》张磊
End
个人博客:博客介绍
欢迎各位-加群交流(推荐微信群聊):运维交流/商务合作,集合
非特殊说明,本博所有文章均为博主原创。
共有 0 条评论