写点什么

K8s 网络基本原理

作者:陈一之
  • 2024-11-04
    广东
  • 本文字数:5939 字

    阅读完需:约 19 分钟

随着 IT 基础设施的发展,应用的部署形态发生了很大的变化,从集中式到分布式,从物理机部署到虚拟机部署,再到现在的容器化部署,应用间的通信也从经历了从物理机网络,到虚拟机网络,再到容器集群网络的发展过程。

容器技术本质上是一种虚拟化技术,容器网络也依托网络虚拟化技术而呈现丰富的多样性,在这里特别地,指 Kubernetes 的网络模型。


连通场景

K8s 对 Pod 如何接入网络有着明确规定,简单来说就是每个 Pod 都有一个 IP,而且内部获取和外部通信相同,这就要求一个无需 NAT 转换的扁平网络,体现在 Pod 和 Node 的相互通信上。

在 K8s 的设计规范里,提出了 4 个需要解决的网络问题,分别是 Container-to-Container、Pod-to-Pod、Pod-to-Service 和 External-to-Internal。在实际中,由于通信端点所在位置不同(如是否同一主机、是否集群内外)和访问方式不同(如 PodIP、ClusterIP、ExternalIP),场景会更多一些。此外,Pod 网段、Node 网段和 Service 网段各不相同,也增加了网络连通的复杂性。

上图基于 K8s 的结构,简要归纳了主要的端到端互联场景,这些场景最终的连通路径和方式可能会有重叠的地方,但作为行为分类有不同的代表意义。

  • 主机内

  • (1)Pod 内容器互通

  • (2)主机内 Pod 互通

  • (3)主机内访问 Pod

  • (4)Service 的后端节点在相同主机

  • 集群内

  • (5)主机之间互通

  • (6)不同主机 Pod 互通、Service 的后端节点在不同主机

  • 集群外

  • (7)集群内访问集群外服务

  • (8)集群外访问集群内服务


基本原理

网络的拓扑结构一般是个有向图,连通的节点间路径长度不定,所以我们也可以说网络是分段的,分段包含的意思是数据包下一跳被送到哪里,从这个角度来说,需要探明的是什么设备(物理的或虚拟的)承担了怎样的中转(或转发)职能,在这基础上,又还有隔离(如地址、端口冲突)和可见性(如路由转换和 IP 抓取)问题。

在 K8s 中,要求 Pod 有独立 IP,实际上是把 Pod 当作一个小型主机来看待,这也是传统应用迁移到容器的便捷性的一个来源,在网络方面,隔离就是指网络栈的隔离了,网络栈包括有接口、设备、路由表、iptables 规则等,主要是通过网络命名空间(Network Namespace)来实现的。

Pod 内容器互通

每个 Pod 对应不同的网络命名空间,拥有各自的网络栈,端口相同也不会冲突,IP 和 MAC 地址由逻辑网络管理分配,与同一主机内的情况类似,Pod 内的容器通过 localhost 即可实现互相访问,由虚拟的环回接口(Loopback Interface)转发到对应端口。

主机内 Pod 互通

根据前面网络分段的说法,主机内不同 Pod 的容器服务之间互通实际也就是主机内的 Pod 互通,只要流量能到达 Pod 的虚拟网卡上,自然就能发送到对应端口的容器里。

在传统的分层网络认知里,数据包流转由二层/三层设备根据一定的协议和规则传递,除了网卡(NIC,Network Interface Card/Controller)外,核心设备有交换机(Switch)和路由器(Router),算上逻辑功能还有网桥(Bridge)和网关(Gateway)等,了解虚拟网络很大程度上也就是了解使用哪些虚拟设备。

一般来说,主机内的 Pod 应该在二层互通,不需要三层路由出去再回来,从物理机的连接方式来说,可以用网线直连两台主机的网卡,也可以通过交换机或网桥来连通,虚拟网络也相应模拟这些行为。


Linux Veth

Linux Veth(Virtual Ethernet)是 Linux 内核中的一种虚拟网络设备,它常用于创建虚拟网络接口对,即 Veth Pair,可以将 Veth Pair 的一端放在一个网络命名空间中,另一端放在另一个网络命名空间中,实现不同命名空间之间的网络通信。

Linux Veth 是同一主机内 Pod 互通的一种基本方式,上图创建了 veth-1-a/veth-1-b 这一组 Veth Pair,两端分别插(设置)到 P1 和 P2 网络命名空间(NS)中。形象上有点像用网线把 P1 和 P2 这两台带网卡的机器直连了起来,实际上是将 Veth 设备作为 Pod 的虚拟网卡,根据 Pod CIDR 网段设置 IP ,流量经由这张网卡时,可以通过 ARP 协议问询到对端 MAC 地址,实现数据包传递。

这种点对点的连接方式在多个 Pod 的场景就不太适用了,多个设备互联一般需要交换机类的角色进行集成中转,Linux 内核也有相应的虚拟设备。


Linux Bridge

Linux Bridge 是一种在 Linux 内核中实现的虚拟网络设备,它在数据链路层(二层)上工作,用于连接多个网络接口,包括物理和虚拟接口。

Linux Bridge 模拟的是物理交换机的功能,如上图的 br0,虚拟网桥设备创建在根网络命名空间(Root NS)下,除了可以连通不同网络命名空间中的 Pod,也是容器连接宿主机(Node)网络的桥梁,与前面直连两个 Pod 不同,这里 Veth Pair 的另一端连接到虚拟网桥的接口上,就像将网线插在交换机上一样,各个 Pod 仍可以通过 ARP 协议实现通信。


MACvlan、IPvlan

MACvlan 和 IPvlan 都是 Linux 内核中的网络虚拟化技术,它们允许多个虚拟接口共享同一个物理网络接口,简单地理解就是可以把一张物理网卡虚拟成多张子网卡。

MACvlan 每个虚拟接口都分配不同的 MAC 地址,IPvlan 每个虚拟接口则与物理接口共享 MAC 地址,两者没有绝对的替代关系,得看具体网络环境的要求,例如 MAC 数量限制和 IP 分配规则等。

MACvlan 能提供接近原生物理设备的性能,IPvlan 也依赖底层父接口处理 ARP 等广播报文,三层工作模式下能直接进行路由转发,减少网络栈开销,一般情况下性能均好于单纯的虚拟设备。

MACvlan 工作在数据链路层,IPvlan 支持二层(L2)和三层(L3)工作模式,另外都还支持不同的类型(mode flags),如 bridge、private、VEPA 等,总的来说,除了三层必要路由配置外,子接口的连通性归根结底是谁来转发数据包,这个无论是二层还是三层都是类似的:

  • 在 bridge 模式下,子接口可以直接通信,由父接口承担交换机或路由器的角色,如图绿色线条所示。

  • 其他不支持子接口直接通信的模式,就由父接口将流量转到外部设备,再通过外部设备寻址或路由转发回来,如图蓝色和紫色线条所示。特别地,外部设备不一定是指主机外的设备,主机内部互通使用前面提到的虚拟网桥也可以。

另外,一般情况下交换机和网桥设备是不支持将流量发回同一接口的,例如受生成树协议(STP)的回环限制,在虚拟化场景物理接口存在复用,所以外部二层设备需要打开发卡模式(hairpin mode),允许流量发回同一接口,以保障子接口间能正常通信。

主机内访问 Pod

主机内访问 Pod 也可以说是如何实现宿主机和容器互通,主机网段和容器网段一般来说是不同的,当然也不是强制要求,即使二者的 CIDR 是相同网段,这个场景也类比于跨网互通,所以问题重点在于连接两边的网关角色。

主机内部也是跨网络命名空间互通场景,基本方式就是找一个既能连通根空间(主机默认的网络命名空间),也能连通容器空间(Pod 所在的网络命名空间)的“设备”,并在宿主机和 Pod 内分别配置跳转路由。

虚拟网桥比物理网桥更灵活,能给它设置 IP 用作网关,配合路由规则不仅可以实现本机和容器网络互通,也可以实现虚拟网络跨主机互通,如上图绿色线条:

  • Pod 内,默认流量经网桥转发,如“default via 10.0.0.1 dev eth0 ……”

  • 主机内,目标为容器的流量经网桥转发,如“10.0.0.0/24 dev br0 ……”

MACvlan 和 IPvlan 的父接口和子接口网络隔离,可以在父接口的网络空间下创建另一个子接口(如上图 eth1),通过它来转发实现主机和容器互通(需要 bridge 模式),主要是从宿主机能到达容器,如上图蓝色线条:

  • 主机内,配置访问容器的路由,如“10.0.0.102 dev eth1 ……”

Pod 访问 Service

Service 可以说是 Pod 虚拟的负载均衡,也是虚拟的反向代理,访问一个 IP 实际导向一组后端 IP,常规做法是中间人代理模式(例如 Nginx),在主机内示意如下图。

中间人模式将原本“客户端-服务器”的连接拆成两段,分别是“客户端-代理”和“代理-服务器”,“实体的中间人”有两个明显的问题,一是会占用端口,需要特别规划避免与业务冲突,二是数据会在用户态和内核态复制,容易导致性能瓶颈。

另外,Service 的访问 IP 是虚拟 IP(如 ClusterIP),不与任何设备关联,无法监听,中间代理的方案要行得通需要将 ServiceIP 转成 localhost,也就是 DNAT(Destination Network Address Translation,目标网络地址转换),这是网络地址转换(NAT)的一种形式,相对的还有 SNAT(Source Network Address Translation,源网络地址转换)。

不过,既然 DNAT 能够修改连接目标,也就没必要多一个代理节点,直接修改成可达的后端 Pod 地址,再加上负载均衡规则就实现主机访问 Service 的功能了,这是由内核提供的。


netfilter

netfilter 是 Linux 内核中的一个网络包过滤和处理的框架,所有的流量都会经过当前网络栈的 netfilter 处理器,其中定义了五个处理节点(钩子,hook points),如下图。


iptables

iptables 是 netfilter 的用户空间接口,用于配置和管理网络数据包,iptables 有五个内置的规则表,通常主要使用以下四张表:

  • Filter 表:这是默认的表,用于控制数据包的访问控制,即决定哪些数据包可以进入、离开或穿过系统。它包含三条内置链:INPUTFORWARDOUTPUT

  • NAT 表:用于网络地址转换,它可以在数据包进入(PREROUTING链)、离开(POSTROUTING链)系统或在系统内部路由(OUTPUT链)时修改数据包的源或目的地址。

  • Mangle 表:用于修改数据包的内容,如调整 TTL 值、设置 QoS 标记等。它在数据包的处理流程中的多个点上都有作用,包括 PREROUTINGINPUTFORWARDOUTPUTPOSTROUTING链。

  • Raw 表:用于决定数据包是否被连接跟踪系统跟踪,这通常用于某些需要绕过连接跟踪的场景。它主要在PREROUTINGOUTPUT链上使用。

除了这四张常用的表,还有:

  • Security 表:这是一个较少使用的表,它与 SELinux 集成,用于强制访问控制策略。

对于前述中间人的例子,如果使用 iptables 来做,就是添加 NAT 表规则,在OUTPUT链中实现 DNAT 转换。

iptables 的规则匹配是链表式的,时间复杂度为 O(n),所以在大规模集群,也就是包含大量规则的情况下,性能可能会受影响。除此之外,集群规则需要推送到每个 Node,规则变更的时间复杂度接近 O(n^2)。


IPVS

IPVS(IP Virtual Server)是一个构建在 Linux 内核中的高性能负载均衡器,它通过虚拟 IP 地址(VIP)将网络请求分发到多个后端服务器上,从而实现负载均衡。IPVS 也是在 netfilter 上构建的,设计时面向水平扩展,采用类哈希表的数据结构,查找时间复杂度接近 O(1),规则变更复杂度也比 iptables 相应降低一个量级,性能相对较好。

Service 的虚拟 IP 一般情况下是 ping 不通的,不过 IPVS 会创建一个虚拟接口,能够处理 ICMP 指令,如果采用的是这个模式,Service 的虚拟 IP 就可以 ping 通。


eBPF

eBPF(Extended Berkeley Packet Filter)是一种革命性的技术,起源于 Linux 内核,能够在操作系统内核中安全有效地运行沙盒程序。它允许开发者在不更改内核代码的前提下,实时获取和修改操作系统的行为,从而扩展内核的功能。eBPF 程序经过内核的验证器校验,确保安全执行,并且可以利用即时编译器(JIT)实现接近直接运行机器码的速度。


eBPF 的工作原理基于事件驱动,可以在内核或应用程序经过特定的钩子点时运行程序。这些钩子包括系统调用、函数入口/退出、内核跟踪点、网络事件等,也可以通过创建内核探针(kprobe)或用户探针(uprobe)来附加 eBPF 程序。

简单来说,eBPF 能够通过可编程的方式在内核态植入自定义逻辑,除了扩展性和函数丰富程度外,还有两点明显的优势:

  • eBPF 能够在连接级别(socket level)进行 NAT,建连后不需要每个包都过滤,对比 iptables 和 IPVS 包级别(packet level)的重复拦截,显然性能更好。

  • iptables 和 IPVS 一般配置在宿主机网络命名空间下,Pod 访问 Service 的基本方式是想办法先转到宿主机网络命名空间,再通过 netfilter 进行地址转换,这种情况下,一方面会多转一次网络协议栈,另一方面对 Pod 流量不经过宿主机网络空间的连网方式不好支持。eBPF 可以配置在任何网络命名空间中,包括宿主机和容器的网络命名空间,相对来说更加灵活。

跨主机 Pod 互通

网络包的投递方式有点像是邮局,对于民政部门正式确立的官方地址,如“XX 市 XX 区 XX 街道 XX 号”,各地邮局能根据系统指引正常中转,邮递员也能准确地送上门,但是对于当地某些非正式的地址,如“XX 村 XX 山北面王二家”,就得依靠熟识的老乡带路才行了。

主机内的容器互通是靠官方还是要靠老乡,对应 Underlay 和 Overlay 两种不同的网络方案。

  • Underlay 网络:Underlay 网络就是我们理解的传统网络,也是物理网络,通过物理或光纤电缆连接网络设备和服务器,包括交换机和路由器等设备,借助以太网协议、路由协议和 VLAN 协议等驱动。

  • Overlay 网络:Overlay 网络是建立在现有的 Underlay 网络之上的一种虚拟网络技术,通过在物理网络上封装数据包,能够创建一个分离的、逻辑上的网络层。

不难想象,Underlay 方案性能更好,但对物理网络有侵入,Overlay 方案则相反,主要对比如下。

其中,VXLAN(Virtual Extensible LAN,虚拟可扩展局域网)是一种网络虚拟化技术,它通过在现有的 IP 网络上创建虚拟网络,允许数据中心实现大规模的网络隔离和扩展。VXLAN 使用 MAC-in-UDP 封装技术,将第二层的以太网帧封装在第三层的 IP 包中,实现跨越物理网络边界的虚拟网络通信。

VXLAN 的核心组件包括 VTEP(VXLAN Tunnel Endpoint,VXLAN 隧道终端),它负责封装和解封 VXLAN 数据包,以及 VXLAN 隧道,即 VTEP 之间的逻辑通道用于传输 VXLAN 数据包。VXLAN 数据包结构包括外层以太网头、外层 IP 头、外层 UDP 头、VXLAN 头(包含 VNI 和其他控制信息)和内层以太网帧。

VXLAN 的工作原理是,当主机发送数据包时,源 VTEP 将数据包封装在 VXLAN 头和外层 IP、UDP 头中,然后发送到目标 VTEP。封装后的 VXLAN 数据包通过物理网络传输,目标 VTEP 接收到数据包后解封,恢复出原始的以太网帧并发送到目标主机。

常规的 Overlay 方案,如 VXLAN,需要额外协议解封装,性能往往较差,如果宿主机之间本身二层互通,可以在宿主机配置路由规则,将宿主机当作容器互通的网关,通过直接转发的方式减少性能损失,如下图。

无论采用哪种模式,避免不了的一个问题是节点和容器是动态加入集群的,如何维护路由表是一项大的挑战,一种方式是利用控制面的 etcd 来存储和通知,通过在 Node 上部署的后台进程来更新本地路由,另一种方式是利用路由学习协议,例如 BGP(边界网关协议,Border Gateway Protocol),进行路由表数据交换。

容器访问集群外

在这里,容器访问集群外特指访问公网,实际上是私有网络访问公共网络的问题,基本方法也是在当地讲当地话,IP 需要在指定域内可路由,一般需要一台能通公网的 NAT 网关,如下图所示。

集群外访问容器

从前面的内容可以知道,集群外访问容器只要流量能到达宿主机,就有办法可以转到指定容器,这里主要介绍一下 NodePort。

NodePort 是 K8s 中的一种服务(Service)类型,它允许通过集群中每个节点的特定端口来访问服务。也就是说 NodePort 服务会在所有节点上打开一个指定的端口(或随机选择一个端口,如果未指定),任何发往该端口的流量都会被转发到服务后端的 Pod。

基本原理如前“Pod 访问 Service”所述,通过 netfilter 或 eBPF 拦截转发,流量示意如下图。


发布于: 刚刚阅读数: 5
用户头像

陈一之

关注

靡不有初,鲜克有终 2017-10-19 加入

让时间流逝

评论

发布
暂无评论
K8s网络基本原理_Kubernetes_陈一之_InfoQ写作社区