34. K8s 网络原理——基本概念

本章讲解知识点

  • Kubernetes 网络模型
  • 网络命名空间
  • veth 设备对
  • 网桥
  • iptables 和 netfilter
  • 路由
  • docker 的网络实现
  • docker 网络的局限性

<br>

1. Kubernetes 网络模型

Kubernetes 的网络模型设计是为了简化容器编排中的网络配置,并提供一种容器之间直接通信的方式。这个网络模型的核心原则是每个 Pod 都拥有一个独立的 IP 地址,而且所有 Pod 都在一个扁平的、直接连通的网络空间中。这意味着不管 Pod 运行在哪个节点上,它们都可以通过彼此的 IP 直接访问。这个原则的设计使得用户不需要额外考虑如何建立 Pod 之间的连接,也不需要考虑如何将容器端口映射到主机端口等问题。

在 Kubernetes 中,IP 地址是按照 Pod 为单位进行分配的,这意味着每个 Pod 内部的所有容器都共享同一个网络堆栈,相当于在一个网络命名空间中。在这个模型中,每个 Pod 都有一个唯一的 IP 地址,也被称为 IP-per-Pod 模型。

由于 Kubernetes 的网络模型假定 Pod 之间使用的是对方 Pod 的实际地址进行访问,因此,一个 Pod 内部的应用程序看到的 IP 地址和端口号与集群中的其它 Pod 看到的是一样的,都是 Pod 实际分配的 IP 地址。这样,IP 地址和端口号在 Pod 内部和外部都保持一致,无需进行地址转换。Kubernetes 采用这种 IP-per-Pod 的方案,以便兼容现有的各种域名解析和发现机制。

对于每个 Pod,都会分配一个独立的 IP 地址,而同一个 Pod 内的容器将共享该 IP 地址和网络命名空间,即 Linux 网络协议栈。这意味着同一个 Pod 内的容器可以通过 localhost 连接对方的端口。这种模型类似于同一虚拟机内的进程之间的通信,Pod 内部容器之间的隔离性相对较低,而且它们使用的端口号也是共享的,因此不存在私有端口的概念。如果某个应用程序需要使用特定的端口范围,可以将该应用程序部署在一个单独的 Pod 中。对于那些不需要特定端口范围的应用程序,它们可以通过共享资源进行通信,这样可以提高通信效率和易用性。尽管这种方法会损失一定的隔离性,但对于许多应用程序而言,这是一个值得接受的折衷方案。

从网络端口分配、域名解析、服务发现、负载均衡、应用程序配置和迁移的角度来看,可以将 Pod 视为一台独立的虚拟机或物理机。因此,IP-per-Pod 模型是一种简单、易于兼容的模型。

1.1 Kubernetes 对集群网络要求

  • 所有 Pod 都可以在不用 NAT 方式同别的 Pod 通信
  • 所有节点上的代理进程都可以在不用 NAT 方式同所有 Pod 通信
  • 以 hostnetwork 模式运行的 Pod 都可以在不用 NAT 方式同所有 Pod 通信

这些基本要求意味着并不是只要两台机器都运行 Docker,Kubernettes 就可以工作了。具体的集群网络必须满足上述基本要求,原生的 Dokcer 网络目前还不能很好的支持这些要求

如果原先的应用程序是在虚拟机上运行,每个虚拟机都拥有独立的 IP 地址,且它们之间可以透明地通信,那么 Kubernetes 的网络模型就可以和虚拟机使用相同的网络模型。这种模型可以轻松地将现有的应用程序从虚拟机或物理机迁移到容器上。

在部署 Kubernetes 和 Docker 集群之前,需要创建符合 Kubernetes 要求的网络环境。许多开源工具可以帮助我们打通 Docker 容器之间的网络,以实现满足 Kubernetes 要求的网络模型。

<br>

2. 网络命名空间

Linux 网络命名空间(network namespace)是 Linux 内核提供的一种网络隔离机制,可以让不同的进程或容器拥有独立的网络栈、IP 地址、路由表、网络设备、防火墙等网络资源,从而实现网络的隔离和虚拟化。每个网络命名空间都有自己的网络设备(例如虚拟网卡)、IP 地址、路由表等网络资源,网络命名空间之间是完全隔离的,彼此之间的网络资源互不影响。

利用网络命名空间,可以实现多个独立的网络环境在同一台物理主机上并存,例如在一台服务器上同时运行多个不同的容器、虚拟机等,每个容器或虚拟机都拥有自己独立的网络环境,彼此之间相互隔离。

网络命名空间是 Linux 内核提供的一个非常强大的网络隔离机制,Docker 等容器技术正是利用了这个机制来实现容器之间的网络隔离和虚拟化,从而保证容器之间的网络资源和网络协议栈是完全隔离的。

2.1 网络命名空间的实现

将与网络协议栈相关的全局变量变成一个 Net Namespace 变量的成员,然后再调用协议栈函数,加入一个 Namespace 参数。这就是 Linux 实现网络命名空间的核心。

在建立新的网络命名空间,并将某个进程关联到这个网络命名空间后,就出现了类似下图的内核数据结构,所有网络栈变量都被放入了网络命名空间的数据结构中。这个网络命名空间是其进程组私有的,和其他进程不冲突。

在新创建的私有命名空间中,只有回环设备(名为“lo”且处于停止状态),其他设备默认都不存在,若需要则需要手动创建。相反,Docker 容器中的各类网络栈设备都是 Docker daemon 在启动时自动创建和配置的。

所有网络设备(包括物理的和虚拟的接口、桥等,在内核里都叫作 Net Device)都只能属于一个命名空间。通常情况下,物理设备(连接实际硬件的设备)只能关联到 root 命名空间中,而虚拟网络设备(如虚拟以太网接口或虚拟网口对)可以被创建并关联到一个指定的命名空间中,且可在这些命名空间之间移动。

正如前面提到的,由于网络命名空间代表的是独立的协议栈,因此它们之间相互隔离,彼此无法通信,甚至在协议栈内部都看不到对方。但是,有一种称为 veth 设备对的技术可以打破这种限制,使不同命名空间中的网络相互通信,甚至与外部网络进行通信。接下来,我们会对这种设备进行详细讲解。

2.2 对网络命名空间的操作

下面列举一些对网络命名空间的操作

  • 创建一个命名空间:

ip netns add <name>

  • 在命名空间中运行命令

ip netns exec <name> <command>

  • 进入命名空间

ip netns exec <name> bash

<br>

3. veth 设备对

veth 设备对是 Linux 内核中一种特殊的虚拟网络设备,它被用于连接两个网络命名空间。一个 veth 设备对是由两个虚拟网络接口组成的,它们成对出现,一端连接到一个命名空间中,另一端连接到另一个命名空间中,可以通过这个设备对实现两个命名空间之间的通信。所以 veth 设备都是成对出现的,很像一对以太网卡,并且中间有一根直连的网线

veth 设备对通常用于容器网络,每个容器都会有一个私有的命名空间,通过使用 veth 设备对,容器内部的网络栈与主机上的网络栈相隔离,容器内部的网络流量也可以通过主机上的网络进行转发和管理,从而实现容器与外界网络的通信。

veth 设备对一端的设备需要关联到一个命名空间中,而另一端的设备可以在另一个命名空间中,也可以在主机的全局命名空间中。在 Docker 中,通过在宿主机上创建一个虚拟网桥,然后把一个端口连接到该网桥,另一个端口连接到容器所在的命名空间中,就可以实现容器之间的通信。

下图是 veth 设备对示意图。

3.1 对 veth 设备对的操作

  • 创建 veth 设备对:

ip link add veth0 type veth peer name veth1

创建后,可以查看 veth 设备对的信息。使用 ip link show 命令查看所有网络接口:

[root@node1 netns]# ip link show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
...
5: cni0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP mode DEFAULT group default qlen 1000
    link/ether 76:ab:d3:a0:1a:e4 brd ff:ff:ff:ff:ff:ff
...
11: veth1@veth0: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether fa:7a:4b:9d:46:d9 brd ff:ff:ff:ff:ff:ff
12: veth0@veth1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 26:4c:42:3d:30:30 brd ff:ff:ff:ff:ff:ff

可以看到有两个设备生成了,一个是 veth0,他的 peer 是 veth1.

现在这两个设备都在一个命名空间中,如果将 veth 看作有两个头的网线,那么我们将另一个头甩另一个命名空间:

ip netns add netns1
ip link set veth1 netns netns1

[root@node1 netns]#  ip link show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
...
5: cni0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP mode DEFAULT group default qlen 1000
    link/ether 76:ab:d3:a0:1a:e4 brd ff:ff:ff:ff:ff:ff
...
12: veth0@if11: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 26:4c:42:3d:30:30 brd ff:ff:ff:ff:ff:ff link-netnsid 2

只剩一个 veth0 设备了,另一个设备已经看不到,说明被转移到另一个网络命名空间了。

网络命名空间被存在目录下:/var/run/docker/netns/

此时使用 ip netns 是无法查看容器的网络命名空间,需要将容器网络命名空间映射出来。

ln -s /var/run/docker/netns /var/run/netns

进入目录:/var/run/netns

[root@node1 netns]# ip netns list
netns1 (id: 2)
7b1c91aee4bd (id: 1)
d3134fca8796 (id: 0)
default

[root@node1 netns]# ip netns exec netns1 ip link show
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
11: veth1@if12: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether fa:7a:4b:9d:46:d9 brd ff:ff:ff:ff:ff:ff link-netnsid 0

可以看见 netns1 网络命名空间,并可以在该命名空间内看到 veth1 设备。

我们当前看到的效果就是,两个不同的命名空间各自有一个 veth 的“网线头”,各显示为一个 device(在docker的实现里面,它除了将 veth 放入容器内,还将它的名字改为了 eth0,以假乱真)。

接下来我们还要为网线头添加 ip 才能真正实现通信。

[root@node1 netns]# ip netns exec netns1 ip addr add 10.104.1.2/24 dev veth1
[root@node1 netns]# ip addr add 10.104.1.3/24 dev veth0

再启动它们:

[root@node1 netns]# ip netns exec netns1 ip link set dev veth1 up
[root@node1 netns]# ip link set dev veth0 up

现在两个命名空间可以相互通信了:

[root@node1 netns]# ping 10.104.1.2
PING 10.104.1.2 (10.104.1.2) 56(84) bytes of data.
64 bytes from 10.104.1.2: icmp_seq=1 ttl=64 time=0.692 ms
64 bytes from 10.104.1.2: icmp_seq=2 ttl=64 time=0.133 ms
64 bytes from 10.104.1.2: icmp_seq=3 ttl=64 time=0.221 ms
^C
--- 10.104.1.2 ping statistics ---
3 packets transmitted, 3

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

云计算面试题全解析 文章被收录于专栏

本专刊适合于立志转行云计算的小白,有一定的编程、操作系统、计算机网络、数据结构、算法基础。 本专刊同时也适合于面向云计算(Docker + Kubernetes)求职的从业者。 本专刊囊括了云计算、VMWare、Docker、Kubernetes、Containerd等一系列知识点的讲解,并且最后总

全部评论

相关推荐

10-28 14:42
门头沟学院 Java
Charles16:你去干的绝对是运维或dba
点赞 评论 收藏
分享
评论
点赞
8
分享
牛客网
牛客企业服务