引擎盖下的Kubernetes网络
介绍
Kubernetes 是一个强大的容器编排系统,可以跨服务器集群管理容器化应用程序的部署和运行。 除了协调容器工作负载之外,Kubernetes 还提供了必要的基础设施和工具来维持应用程序和服务之间的可靠网络连接。
Kubernetes 集群网络文档 指出 Kubernetes 网络的基本要求是:
* 所有容器都可以在没有 NAT 的情况下与所有其他容器通信
- 所有节点都可以在没有 NAT 的情况下与所有容器通信(反之亦然)
- 容器认为自己的 IP 与其他人认为的 IP 相同
在本文中,我们将讨论 Kubernetes 如何满足集群内的这些网络需求:数据如何在 Pod 内、Pod 之间以及节点之间移动。
我们还将展示 Kubernetes Service 如何为应用程序提供单个静态 IP 地址和 DNS 条目,从而简化与可能分布在多个不断扩展和移动的 Pod 之间的服务的通信。
如果您不熟悉 Kubernetes pods 和 nodes 的术语或其他基础知识,我们的文章 An Introduction to Kubernetes 涵盖了所涉及的一般架构和组件。
我们先来看看单个 pod 内的联网情况。
Pod 网络
在 Kubernetes 中,pod 是最基本的组织单元:一组紧密耦合的容器,它们都密切相关并执行单个功能或服务。
在网络方面,Kubernetes 将 Pod 视为类似于传统虚拟机或单个裸机主机:每个 Pod 接收一个唯一的 IP 地址,Pod 中的所有容器共享该地址并通过 lo 相互通信 环回接口使用 localhost 主机名。 这是通过将所有 pod 的容器分配到同一个网络堆栈来实现的。
对于在容器化时代之前已经在单个主机上部署多个服务的任何人来说,这种情况应该很熟悉。 所有服务都需要使用唯一的端口来监听,否则通信不复杂且开销低。
Pod 到 Pod 网络
大多数 Kubernetes 集群需要为每个节点部署多个 Pod。 Pod 到 Pod 的通信可能发生在同一节点上的两个 Pod 之间,也可能发生在两个不同节点之间。
一个节点上的 Pod 到 Pod 通信
在单个节点上,您可以拥有多个需要直接相互通信的 Pod。 在我们跟踪 pod 之间的数据包路由之前,让我们检查一下节点的网络设置。 下图提供了一个概述,我们将详细介绍:
每个节点都有一个网络接口——在本例中为 eth0——连接到 Kubernetes 集群网络。 该接口位于节点的 root 网络命名空间内。 这是 Linux 上网络设备的默认命名空间。
正如进程命名空间使容器能够将正在运行的应用程序相互隔离一样,网络命名空间可以隔离网络设备,例如接口和网桥。 节点上的每个 pod 都分配有自己的隔离网络命名空间。
Pod 命名空间通过 虚拟以太网对 连接回 root 命名空间,本质上是两个命名空间之间的管道,每端都有一个接口(这里我们使用 veth1 root 命名空间中的 ,以及 pod 中的 eth0)。
最后,Pod 通过网桥 br0 相互连接并连接到节点的 eth0 接口(您的节点可能使用 cbr0 或 之类的东西)码头工人0 )。 网桥本质上就像物理以太网交换机一样工作,使用 ARP(地址解析协议)或基于 IP 的路由来查找其他本地接口以将流量定向到。
现在让我们跟踪从 pod1 到 pod2 的数据包:
- pod1 创建一个以 pod2 的 IP 为目标的数据包
- 数据包通过虚拟以太网对传输到根网络命名空间
- 数据包继续到网桥br0
- 因为目的 pod 在同一个节点上,所以网桥将数据包发送到 pod2 的虚拟以太网对
- 数据包通过虚拟以太网对,进入 pod2 的网络命名空间和 pod 的 eth0 网络接口
现在我们已经跟踪了节点内从 pod 到 pod 的数据包,让我们看看 pod 流量如何在节点之间传输。
两个节点之间的 Pod 到 Pod 通信
因为集群中的每个 pod 都有一个唯一的 IP,并且每个 pod 都可以直接与所有其他 pod 通信,所以在两个不同节点上的 pod 之间移动的数据包与前面的场景非常相似。
让我们跟踪一个从 pod1 到 pod3 的数据包,它位于不同的节点上:
- pod1 创建一个以 pod3 的 IP 为目标的数据包
- 数据包通过虚拟以太网对传输到根网络命名空间
- 数据包继续到网桥br0
- 网桥找不到要路由到的本地接口,因此将数据包发送到默认路由,发往 eth0
- 可选: 如果您的集群需要网络覆盖以将数据包正确路由到节点,则数据包可能会在前往网络之前封装在 VXLAN 数据包(或其他网络虚拟化技术)中。 或者,可以使用适当的静态路由设置网络本身,在这种情况下,数据包会传输到 eth0 并在未更改的情况下离开网络。
- 数据包进入集群网络并被路由到正确的节点。
- 数据包在eth0上进入目的节点
- 可选:如果你的数据包被封装,此时将被解封装
- 数据包继续到网桥br0
- 网桥将数据包路由到目标 pod 的虚拟以太网对
- 数据包通过虚拟以太网对到达 pod 的 eth0 接口
既然我们已经熟悉了数据包是如何通过 pod IP 地址路由的,那么让我们来看看 Kubernetes services 以及它们是如何构建在这个基础设施之上的。
Pod 到服务网络
仅使用 pod IP 将流量发送到特定应用程序是很困难的,因为 Kubernetes 集群的动态特性意味着 pod 可以移动、重新启动、升级或缩小或缩小存在。 此外,一些服务会有很多副本,所以我们需要一些方法来平衡它们之间的负载。
Kubernetes 用 Services 解决了这个问题。 Service 是一个 API 对象,它将单个虚拟 IP (VIP) 映射到一组 pod IP。 此外,Kubernetes 为每个服务的名称和虚拟 IP 提供了一个 DNS 条目,因此可以通过名称轻松地对服务进行寻址。
集群内虚拟 IP 到 Pod IP 的映射由每个节点上的 kube-proxy
进程协调。 此过程设置 iptables 或 IPVS 以在将数据包发送到集群网络之前自动将 VIP 转换为 pod IP。 跟踪各个连接,以便数据包在返回时可以正确解译。 IPVS 和 iptables 都可以将单个服务虚拟 IP 负载均衡到多个 pod IP,尽管 IPVS 在它可以使用的负载均衡算法方面具有更大的灵活性。
注意:这个翻译和连接跟踪过程完全发生在Linux内核中。 kube-proxy 从 Kubernetes API 读取并更新 iptables ip IPVS,但它不在单个数据包的数据路径中。 这比以前版本的 kube-proxy 更有效,性能更高,后者充当用户级代理。
让我们再次遵循数据包从 pod pod1 到服务 service1 的路由:
- pod1 创建一个以 service1 的 IP 为目标的数据包
- 数据包通过虚拟以太网对传输到根网络命名空间
- 数据包继续到网桥br0
- 网桥找不到将数据包路由到的本地接口,因此数据包被发送到 eth0 的默认路由
- 由
kube-proxy
设置的 iptables 或 IPVS 匹配数据包的目标 IP,并使用可用或指定的任何负载平衡算法将其从虚拟 IP 转换为服务的 Pod IP 之一 - 可选: 您的数据包可能在此时被封装,如上一节所述
- 数据包进入集群网络并被路由到正确的节点。
- 数据包在eth0上进入目的节点
- 可选:如果你的数据包被封装,此时将被解封装
- 数据包继续到网桥br0
- 数据包通过 veth1 发送到虚拟以太网对
- 数据包通过虚拟以太网对并通过其 eth0 网络接口进入 pod 网络命名空间
当数据包返回到 node1 时,VIP 到 pod IP 的转换将被反转,数据包将通过桥接器和虚拟接口返回到正确的 pod。
结论
在本文中,我们回顾了 Kubernetes 集群的内部网络基础设施。 我们已经讨论了构成网络的构建块,并详细说明了数据包在不同场景中的逐跳旅程。
有关 Kubernetes 的更多信息,请查看 我们的 Kubernetes 教程标签 和 Kubernetes 官方文档 。