Kubernetes 网络模型
{Back to Index}

Table of Contents

1 K8S 组网规范 1

Kubernetes 对所有网络设施的实施,需要满足以下的基本要求:

  • 节点上的 Pod 可以不通过 NAT 和其他任何节点上的 Pod 通信
  • 节点上的任何进程可以和节点上的所有Pod通信
  • 那些运行在节点的主机网络里的 Pod 可以不通过 NAT 和所有节点上的 Pod 通信 (Pod 自身的 IP 和其他 Pod 看到它的 IP 是一致的)

2 容器之间的通信

Pod 本质上是一组共享 Network Namespace 的容器,因此这些容器使用相同的 IP 地址和端口分配空间。

pod-namespace.png

Figure 1: 每个 Pod 使用单独的命名空间

3 Pod 之间的通信

在同一个主机上,Pod 之间的通信本质上是不同命名空间之间的通信,使用 Linux Virtual Ethernet Device 可以将不同的命名空间联结起来。

pod-veth-pairs.png

Figure 2: veth pair 将主机上的 Pod 与根命名空间联结起来

3.1 Linux Ethernet bridge

为了透过根命名空间使得 Pod 之间可以通信,需要使用 Linux Ethernet bridge ,它是虚拟二层网络设备,用于连接多个网段。

当网桥接收到一个数据帧,会广播 ARP 消息至所有与之相连接的设备,响应 ARP 消息的设备 MAC 会被记录于关联表中, 即网桥利用 ARP 协议将 MAC 与 IP 关联起来,后续相同 IP 的数据将根据关联表转发至对应的冲突域。

pods-connected-by-bridge.png

Figure 3: 通过网桥连接不同的命名空间

3.2 数据包的传递(单机)

pod-to-pod-same-node.gif

Figure 4: 数据包在同一主机内的 Pod 之间传递

Pod 1 将数据包发送至 eth0 ,该 eth0veth0 是一对 vethveth0 也会收到数据包 (1)。
网桥 cbr0veth0veth1 联结在一起,当数据包到达网桥后,通过 ARP 协议,被转发到 veth1 (3)。
veth1 上的数据包直接转发至 Pod 2 命名空间中的 eth0 (4)。

3.3 数据包的传递(跨主机)

通常,集群中的每个主机会被分配一个 CIDR block 用于指定该主机上 Pod 可以使用的 IP 地址范围。
当目标是某个 CIDR block 地址的数据包到达主机,该主机会将其转发至正确的主机(通过隧道技术),并进而转发至正确的 Pod 。 下图示意了数据包在不同主机间的传递(假设网络可以将属于某个 CIDR block 的数据转发至正确的主机)。

pod-to-pod-different-nodes.gif

Figure 5: 数据包在跨主机的 Pod 之间传递

Pod 1 中的数据会到达根命名空间中的网桥 cbr0 (2) ,此时,网桥上的 ARP 过程会失败,因为连在其上的设备没有一个可以响应。
接着数据会通过隧道技术的封装而进入网络 (3) ,并发送至正确的主机上 (4) ,出现在 VM 2 的根命名空间中 (eth0) 。
通过网桥,数据被路由至正确的虚拟设备 veth1 (5) 。最终,到达 eth0 (6) 。

4 Pod 与 Service 的通信

当创建 Service 时,cluster IP 也会被创建出来,在集群内部,流向 Cluster IP 的数据会以 Round Robin 的方式导向该 Service 的支撑 Pods , 即 K8S 内部利用这种机制实现了负载均衡的功能。

pod-to-service.gif

Figure 6: 数据从 Pod 发送至 Service

网桥会选择将数据包通过默认路由 eth0 发送出去 (3) 。
eth0 接收数据之前,IPTABLES 会对数据包进行处理,它会根据规则(这些规则是由 kube-proxy 负责维护的) 将数据包的目标 IP 从 service IP 改为具体某个 Pod 的 IP (4) 。

service-to-pod.gif

Figure 7: 数据从 Service 发送至 Pod

Service Pod 返回的数据包的源 IP 为它自己的地址,目标 IP 为发送方 Pod 的 IP 地址 (1) 。
当数据到达目标主机,借助于 conntrack 模块,IPTABLES 将源地址再改回 Service 的 cluster IP (2) 。

5 与 Internet 的通信

5.1 Pod 访问 Internet

pod-to-internet.gif

Figure 8: 数据包从 Pod 路由到 Internet

在数据包到达 eth0 前,IPTABLES 会先对其进行处理。假设数据包的源地址仍然为 Pod 的 IP ,该数据包会被 Internet 网关拒绝,因为网关 NAT 只接受 VM 的 IP 。 因此,IPTABLES 必须使用 SNAT ,即将源地址修改为 VM 的内网 IP ,这样,数据包就能离开 VM (4) 从而到达网关 (5) 。 接着,网关会再做一次 NAT :将源地址从 VM 的内部 IP 改为外部 IP ,最终数据包进入公网 (6) 。

返回的数据包则会依次执行相反的操作:将源地址修改回每一层可以理解的 IP ,即内网 IP ( VM 层),Pod IP ( Pod 命名空间层)。

5.2 Internet 访问 Service

从外网访问集群内部的 Service 通常有两种解决方案:

  1. 使用 LoadBalancer
  2. 使用 Ingress controller

5.2.1 LoadBalancer (L4)

创建 Service 时,可以指定一个 LoadBalancer ,LoadBalancer 一般由 云服务商 实现。Service 会将 IP 地址通知给 LoadBalancer 。

LoadBalancer 知道其背后所有 VM 的地址,并会将流量均匀的分布在这些主机上。当流量到达主机后,IPTABLES 再负责转发到正确的 Pod 。

internet-to-service.gif

Figure 9: 数据包从 Internet 发送到 Service

如上图所示,当 LoadBalancer 收到数据包后 (2) ,会 随机 选择一个 VM ,图中选择了无任何 Pod 运行的 VM2 (3) 。
VM2 上的 IPTABLES 再使用 NAT 将数据转发至正确的 Pod 上 (4) 。

5.2.2 Ingress controller (L7)

使用 Ingress 的前提是需要先创建 NodePort Service ,它也是由云提供商实现,工作在 7 层并感知 HTTP/HTTPS 协议,将 HTTP 请求映射到 Services 。
Ingress 和 L4 LoadBalancer 类似,只能知道 Node 的 地址,不能感知 Pod 的 IP 信息。 Ingress 和 Node 之间的连接由 Service 的 nodePort 决定。

5.2.2.1 AWS Ingress controller 设计 2

ingress-controller-design.png

Figure 10: AWS 的 Ingress controller 设计

Footnotes:

Author: Hao Ruan (ruanhao1116@gmail.com)

Created: 2020-02-23 Sun 17:22

Updated: 2021-08-17 Tue 11:23

Emacs 27.1 (Org mode 9.3)