K8s学习笔记之了解k8s的网络模型

在这里插入图片描述

文章目录

  • docker 网络模型
  • 容器与容器之间,容器与宿主机之间如何通信
    • 容器访问外部网络
    • 外部网络访问容器
  • k8s 网络模型
    • CNI
    • pod 网络配置流程
  • k8s 热门网络插件介绍
    • Flannel 来源
    • Calico 来源
    • Cilium 来源
  • k8s 网络插件的工作模式
    • Flannel 的工作模式
    • Calico 的工作模式
      • BGP 和 AS
        • 覆盖网络 VXLAN
        • 覆盖网络 ipip
          • 在 IPIP 模式下,数据包是如何传递呢?
        • 路由网络 BGP
        • 总结
      • 路由反射器
    • Cilium 的工作模式
      • eBPF 和 XDP
    • 三大插件的对比

docker 网络模型

其实 k8s 的网络模式和 k8s 还是比较像的,从 docker 网络模型开始,是一个相对简单的起点,Docker 使用 linux Namespace 实现了容器的资源隔离,包括 pid ,mount ,network ,uts ,ipc 。通过这些命名空间,实现了容器之间的独立性

容器与容器之间,容器与宿主机之间如何通信

在一个 IDC 机房中,如果想实现两台物理机之间的网络通信,一般情况下会通过网线连接两台主机使其形成一个局域网。在 docker 中也是类似的原理,docker 引入了一个叫做网桥的概念,并在宿主机上创建了一个名为 docker0 的网桥从当交换机的角色,所有容器都连接的到这个网桥上,使得容器与容器之间就像在一个局域网之内。

由于 docker0 被创建在宿主机的网络命名空间中,因此容器无法直接跨命名空间通信宿主机,为了解决这个问题,docker 引入了虚拟以太网这个概念,每个 Veth 都包含两个虚拟网络接口,其中一个接口被配置到容器的网阔栈中,命名为 eth0 另外一个则被配置的到了宿主机的网络栈中,被命名为 veth… 并连接到了 docker0 网桥中。

Veth 就像一根虚拟网线,连接了容器与宿主机之间的网络命名空间,这就是容器与宿主机之间通信的原理,那么容器与容器之间是如何通信的呢?当容器与容器通信时,其中一个容器会从 eth0 发送数据包到 docker0 ,docker0 再将数据包发送给另外一个容器的 eth0 ,实现了容器与容器之间的网络通信

容器访问外部网络

当容器需要访问外部网络时,数据包会被 iptables 转发到目标网络,docker 默认会从数据主机上创建 iptables 规则

iptables -A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
  • -A POSTROUTING : 将规则添加到 POSTROUTING 链中,用于处理出站数据包
  • -s 172.17.0.0/16 : 匹配来着这个 ip 端的数据包,这是 docker 的默认网段,用于为容器分配 ip
  • ! -o docker0 : 匹配不是从 docker0 过来的数据包
  • -j MASQUERADE : 对数据包执行 MASQUERADE 操作【类似于源地址转换】其中数据包的源地址会被转换成为宿主机的 ip 地址,然后由宿主机的网络去发送

外部网络访问容器

虽然容器具有独立的网络,但是除了宿主机之外,其他的网络是无法直接访问容器的。为了解决这个问题,在创建容器时需要指定 -p 参数,将宿主机端口映射到容器中,端口映射也是通过 iptables 实现的,具体规则如下:

iptables -A DOCKER ! -i docker0 -p tcp -m tcp --dport 8080 -j DNAT --to-destination 172。17.0.7:80
  • -A DOCKER : docker 自定义的链名
  • ! -i docker0 : 匹配不是来自 docker0 的数据包
  • -p tcp -m tcp --dport 8080 : 匹配 tcp 和目标端口的 8080 的数据包
  • -j DNAT --to-destination 172。17.0.7:80 : 对匹配的数据包执行 DNAT 操作,其中数据包的目标地址和端口分别被修改为 172.17.0.7 和 80 ,然后由宿主机网络转发到容器内

k8s 网络模型

在 K8s 集群中,如果运行在一个节点的 pod 之间需要通信,则仍然可以使用 docker 那套模式。

k8s 集群是由多个节点组成的,pod 可以被调度到任意的节点上,这就需要不同节点之间的 pod 之间通信了,为了解决不同节点上 pod 的通信,k8s 集群引入了一些第三方的网络插件,例如 calico , Flannel , Cilium 等。这些网络插件必须遵循 CNI 标准才能为 k8s 集群提供网络服务。

Container Network Interface (CNI)
CNI 的基本要求:

  • 每个 pod 拥有集群中唯一的 ip
  • pod 能够与其他节点上的 pod 通信
  • 节点网络可以与集群中的所有 pod 通信
    CNI 宗旨在规范网络插件的是实现,屏蔽低层网络实现的复杂性,将网络插件与 k8s 进行解耦,以提高系统的扩展性和灵活性

CNI

Container Network Interface (CNI) 最早是由CoreOS发起的容器网络规范,是Kubernetes网络插件的基础。其基本思想为:Container Runtime在创建容器时,先创建好network namespace,然后调用CNI插件为这个netns配置网络,其后再启动容器内的进程。现已加入CNCF,成为CNCF主推的网络模型。

pod 网络配置流程

当 kubelet 接收到创建 pod 的请求时,首先它先调用容器运行时创建 Pause 容器,接着 kubelet 会调用 CNI 插件来为 Pause 容器配置网络。这些 CNI 插件通常对应有可执行文件,kubelet 在调用可执行文件时,会传递两个关键的数据 ,第一个是数据的操作方法比如 add 等等,详情请看【k8s 网络插件介绍[[#CNI]]】,第二个参数就是网络插件的配置,可执行文件一旦接收了这些数据,就会去配置 Pause 容器的网络,最后 kubelet 调用容器运行时去创建应用容器,并将其加入到 Pause 容器的网络命名空间中。

当删除 pod 时,kubelet 会通过 CNI 插件进行网络资源的清理和回收

k8s 热门网络插件介绍

K8s 网络插件介绍

Flannel 来源

Flannel 最初是由 CoreOS(后被 Red Hat 收购)开发的,目的是为 Kubernetes 提供一个简单、易于配置的网络方案。Flannel 诞生的背景可以追溯到 Kubernetes 在早期阶段对 容器网络接口(CNI) 插件的需求。

Calico 来源

Calico 最早由 Tigera 公司开发,该公司专注于容器网络和安全解决方案。Calico 的设计目标是为 Kubernetes 和 OpenStack 等现代容器编排平台提供 高性能的网络和安全功能,特别是在大规模、动态环境中的容器管理。

Cilium 来源

Cilium 最初由 Isovalent(一个专注于 eBPF 和容器网络的公司)开发。它是基于 eBPF(Extended Berkeley Packet Filter)技术构建的,eBPF 是一种可以在 Linux 内核中执行的程序,用于高效处理网络包、监控和安全功能。eBPF 技术为 Cilium 提供了 高性能、可编程性和 深度网络可观察性,这使得 Cilium 与传统的网络插件(如 Calico 和 Flannel)区别开来。

k8s 网络插件的工作模式

Flannel 的工作模式

Flannel 通过给每一个宿主机分配一个子网的方式为容器提供虚拟网络,它基于 Linux TUN/TAP , 使用 UDP 封装 IP 包来创建 overlay 网络,并借助 etcd 维护网络的分配情况

flannel 会为每一个节点分配一个独立的子网,每个容器获得该子网中的一个 ip 地址。flannel 使用不同的 backend 来实现 overlay 网络,常用的也就是 VXLAN 了,在 VXLAN 模式下flannel 会创建一个 vxlan0 的虚拟的隧道设备,在节点之间封装和解封数据包。容器通过节点之间的虚拟网卡通信,flannel 会将数据包封装到 VXLAN 隧道中通过底层物理网络进行传输

Calico 的工作模式

Calico 利用 linux 内核实现高效的虚拟路由器,每个虚拟路由器通过 BGP 将本地运行的工作负载的路由信息传播到整个 Calico 网络上,从而使各个节点上的工作负载能够实现可靠的网络通信

BGP 和 AS

BGP 是一种用于在互联网中交换路由信息的标准协议,它主要用在两个不同的自治系统之间交换路由信息,以帮助互联网中的路由器动态学习和选择最佳的路由路线。那什么又叫自治系统呢?

自治系统(Autonomous System,简称 AS)是指由一个活多个网络组成的,在互联网上有一个统一的管理和路由控制的区域

了解 BGP 之后就好了解 Calico 了,Calico 在每个节点运行一个名为 calico-node 的 pod , 其中包含以下三个守护进程

  • Felix: 负责配置和管理节点上的路由表以及网络策略规划。它与 linux 内核进行交互生成路由表和 iptables 规则,以实现集群网络通信和网络策略
  • BIRD: 是 BGP 协议的守护进程。它负责与其他节点的 BIRD 守护进程建立连接,并相互学习对方的路由表
  • confd: 是一个配置管理工具,用于动态生成和更新 Calico 组件和配置文件

在这三个插件中,Calico 是目前最流行的网络插件:
简单聊一聊 calico 的几个工作模式:

覆盖网络 VXLAN

在上述如何查看 calico 当前工作模式的命令返回的结果来看,如何判断目前 calico 使用的是 VXLAN 呢?其中 VXLANMODE 字段为 Always ,表示成功切换到 VXLAN 模式

VXLAN 封装数据包
calico 通过 VXLAN 将 pod1 的数据包封装成一个从本机网络接口 vxlan.calico 到目的节点上的网络接口 vxlan.calico 的二层数据帧,即本机网络接口 vxlan.calico 的 mac 地址作为源 mac 地址,目标节点上的网络接口 vxlan.calico 的 mac 地址作为目的 mac 地址。这个目的 mac 地址会被维护在本机 ARP 表中,可以通过以下命令查看:

ip neigh show dev vxlan.calico

接着 ,VXLAN 将内部数据帧封装到一个 UDP 数据包中,即内部数据帧的目的 mac 地址所在节点的 ip 地址,它被维护在本机的 FDB (转发数据库)中,可以通过以下命令查看

bridge fdb show dev vxlan.calico

该数据包还会添加 VXLAN 头部,以标识这是一个 VXLAN 数据包

数据包达到目标节点
在 UDP 数据包达到目标节点之后,发现它是一个 VXLAN 的数据包,于是使用 VXLAN 对 UDP 数据包进行解封装,以从中获取内部数据帧。根据内部数据帧的目标 ip 地址查找路由表,

覆盖网络 ipip

ipip 和 LXVAN 模式类似,但使用了不同的封装机制,ipip 封装技术是将原始 ip 数据包封装到一个新的 ip 数据包中。

查看 Calico 当前工作模式。其中 IPIPMODE 字段为 Always ,表示当前工作模式成功切换为 IPIP 模式了

在 IPIP 模式下,数据包是如何传递呢?

在 ipip 模式下。数据包的传递也还是隧道模式:
首先初始化数据包,假设 A 和 B 两个 pod 在不同的 k8s 集群的节点上,A 要向 B 发送一个数据包,当 A 节点通过 CNI 协议检测到 B 不在本节点上,它就会去查找路由表,通过 IPIP 隧道将数据包发送到 B 接点上,A 节点会在原始的数据包外再添加一个新的 IP 头,这个过程也就是 IPIP 的由来。其中外部的 ip 主要 AB 节点的IP 内层则是 AB pod 的 IP 。再数据包通过底层网络从 A 节点到 B 节点,底层的传输只关心外部 IP ,它负责将数据包传输到目的节点。在 B 节点接收到数据包之后会通过外部 IP 携带的信息得知目的地址是自己,然后 B 节点就会通过 CNI 移除外部的 IP 恢复原本的数据包。这就是 IPIP 的数据传递的过程

路由网络 BGP

相较于前两种模式,BGP 就简单很多,它将目标节点看作 BGP 网关,并直接通过节点网络转发数据包,IPIPMODE 和 VXLANMODE 的值都是 Never 就表示关闭了这两个模式,这个时候就意味着启动了 BGP 模式

总结

VXLAN 和 IPIP 他们不依赖低层网络环境,几乎可以在任何网络中使用。缺点就是数据包会经历两次封装和解包,这会导致一定的性能开销

BGP 的优点是动态学习路由,基于路由出发,具有良好的网络性能,但是它依赖于二层网络,当节点较多的场景下路由表会是巨大的

路由反射器

Calico 默认采用 BGP 模式,跟随上述,当节点数量庞大时,是一个不小的性能开销,因此 Calico 引入了路由反射器机制,通过在节点中选择几个节点作为路由反射器,其他的节点只需要将路由指向这些路由反射器与其建立 BGP 连接即可,这样可以有效的减少连接数的激增。

Cilium 的工作模式

Cilium 是一个基于 eBPF 和 XDP 的高性能容器网络方案,代码开源在 https://github.com/cilium/cilium

工作流程
eBPF 加载程序:cilium 将 eBPF 程序加载到 Linux 内核中,这些程序可以直接处理网络流量和容器数据包,接着就是通过 k8s的标签和服务信息来建立容器之间的网络连接。每个容器都会分配到一个 ip 地址,类似于 calico 的地址分发规则,当数据包通过 linux 内核的 eBPF 程序时,该程序会在内核层面对数据进行解析,过滤,路由,加密等操作。

eBPF 和 XDP

eBPF(extended Berkeley Packet Filter)起源于BPF,它提供了内核的数据包过滤机制。BPF的基本思想是对用户提供两种SOCKET选项:SO_ATTACH_FILTERSO_ATTACH_BPF,允许用户在sokcet上添加自定义的filter,只有满足该filter指定条件的数据包才会上发到用户空间。SO_ATTACH_FILTER插入的是cBPF代码,SO_ATTACH_BPF插入的是eBPF代码。eBPF是对cBPF的增强,目前用户端的tcpdump等程序还是用的cBPF版本,其加载到内核中后会被内核自动的转变为eBPF。Linux 3.15 开始引入 eBPF。其扩充了 BPF 的功能,丰富了指令集。它在内核提供了一个虚拟机,用户态将过滤规则以虚拟机指令的形式传递到内核,由内核根据这些指令来过滤网络数据包。

XDP(eXpress Data Path)为Linux内核提供了高性能、可编程的网络数据路径。由于网络包在还未进入网络协议栈之前就处理,它给Linux网络带来了巨大的性能提升。XDP 看起来跟 DPDK 比较像,但它比 DPDK 有更多的优点,如

  • 无需第三方代码库和许可

  • 同时支持轮询式和中断式网络

  • 无需分配大页

  • 无需专用的CPU

  • 无需定义新的安全网络模型

当然,XDP的性能提升是有代价的,它牺牲了通用型和公平性:(1)不提供缓存队列(qdisc),TX设备太慢时直接丢包,因而不要在RX比TX快的设备上使用XDP;(2)XDP程序是专用的,不具备网络协议栈的通用性。

三大插件的对比

特性 Cilium Calico Flannel
核心技术 基于 eBPF 的数据包处理和网络策略 BGP 路由策略。或者 IPIP / VXLAN 封装 VXLAN 或者其他的 Overlay 技术
层级支持 Layer3,4,7 Layer3,4 Layer3
网络模型 无需封装,直接在内核运行,支持透明加密 可以选择路由模式或者使用覆盖网络模式 以覆盖网络模式为主
网络策略支持 原生支持高级网络策略,支持服务到服务的颗粒度控制 原生支持 Layer3,4 的网络策略 需要与 Calico 或者其他平台插件配合使用
性能 极高,基于 eBPF ,避免用户态和内核态的切换,降低延迟 高,特别是在 BGP 模式下的性能最佳 隧道封装增加了额外的网络资源开销,性能相对较弱
服务发现和负载均衡 原生支持,可以代替 kube-proxy 不支持原生服务发现 不支持原生服务
安装与配置 相对复杂,需要内核支持 eBPF 需要配置 BGP 或者选择封装模式 开箱即用
加密支持 支持透明加密 支持透明加密,但是配置比前者较复杂 不支持
适用场景 高性能,复杂策略需求的集群;需要应用层可见性 高性能集群,需求是精细化网络策略的场景 小规模集群,网络功能需求简单的场景
可扩展性 适用于大规模集群 适用于大规模集群 适用于中小规模的集群
社区支持 活跃,快速增长,现代化解决方案 技术成熟,应用广泛 配置简单,功能也相对较少
上一篇:Django:从入门到精通


下一篇:Go语言中AES加密算法的实现与应用