首页 > 其他分享 >深入剖析 Kubernetes-5 容器网络

深入剖析 Kubernetes-5 容器网络

时间:2023-01-08 23:13:30浏览次数:41  
标签:容器 UDP Kubernetes IP 宿主机 剖析 网桥 设备

深入剖析 Kubernetes-5 容器网络

1 浅谈容器网络

1.1 Veth Pair与Docker网桥

容器要想跟外界进行通信,它发出的 IP 包就必须从它的 Network Namespace 里出来,来到宿主机上。

Docker为容器创建一个一端在容器里充当默认网卡、另一端在宿主机上的 Veth Pair 设备。根据 Veth Pair 设备的原理,发送到容器中 Veth 的数据包会立刻出现在宿主机的 Veth 设备上。宿主机的一端 Veth 被插在 docker0 网桥上,网桥会扮演二层交换机的角色,根据数据包的目的 MAC 地址,在它的CAM表(即交换机通过 MAC 地址学习维护的端口和 MAC 地址的对应表),查找到对应端口(即某个Veth设备),然后把数据包发往这个端口。而这个端口正是另外一个容器插在 docker0 网桥上的一块虚拟网卡,这样,数据包就进入到目标容器的Network Namespace里。

一旦一张虚拟网卡被“插”在网桥上,它就会变成该网桥的“从设备”。从设备会被“剥夺”调用网络协议栈处理数据包的资格,从而“降级”成为网桥上的一个端口。而这个端口唯一的作用,就是接收流入的数据包,然后把这些数据包的“生杀大权”(比如转发或者丢弃),全部交给对应的网桥。

image-20230108190535852

1.2 总结

默认情况下,被限制在 Network Namespace 里的容器进程,实际上是通过 Veth Pair 设备 + 宿主机网桥的方式,实现了跟同其他容器的数据交换。

同一宿主机同一子网内的容器之间是二层网络直连的。

docker网桥充当了容器网络的默认网关,容器访问宿主机或宿主机访问容器,都会经过docker网桥,然后根据宿主机上的iptables规则转发到目的宿主机。

2 深入解析容器跨主机网络

2.1 Flannel

Flannel 项目是 CoreOS 公司主推的容器网络方案。

目前,Flannel 支持三种后端实现,分别是:

  • VXLAN
  • host-gw
  • UDP

2.1.1 UDP模式

在 Linux 中,TUN 设备是一种工作在三层(Network Layer)的虚拟网络设备。TUN 设备的功能非常简单,即:在操作系统内核和用户应用程序之间传递 IP 包。

基于 Flannel UDP 模式的跨主通信的基本原理:

image-20230108212622728

以Node1上的container-1访问Node2上的container-2为例:

  • container-1到Node1

container-1 容器里的进程发起的 IP 包,其源地址就是 100.96.1.2,目的地址就是 100.96.2.3。由于目的地址 100.96.2.3 并不在 Node 1 的 docker0 网桥的网段里,所以这个 IP 包会被交给默认路由规则,通过容器的网关进入 docker0 网桥(如果是同一台宿主机上的容器间通信,走的是直连规则),从而出现在宿主机上。

  • Node1 flannel0处理

这时候,这个 IP 包的下一个目的地,就取决于宿主机上的路由规则了。此时,Flannel 已经在宿主机上创建出了一系列的路由规则,指定了访问container-2的网段进入到一个叫做flannel0的设备中。

100.96.0.0/16 dev flannel0  proto kernel  scope link  src 100.96.1.0

flannel0 设备:TUN 设备(Tunnel 设备),当操作系统将一个 IP 包发送给 flannel0 设备之后,flannel0 就会把这个 IP 包,交给创建这个设备的应用程序,也就是 Flannel 进程。这是一个从内核态(Linux 操作系统)向用户态(Flannel 进程)的流动方向。

  • Node1 flanneld 进程找到 Node2 IP地址,并封装为UDP包发往Node2

宿主机上的 flanneld 进程(Flannel 项目在每个宿主机上的主进程),就会收到这个 IP 包。flanneld 进程在处理由 flannel0 传入的 IP 包时,就可以根据目的 IP 的地址(比如 100.96.2.3),匹配到对应的子网(比如 100.96.2.0/24),从 Etcd 中找到这个子网对应的宿主机的 IP 地址是 10.168.0.3。

由 Flannel 管理的容器网络里,一台宿主机上的所有容器,都属于该宿主机被分配的一个“子网”。在我们的例子中,Node 1 的子网是 100.96.1.0/24,container-1 的 IP 地址是 100.96.1.2。Node 2 的子网是 100.96.2.0/24,container-2 的 IP 地址是 100.96.2.3。子网与宿主机的对应关系,正是保存在 Etcd 当中。

flanneld 会把这个 IP 包直接封装在一个 UDP 包里,然后发送给 Node 2,这个 UDP 包的源地址,就是 flanneld 所在的 Node 1 的地址,而目的地址,则是 container-2 所在的宿主机 Node 2 的地址。

这个请求得以完成的原因是,每台宿主机上的 flanneld,都监听着一个 8285 端口,所以 flanneld 只要把 UDP 包发往 Node 2 的 8285 端口即可。

  • Node2 flanneld 解析 UDP 包,并发往 flannel0 设备

Node2 上的flanneld 可以从这个 UDP 包里解析出封装在里面的、container-1 发来的原 IP 包,把这个 IP 包发送给它所管理的 TUN 设备,即 flannel0 设备。

  • Node2到container-2

这是一个从用户态向内核态的流动方向,Linux 内核网络栈就会负责处理这个 IP 包,具体的处理方法,就是通过本机的路由表来寻找这个 IP 包的下一步流向,Linux 内核会按照路由规则,把这个 IP 包转发给 docker0 网桥,然后通过网桥二层交换机发送到对应的Veth,通过Veth Pair进入到container-2 的 Network Namespace 里。

100.96.2.0/24 dev docker0  proto kernel  scope link  src 100.96.2.1

总结

  • Flannel UDP 模式提供的其实是一个三层的 Overlay 网络,即:它首先对发出端的 IP 包进行 UDP 封装,然后在接收端进行解封装拿到原始的 IP 包,进而把这个 IP 包转发给目标容器。这就好比,Flannel 在不同宿主机上的两个容器之间打通了一条“隧道”,使得这两个容器可以直接使用 IP 地址进行通信,而无需关心容器和宿主机的分布情况。
  • UDP 模式有严重的性能问题,已经被废弃了,原因时 flanneld 的处理过程,使用了 flannel0 这个 TUN 设备,需要经过多次用户态和内核态之间的数据拷贝。且Flannel 进行 UDP 封装(Encapsulation)和解封装(Decapsulation)的过程,也都是在用户态完成的。

image-20230108214828094

  • 我们在进行系统级编程的时候,有一个非常重要的优化原则,就是要减少用户态到内核态的切换次数,并且把核心的处理逻辑都放在内核态进行。

2.1.2 VXLAN

原理

VXLAN,即 Virtual Extensible LAN(虚拟可扩展局域网),是 Linux 内核本身就支持的一种网络虚似化技术。VXLAN 可以完全在内核态实现上述封装和解封装的工作,从而通过与前面相似的“隧道”机制,构建出覆盖网络(Overlay Network)。

VXLAN 的覆盖网络的设计思想是:在现有的三层网络之上,“覆盖”一层虚拟的、由内核 VXLAN 模块负责维护的二层网络,使得连接在这个 VXLAN 二层网络上的“主机”(虚拟机或者容器都可以)之间,可以像在同一个局域网(LAN)里那样自由通信。当然,实际上,这些“主机”可能分布在不同的宿主机上,甚至是分布在不同的物理机房里。

为了能够在二层网络上打通“隧道”,VXLAN 会在宿主机上设置一个特殊的网络设备作为“隧道”的两端。这个设备就叫作 VTEP,即:VXLAN Tunnel End Point(虚拟隧道端点)。VTEP 设备的作用,其实跟前面的 flanneld 进程非常相似。只不过,它进行封装和解封装的对象,是二层数据帧(Ethernet frame);而且这个工作的执行流程,全部是在内核里完成的(因为 VXLAN 本身就是 Linux 内核中的一个模块)。

每台宿主机上名叫 flannel.1 的设备,是 VXLAN 所需的 VTEP 设备,它既有 IP 地址,也有 MAC 地址。

image-20230108220932569

以Node1上的container-1访问Node2上的container-2为例:

  • Node1访问container-2经由flannel.1设备,封装二层包内部数据帧

Node1上的路由表确定了访问 container-2 所在网段经由 flannel.1 设备发出,发出的网关地址是 Node2 的VTEP设备的IP地址。

有了目的 VTEP 设备的 IP 地址,还需要 MAC 地址,而flanneld 进程在节点启动时,会自动将 VTEP设备的 MAC 地址添加到ARP表中。

# 在 Node 1 上
$ ip neigh show dev flannel.1
10.1.16.0 lladdr 5e:f8:4f:00:e3:37 PERMANENT

有了目的 VTEP 设备的 MAC 地址后,Linux 内核就可以开始二层封包工作了。封包过程只是加一个二层头,不会改变“原始 IP 包”的内容。Inner IP Header 字段,依然是 container-2 的 IP 地址,即 10.1.16.3。

  • Node1获取目的VTEP设备所在宿主机IP,封装外部数据帧,发送UDP包

封装好二层头后,并不能在宿主机二层网络里传输,我们称它为”内部数据帧“,Linux 内核还需要再把“内部数据帧”进一步封装成为宿主机网络里的一个普通的数据帧,好让它“载着”“内部数据帧”,通过宿主机的 eth0 网卡进行传输。

Linux 内核会在“内部数据帧”前面,加上一个特殊的 VXLAN 头,用来表示这个“乘客”实际上是一个 VXLAN 要使用的数据帧。VXLAN 头里有一个重要的标志叫作VNI,它是 VTEP 设备识别某个数据帧是不是应该归自己处理的重要标识。而在 Flannel 中,VNI 的默认值是 1。

Linux 内核会把这个数据帧封装进一个 UDP 包里发出去,但是此时只知道目的flannel.1 设备的 MAC 地址,却不知道对应的宿主机地址是什么。

在这种场景下,flannel.1 设备实际上要扮演一个“网桥”的角色,在二层网络进行 UDP 包的转发。而在 Linux 内核里面,“网桥”设备进行转发的依据,来自于一个叫作 FDB(Forwarding Database)的转发数据库。这个 flannel.1“网桥”对应的 FDB 信息,也是 flanneld 进程负责维护的。

# 在 Node 1 上,使用“目的 VTEP 设备”的 MAC 地址进行查询
$ bridge fdb show flannel.1 | grep 5e:f8:4f:00:e3:37
5e:f8:4f:00:e3:37 dev flannel.1 dst 10.168.0.3 self permanent

现在UDP包要访问的目的地址找到后,接下来的流程,就是一个正常的、宿主机网络上的封包工作。

image-20230108223150206

  • Node2解析UDP包并拿到内部数据帧,发送给目的VTEP设备,进而获取到原始IP包

Node 1 上的 flannel.1 设备就可以把这个数据帧从 Node 1 的 eth0 网卡发出去。显然,这个帧会经过宿主机网络来到 Node 2 的 eth0 网卡。Node 2 的内核网络栈会发现这个数据帧里有 VXLAN Header,并且 VNI=1。所以 Linux 内核会对它进行拆包,拿到里面的内部数据帧,然后根据 VNI 的值,把它交给 Node 2 上的 flannel.1 设备。而 flannel.1 设备则会进一步拆包,取出“原始 IP 包”。

总结

VXLAN 模式组建的覆盖网络,其实就是一个由不同宿主机上的 VTEP 设备,也就是 flannel.1 设备组成的虚拟二层网络。对于 VTEP 设备来说,它发出的“内部数据帧”就仿佛是一直在这个虚拟的二层网络上流动。这,也正是覆盖(Overlay )网络的含义。

标签:容器,UDP,Kubernetes,IP,宿主机,剖析,网桥,设备
From: https://www.cnblogs.com/hunter-w/p/17035670.html

相关文章

  • python中的容器类型(2)
    Python中的容器类型(2) python中的容器类型包含字符串(str),元组(tuple),列表(list),集合(set)等类型。接下来是对集合(set),元组(tuple),字典的介绍。1.集合  集......
  • c++ vector容器总结
    vector1.动态扩展:并不是在原空间之后续接新空间,而是找更大的内存空间,然后将原数据拷贝新空间,释放原空间。(一般会找比预料更多的空间)2.vector容器构造1.构造vector​​<int>......
  • [kubernetes]二进制部署k8s集群
    0.前言采用二进制部署三主三工作节点的k8s集群,工作节点和Master节点共用服务器,因此只用到了三台服务器。master采用haproxy+keepalive实现高可用。实际生产环境中,建议......
  • How to create pods in Kubernetes
    https://www.educative.io/answers/how-to-create-pods-in-kubernetes A pod isthesmallestunitinthek8s(Kubernetes)ecosystem,likeanatomin......
  • Kubernetes(k8s) kubectl config常用命令
    kubectl在$HOME/.kube目录中查找一个名为config的配置文件。可以通过设置KUBECONFIG环境变量或设置--kubeconfig参数来指定其它kubeconfig文件。本文主要介绍K......
  • 你可能不知道的容器镜像安全实践
    大家好,我是Edison。最近在公司搭建CI流水线,涉及到容器镜像安全的话题,形成了一个笔记,分享与你,也希望我们都能够提高对安全的重视。1时代背景近年来应用程序逐步广泛运行......
  • 使用 Helm 为 Kubernetes 捆绑 YAML
    微服务架构的引入彻底改变了当今软件的开发方式。后微服务架构取代了单体,容器取代了虚拟机。通过这种转换,构建应用程序因多个容器而变得复杂。容器编排是一个新的瓶颈,被......
  • 十分钟带你理解Kubernetes核心概念
    十分钟带你理解Kubernetes核心概念本文将会简单介绍​​Kubernetes​​​的核心概念。因为这些定义可以在Kubernetes的文档中找到,所以文章也会避免用大段的枯燥的文字介绍。......
  • 为什么 java 容器推荐使用 ExitOnOutOfMemoryError 而非 HeapDumpOnOutOfMemoryError
    前言好久没写文章了,今天之所以突然心血来潮,是因为昨天出现了这样一个情况:我们公司的某个手机APP后端的用户(customer)微服务出现内存泄露,导致OutOfMemoryError,但......
  • [Docker] 将容器打包成镜像、镜像分层机制详解
    目录commit命令创建一个容器打包镜像联合文件系统联合文件系统实践前置准备不使用联合文件系统的挂载使用联合文件系统进行挂载写时复制机制commit命令#将容器打包成......