追踪 Kubernetes 中的网络流量

31f2b88bf69d894346b178451a32527b.gif

作者 | Addo Zhang

来源 | 云原生指北

译者注:

这篇文章很全面的罗列出了 Kubernetes 中涉及的网络知识,从 Linux 内核的网络内容,到容器、Kubernetes,一一进行了详细的说明。

文章篇幅有点长,不得不说,网络是很复杂很麻烦的一层,但恰恰这层多年来变化不大。希望翻译的内容对大家能有所帮助,有误的地方,也欢迎大家指正。

本文翻译获得 Learnk8s 的授权,原文 Tracing the path of network traffic in Kubernetes[1] 作者 Kristijan Mitevski。


0e4f7c66036a78dbab737659f2d9bd75.png

TL;DR: 本文将代理了解 Kubernetes 集群内外的数据流转。从最初的 Web 请求开始,一直到托管应用程序的容器。

目录

  • Kubernetes 网络要求

  • Linux 网络命名空间如果在 pod 中工作

  • Pause 容器创建 Pod 中的网络命名空间

  • 为 Pod 分配了 IP 地址

  • 检查集群中 pod 到 pod 的流量

  • Pod 命名空间连接到以太网桥接器

  • 跟踪同一节点上 pod 间的流量

  • 跟踪不同节点上 pod 间的通信

    • 位运算的工作原理

  • 容器网络接口 - CNI

  • 检查 pod 到服务的流量

  • 使用 Netfilter 和 Iptables 拦截和重写流量

  • 检查服务的响应

  • 回顾

Kubernetes 网络要求

在深入了解 Kubernetes 中的数据流转之前,让我们先澄清下 Kubernetes 网络的要求。

Kubernetes 网络模型定义了一套基本规则:

  • 集群中的 pod 应该能够与任何其他 pod 自由通信,而无需使用网络地址转换(NAT)。

  • 在不使用 NAT 的情况下,集群节点上运行的任意程序都应该能够与同一节点上的任意 pod 通信

  • 每个 pod 都有自己的 IP 地址(IP-per Pod),其他 pod 都可以使用同一个地址进行访问。

这些要求不会将实现限制在单一方案上。

相反,他们概括了集群网络的特性。

在满足这些限制时,必须解决如下挑战[2]

  1. 如何保证同一 pod 中的容器间的访问就像在同一主机上一样?

  2. Pod 能否访问集群中的其他 pod?

  3. Pod 能否访问服务(service)?以及服务可以负载均衡请求吗?

  4. Pod 可以接收来自集群外的流量吗?

本文将专注于前三点,从 pod 内部网络或者容器间的通信说起。

Linux 网络命名空间如果在 pod 中工作

我们想象下,有一个承载应用程序的主容器和另一个与它一起运行的容器。

在示例 Pod 中有一个 Nginx 容器和 busybox 容器:

apiVersion: v1
kind: Pod
metadata:name: multi-container-pod
spec:containers:- name: container-1image: busyboxcommand: ['/bin/sh', '-c', 'sleep 1d']- name: container-2image: nginx

在部署时,会出现如下情况:

  1. Pod 在节点上得到自己的网络命名空间

  2. Pod 分配到一个 IP 地址,两个容器间共享端口。

  3. 两个容器共享同一个网络命名空间,在本地互相可见。

网络配置在后台很快完成。

然后,我们退后一步,是这理解为什么上面是容器运行所必须的。

在 Linux 中,网络命名空间是独立的、隔离的逻辑空间。[3]

可以将网络命名空间堪称将物理网络接口分割成更小的独立部分。

每部分都可以单独配置,并使用自己的网络规则和资源。

这些可以包括防火墙规则、接口(虚拟或物理)、路由和其他所有与网络相关的内容。

  1. 物理接口持有根命名空间。

  2. 可以使用 Linux 网络命名空间创建隔离的网络。每个网络都是独立的,除非进行配置否则不会与其他命名空间通信。

物理接口必须处理最后的所有真实数据包,因此所有的虚拟接口都是从中创建的。

网络命名空间可以通过 `ip-netns` 管理工具[4] 来管理,可以使用 ip netns list 列出主机上的命名空间。

请注意,创建的网络命名空间将会出现在 /var/run/netns 目录下,但 Docker 并没有遵循这一点[5]

例如,下面是 Kubernetes 节点的命名空间:

$ ip netns list
cni-0f226515-e28b-df13-9f16-dd79456825ac (id: 3)
cni-4e4dfaac-89a6-2034-6098-dd8b2ee51dcd (id: 4)
cni-7e94f0cc-9ee8-6a46-178a-55c73ce58f2e (id: 2)
cni-7619c818-5b66-5d45-91c1-1c516f559291 (id: 1)
cni-3004ec2c-9ac2-2928-b556-82c7fb37a4d8 (id: 0)

注意 cni- 前缀意味着命名空间的创建由 CNI 来完成。

当创建 pod 并分配给节点时,CNI[6] 会:

  1. 为其创建网络命名空间。

  2. 分配 IP 地址。

  3. 将容器连接到网络。

如果 pod 像上面的示例一样包含多个容器,则所有容器都被置于同一个命名空间中。

  1. 创建 pod 时,CNI 为容器创建网络命名空间

  2. 然后分配 IP 地址

  3. 最后将容器连接到网络的其余部分

那么当列出节点上的容器时会看到什么?

可以 SSH 到 Kubernetes 节点来查看命名空间:

$ lsns -t netNS TYPE NPROCS   PID USER     NETNSID NSFS                           COMMAND
4026531992 net     171     1 root  unassigned /run/docker/netns/default      /sbin/init noembed norestore
4026532286 net       2  4808 65535          0 /run/docker/netns/56c020051c3b /pause
4026532414 net       5  5489 65535          1 /run/docker/netns/7db647b9b187 /pause

lsns 命令会列出主机上所有的命名空间。

记住 Linux 中有多种命名空间类型[7]

Nginx 容器在哪?

那么 pause 容器又是什么?

Pause 容器创建 Pod 中的网络命名空间

从节点上的所有进程中找出 Nginx 容器:

$ lsnsNS TYPE   NPROCS   PID USER            COMMAND
# truncated output
4026532414 net         5  5489 65535           /pause
4026532513 mnt         1  5599 root            sleep 1d
4026532514 uts         1  5599 root            sleep 1d
4026532515 pid         1  5599 root            sleep 1d
4026532516 mnt         3  5777 root            nginx: master process nginx -g daemon off;
4026532517 uts         3  5777 root            nginx: master process nginx -g daemon off;
4026532518 pid         3  5777 root            nginx: master process nginx -g daemon off;

该容器出现在了挂在(mount mnt)、Unix 分时系统(Unix time-sharing uts)和 PID(pid)命名空间中,但是并不在网络命名空间(net)中。

不幸的是,lsns 只显示了每个进程最低的 PID,不过可以根据进程 ID 进一步过滤。

可以通过以下内容检索Nginx 容器的所有命名空间:

$ sudo lsns -p 5777NS TYPE   NPROCS   PID USER  COMMAND
4026531835 cgroup    178     1 root  /sbin/init noembed norestore
4026531837 user      178     1 root  /sbin/init noembed norestore
4026532411 ipc         5  5489 65535 /pause
4026532414 net         5  5489 65535 /pause
4026532516 mnt         3  5777 root  nginx: master process nginx -g daemon off;
4026532517 uts         3  5777 root  nginx: master process nginx -g daemon off;
4026532518 pid         3  5777 root  nginx: master process nginx -g daemon off;

pause 进程再次出现,这次它劫持了网络命名空间。

那是什么?

集群中的每个 pod 都有一个在后台运行的隐藏容器,被称为 pause

列出节点上的所有容器并过滤出 pause 容器:

$ docker ps | grep pause
fa9666c1d9c6   k8s.gcr.io/pause:3.4.1  "/pause"  k8s_POD_kube-dns-599484b884-sv2js…
44218e010aeb   k8s.gcr.io/pause:3.4.1  "/pause"  k8s_POD_blackbox-exporter-55c457d…
5fb4b5942c66   k8s.gcr.io/pause:3.4.1  "/pause"  k8s_POD_kube-dns-599484b884-cq99x…
8007db79dcf2   k8s.gcr.io/pause:3.4.1  "/pause"  k8s_POD_konnectivity-agent-84f87c…

将看到对于节点分配到的每个 pod,都有一个匹配的 pause 容器。

该 pause 容器负责创建和维持网络命名空间。

它包含的代码极少,部署后立即进入睡眠状态。

然而,它在 Kubernetes 生态中的首当其冲,发挥着至关重要的作用。[8]

  1. 创建 pod 时,CNI 会创建一个带有睡眠容器的网络命名空间

  2. Pod 中的所有容器都会加入到它创建的网络命名空间中

  3. 此时 CNI 分配 IP 地址并将容器连接到网络

进入睡眠状态的容器有什么用?

要了解它的实用性,我们可以想象下如示例一样有两个容器的 pod,但没有 pause 容器。

容器启动,CNI:

  1. 为 Nginx 容器创建一个网络命名空间。

  2. 把 busybox 容器加入到前面创建的网络命名空间中。

  3. 为 pod 分配 IP 地址。

  4. 将容器连接到网络。

假如 Nginx 容器崩溃了会发生什么?

CNI 将不得不再次完成所有流程,两个容器的网络都会中断。

由于 sleep 容器不太可能有任何 bug,因此创建网络命名空间通常是一个更保险、更健壮的选择。

如果 pod 中的一个容器崩溃,其余的仍可以处理网络请求。

为 Pod 分配了 IP 地址

前面提到 pod 和所有容器获得了同样的 IP。

这是怎么配置的?

在 pod 网络命名空间中,创建一个接口并分配 IP 地址。

我们来验证下。

首先,找到 pod 的 IP 地址:

$ kubectl get pod multi-container-pod -o jsonpath={.status.podIP}
10.244.4.40

接下来,找到相关的网络命名空间。

由于网络命名空间是从物理接口创建的,需要访问集群节点。

如果你运行的是 minikube,可以通过 minikube ssh 访问节点。如果在云提供商中运行,应该有某种方法通过 SSH 访问节点。

进入后,可以找到创建的最新的网络命名空间:

$ ls -lt /var/run/netns
total 0
-r--r--r-- 1 root root 0 Sep 25 13:34 cni-0f226515-e28b-df13-9f16-dd79456825ac
-r--r--r-- 1 root root 0 Sep 24 09:39 cni-4e4dfaac-89a6-2034-6098-dd8b2ee51dcd
-r--r--r-- 1 root root 0 Sep 24 09:39 cni-7e94f0cc-9ee8-6a46-178a-55c73ce58f2e
-r--r--r-- 1 root root 0 Sep 24 09:39 cni-7619c818-5b66-5d45-91c1-1c516f559291
-r--r--r-- 1 root root 0 Sep 24 09:39 cni-3004ec2c-9ac2-2928-b556-82c7fb37a4d8

本示例中,它是 cni-0f226515-e28b-df13-9f16-dd79456825ac。此时,可以在该命名空间总执行 exec命令:

$ ip netns exec cni-0f226515-e28b-df13-9f16-dd79456825ac ip a
# output truncated
3: eth0@if12: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP group defaultlink/ether 16:a4:f8:4f:56:77 brd ff:ff:ff:ff:ff:ff link-netnsid 0inet 10.244.4.40/32 brd 10.244.4.40 scope global eth0valid_lft forever preferred_lft foreverinet6 fe80::14a4:f8ff:fe4f:5677/64 scope linkvalid_lft forever preferred_lft forever

10.244.4.40 就是 pod 的 IP 地址。

通过查找 @if12 中的 12 找到网络接口。

$ ip link | grep -A1 ^12
12: vethweplb3f36a0@if16: mtu 1376 qdisc noqueue master weave state UP mode DEFAULT group defaultlink/ether 72:1c:73:d9:d9:f6 brd ff:ff:ff:ff:ff:ff link-netnsid 1

还可以验证 Nginx 容器是否从该命名空间中监听 HTTP 流量:

$ ip netns exec cni-0f226515-e28b-df13-9f16-dd79456825ac netstat -lnp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      692698/nginx: master
tcp6       0      0 :::80                   :::*                    LISTEN      692698/nginx: master

如果无法通过 SSH 访问集群的节点,可以试试 kubectl exec 进入到 busybox 容器,然后使用 ip 和 netstat 命令。

太棒了!

现在我们已经介绍了容器间的通信,接下来看看 Pod 与 Pod 直接如何建立通信。

检查集群中 pod 到 pod 的流量

当说起 pod 间通信时,会有两种可能:

  1. Pod 流量流向同一节点上的 pod。

  2. Pod 流量流量另一个节点上的 pod。

为了使整个设置正常工作,我们需要之前讨论过的虚拟接口和以太网桥接。

在继续之前,我们先讨论下他们的功能以及为什么他们时必需的。

要完成 pod 与其他 pod 的通信,它必须先访问节点的根命名空间。

这是使用连接 pod 和根命名空间的虚拟以太网对来实现的。

这些虚拟接口设备[9]veth 中的 v)连接并充当两个命名空间间的隧道。

使用此 veth 设备,将一端连接到 pod 的命名空间,另一端连接到根命名空间。

这些 CNI 可以替你做,也可以手动操作:

$ ip link add veth1 netns pod-namespace type veth peer veth2 netns root

现在 pod 的命名空间有了可以访问根命名空间的隧道。

节点上每个新建的 pod 都会设置如下所示的 veth 对。

创建接口对时其中一部分。

其他的就是为以太网设备分配地址,并创建默认路由。

来看下如何在 pod 的命名空间中设置 veth1 接口:

$ ip netns exec cni-0f226515-e28b-df13-9f16-dd79456825ac ip addr add 10.244.4.40/24 dev veth1
$ ip netns exec cni-0f226515-e28b-df13-9f16-dd79456825ac ip link set veth1 up
$ ip netns exec cni-0f226515-e28b-df13-9f16-dd79456825ac ip route add default via 10.244.4.40

在节点侧,我们创建另一个 veth2 对:

$ ip addr add 169.254.132.141/16 dev veth2
$ ip link set veth2 up

可以像以前一样检查现有的 veth 对。

在 pod 的命名空间中,检查 eth0 接口的后缀。

$ ip netns exec cni-0f226515-e28b-df13-9f16-dd79456825ac ip link show type veth
3: eth0@if12: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP mode DEFAULT group defaultlink/ether 16:a4:f8:4f:56:77 brd ff:ff:ff:ff:ff:ff link-netnsid 0

这种情况下可以使用 grep -A1 ^12 进行查找(或者滚动到目标所在):

$ ip link show type veth
# output truncated
12: cali97e50e215bd@if3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1450 qdisc noqueue state UP mode DEFAULT group defaultlink/ether ee:ee:ee:ee:ee:ee brd ff:ff:ff:ff:ff:ff link-netns cni-0f226515-e28b-df13-9f16-dd79456825ac

也可以使用 ip -n cni-0f226515-e28b-df13-9f16-dd79456825ac link show type veth命令。

注意 3: eth0@if12 和 12: cali97e50e215bd@if3 接口上的符号。

在 pod 命名空间中,eth0 接口连接到根命名空间中编号为 12 的接口。因此是 @if12

在 veth 对的另一端,根命名空间连接到 pod 命名空间的 3 号接口。

接下来是连接 veth 对两端的桥接器(bridge)。

Pod 命名空间连接到以太网桥接器

桥接器将位于根命名空间中的虚拟接口的每一端“绑定”。

该桥接器将允许流量在虚拟对之间流动,并通过公共根命名空间。

理论时间。

以太网桥接器位于OSI 网络模型[10]的第二层。

可以将桥接器看作一个虚拟交换机,接受来自不同命名空间和接口的连接。[11]

以太网桥接器允许连接同一个节点上的多个可用网络。

因此,可以使用该设置连接两个接口:从 pod 命名空间的 veth 连接到同一节点上另一个 pod 的 veth

我们继续看下以太网桥接器和 veth 对的作用。

跟踪同一节点上 pod 间的流量

假设同一个节点上有两个 pod,Pod-A 想向 Pod-B 发送消息。

  1. 由于目标不是同命名空间的容器,Pod-A 向其默认接口 eth0 发送数据包。这个接口与 veth 对的一端绑定,作为隧道。因此数据包将被转发到节点的根命名空间。

  2. 以太网桥接器作为虚拟交换机,必须以某种方式将目标 pod IP(Pod-B)解析为其 MAC 地址。

  3. 轮到ARP 协议上场了。当帧到达桥接器时,会向所有连接的设备发送 ARP 广播。桥接器喊道谁有 Pod-B 的 IP 地址。

  4. 收到带有连接 Pod-B 接口的 MAC 地址的回复,然后此信息存储在桥接器 ARP 缓存(查找表)中。

  5. IP 和 MAC 地址的映射存储完成后,桥接器在表中查找,并将数据包转发到正确的短点。数据包到达根命名空间中 Pod- B 的 veth,然后从那快速到达 Pod-B 命名空间内的 eth0 接口。

有了这个,Pod-A 和 Pod-B 之间的通信取得了成功。

跟踪不同节点上 pod 间的通信

对于需要跨不同节点通信的 pod,需要额外的通信跳转。

  1. 前几个步骤保持不变,直到数据包到达根命名空间并需要发送到 Pod- B。

  2. 当目标地址不在本地网络中,数据包将被转发到本节点的默认网关。节点上退出或默认网关通常位于 eth0 接口上 -- 将节点连接到网络的物理接口。

这次并不会发生 ARP 解析,因为源和目标 IP 在不同网络上。

检查使用位运算(Bitwise)操作完成。

当目标 IP 不在当前网络上时,数据包将被转发到节点的默认网关。

位运算的工作原理

在确定数据包的转发位置时,源节点必须执行位运算。

这也被称为与操作。[12]

作为复习,位与操作产生如下结果:

0 AND 0 = 0
0 AND 1 = 0
1 AND 0 = 0
1 AND 1 = 1

除了 1 与 1 以外的都是 false。

如果源节点的 IP 为 192.168.1.1,子网掩码为 /24,目标 IP 为 172.16.1.1/16,则按位与操作将确认他们不在同一网络上。

这意味着目标 IP 与数据包的源在不同的网络上,因此数据包将在默认网关中转发。

数学时间。

我们必须从二进制文件中的 32 位地址执行与操作开始。

先找出源和目标 IP 的网络。

| Type             | Binary                              | Converted          |
| ---------------- | ----------------------------------- | ------------------ |
| Src. IP Address  | 11000000.10101000.00000001.00000001 | 192.168.1.1        |
| Src. Subnet Mask | 11111111.11111111.11111111.00000000 | 255.255.255.0(/24) |
| Src. Network     | 11000000.10101000.00000001.00000000 | 192.168.1.0        |
|                  |                                     |                    |
| Dst. IP Address  | 10101100.00010000.00000001.00000001 | 172.16.1.1         |
| Dst. Subnet Mask | 11111111.11111111.00000000.00000000 | 255.255.0.0(/16)   |
| Dst. Network     | 10101100.00010000.00000000.00000000 | 172.16.0.0         |

位运算操作后,需要将目标 IP 与数据包源节点的子网进行比较。

| Type             | Binary                              | Converted          |
| ---------------- | ----------------------------------- | ------------------ |
| Dst. IP Address  | 10101100.00010000.00000001.00000001 | 172.16.1.1         |
| Src. Subnet Mask | 11111111.11111111.11111111.00000000 | 255.255.255.0(/24) |
| Network  Result  | 10101100.00010000.00000001.00000000 | 172.16.1.0

进行位比较后,ARP 会检查其查询表来查找默认网关的 MAC 地址。

如果有条目,将立即转发数据包。

否则,先进行广播以确定网关的 MAC 地址。

  1. 数据包现在路由到另一个节点的默认接口,我们叫它 Node-B。

  2. 以相反的顺序。数据包现在位与 Node-B 的根命名空间,并到达桥接器,这里会进行 ARP 解析。

  3. 收到带有连接 Pod-B 的接口 MAC地址的回复。

  4. 这次桥接器通过 Pod-B 的 veth 设备将帧转发,并到达 Pod-B 自己的命名空间。

此时应该已经熟悉了 pod 之间的流量如何流转,接下来再探索下 CNI 如何创建上述内容。

容器网络接口 - CNI

容器网络接口(CNI)关注当前节点的网络。[13]

bd010f4c50d826492649481a1c8e78bd.png

可以将 CNI 看作网络插件在解决 Kubernetes 某些 需求时要遵循的一套规则。

然而,它不仅仅与 Kubernetes 或者特定网络插件关联。

可以使用如下 CNI:

  • Calico[14]

  • Cilium[15]

  • Flannel[16]

  • Weave Net[17]

  • 其他网络插件[18]

他们都实现相同的 CNI 标准。

如果没有 CNI,你需要手动完成如下操作:

  • 创建 pod(容器)的网络命名空间

  • 创建接口

  • 创建 veth 对

  • 设置命名空间网络

  • 设置静态路由

  • 配置以太网桥接器

  • 分配 IP 地址

  • 创建 NAT 规则

还有太多其他需要手动完成的工作。

更不用说删除或重新启动 pod 时删除或调整上述所有内容了。

CNI 必须支持四个不同的操作[19]

  • ADD - 将容器添加到网络

  • DEL - 从网络中删除容器

  • CHECK - 如果容器的网络出现问题,则返回错误

  • VERSION - 显示插件的版本

让我们在实践中看看它是如何工作的。

当 pod 分配到特定节点时,kubelet 本身不会初始化网络。

相反,它将任务交给了 CNI。

然后,它指定了配置,并以 JSON 格式将其发送给 CNI 插件。

可以在节点的 /etc/cni/net.d 目录中,找到当前 CNI 的配置文件:

$ cat 10-calico.conflist
{"name": "k8s-pod-network","cniVersion": "0.3.1","plugins": [{"type": "calico","datastore_type": "kubernetes","mtu": 0,"nodename_file_optional": false,"log_level": "Info","log_file_path": "/var/log/calico/cni/cni.log","ipam": { "type": "calico-ipam", "assign_ipv4" : "true", "assign_ipv6" : "false"},"container_settings": {"allow_ip_forwarding": false},"policy": {"type": "k8s"},"kubernetes": {"k8s_api_root":"https://10.96.0.1:443","kubeconfig": "/etc/cni/net.d/calico-kubeconfig"}},{"type": "bandwidth","capabilities": {"bandwidth": true}},{"type": "portmap", "snat": true, "capabilities": {"portMappings": true}}]
}

每个插件都使用不同类型的配置来设置网络。

例如,Calico 使用 BGP 路由协议配对的第 3 层网络来连接 pod。

Cilium 在第 3 到 7 层使用 eBPF 配置覆盖网络。

与 Calico 一起,Cilium 支持设置网络策略来限制流量。

那该如何选择呢?

这取决于。

CNI 主要有两组。

第一组中,可以找到使用基本网络设置(也称为扁平网络)的CNI,并将集群 IP 池 中的IP 地址分配给 pod。

这可能会因为快速用尽可用的 IP 地址而成为负担。

相反,另一种方法是使用覆盖网络。

简而言之,覆盖网络是主(底层)网络之上的辅助网络。

覆盖网络的工作原理是封装来自底层网络的所有数据包,这些数据包指向另一个节点上的 pod。

覆盖网络的一项流行技术是 VXLAN[20],它允许在 L3 网络上隧道传输 L2 域。

那么哪种更好?

没有唯一的答案,通常取决于你的需求。

你是在构建一个拥有数万个节点的大集群吗?

可能覆盖网络更好。

你是否在意更简单的设置和在嵌套网络中不失去检查网络流量的能力。

扁平网络更适合你。

现在已经讨论了 CNI,让我们继续探索 Pod 到服务(service)的通信是如何完成的。

检查 pod 到服务的流量

由于 Kubernetes 环境下 pod 的动态特性,分配给 pod 的 IP 地址不是静态的。

这些 IP 地址是短暂的,每次创建或者删除 pod 时都会发生变化。

服务解决了这个问题,为连接到一组 pod 提供了稳定的机制。

默认情况下,在 Kubernetes 中创建服务时,会为其预定并分配虚拟 IP [21]

使用选择器将服务于目标 pod 进行管理。

当删除 pod 并添加新 pod 时会发生什么?

该服务的虚拟 IP 保持不变。

然而,无需敢于,流量将到达新创建的 pod。

换句话说,Kubernetes 中的服务类似于负载均衡器。

但他们时如何工作的?

使用 Netfilter 和 Iptables 拦截和重写流量

Kubernetes 中的服务基于两个 Linux 内核组件:

  1. Netfilter

  2. Iptables

Netfilter 是一个框架,允许配置数据包过滤、创建 NAT或端口翻译规则,并管理网络中的流量。

此外,它还屏蔽和阻止不请自来的连接访问服务。

另一方面,Iptables 是一个用户空间程序,允许你配置 Linux 内核防火墙的 IP 数据包过滤器规则。

iptables 使用不同的 Netfilter 模块实现。

你可以使用 iptables CLI 实时更改过滤规则,并将其插入 netfilters 的挂点。

过滤器组织在不同的表中,其中包含处理网络流量数据包的链。

每个协议都使用不同的内核模块和程序。

当提到 iptables 时,通常说的是 IPV4。对于 IPV6 的规则,CLI 是 ip6tables。

Iptables 有五种类型的链,每种链都直接映射到 Netfilter 钩子。

从 iptables 角度看是:

  • PRE_ROUTING

  • INPUT

  • FORWARD

  • OUTPUT

  • POST_ROUTING

对应映射到 Netfilter 钩子:

  • NF_IP_PRE_ROUTING

  • NF_IP_LOCAL_IN

  • NF_IP_FORWARD

  • NF_IP_LOCAL_OUT

  • NF_IP_POST_ROUTING

当数据包到达时,根据所处的阶段,会“出发” Netfilter 钩子,该钩子应用特定的 iptables 过滤。

哎呀,看起来很复杂!

不过不需要担心。

这就是为什么我们使用 Kubernetes,上面的所有内容都是通过使用服务来抽象的,一个简单的 YAML 定义就可以自动完成这些规则的设置。

如果对这些 iptables 规则感兴趣,可以登陆到节点并运行:

iptables-save

也可以使用可视化工具[22]浏览节点上的 iptables 链。

记住,可能会有数百条规则。想象下手动创建的难度。

我们已经解释了相同和不同节点上的 pod 间如何通信。

在 Pod-to-Service 中,通信的前半部分保持不变。

当 Pod-A 发出请求时,希望到达 Pod-B(这种情况下,Pod-B 位与服务之后),转移的过程中会发生其他变化。

原始请求从 Pod-A 命名空间中的 eth0 接口出来。

从那里穿过 veth 对,到达根命名空间的以太网桥。

一旦到达桥接器,数据包立即通过默认网关转发。

与 Pod-to-Pod 部分一样,主机进行位比较,由于服务的 vIP 不是节点 CIDR 的一部分,数据包将立即通过默认网关转发出去。

如果查找表中尚没有默认网关的 MAC 地址,则会进行相同的 ARP 解析。

现在魔法发生了。

在数据包经过节点的路由处理之前,Netfilter 钩子 NF_IP_PRE_ROUTING 被触发并应用一条 iptables 规则。规则进行了 DNAT 转换,重写了 POD-A 数据包的目标 IP 地址。

原来服务 vIP 地址被重写称 POD-B 的IP 地址。

从那里,路由就像 Pod-to-Pod 直接通信一样。

然而,在所有这些通信之间,使用了第三个功能。

这个功能被称为 conntrack[23],或连接跟踪。

Conntrack 将数据包与连接关联起来,并在 Pod-B 发送回响应时跟踪其来源。

NAT 严重依赖 contrack 工作。

如果没有连接跟踪,它将不知道将包含响应的数据包发送回哪里。

使用 conntrack 时,数据包的返回路径可以轻松设置相同的源或目标 NAT 更改。

另一半使用相反的顺序执行。

Pod-B 接收并处理了请求,现在将数据发送回 Pod-A。

此时会发生什么?

检查服务的响应

现在 Pod-B 发送响应,将其 IP 地址设置为源地址,Pod-A IP 地址设置为目标地址。

  1. 当数据包到达 Pod-A 所在节点的接口时,就会发生另一个 NAT

  2. 这次,使用 conntrack 更改源 IP 地址,iptables 规则执行 SNAT 将 Pod-B IP 地址替换为原始服务的 VIP 地址。

  3. 从 Pod-A 来看像是服务发回的响应,而不是 Pod-B。

其他部分都一样;一旦 SNAT 完成,数据包到达根命名空间中的以太网桥接器,并通过 veth 对转发到 Pod-A。

回顾

让我们来总结下你在本文中学到的东西:

  • 容器如何在本地或 pod 内通信。

  • 当 pod 位于相同和不同的节点上时,Pod-to- Pod 如何通信。

  • Pod-to-Service - 当 pod 向 Kubernetes 服务背后的 pod 发送流量时。

  • Kubernetes 网络工具箱中有效通信所需的命名空间、veth、iptables、链、Netfilter、CNI、覆盖网络以及所有其他内容。

5b9ad5f38e3ec92dd272589d75616226.gif

往期推荐

好难啊……一个 try-catch 问出这么多花样

k8s集群居然可以图形化安装了?

恶意流量威胁新趋势,揭秘网络黑产3大核心本质

将 k8s 制作成 3D 射击游戏,好玩到停不下来

a5af91095de6c76b115cc6f36989112f.gif

点分享

25047d90d38278c9b97aac1fc080d094.gif

点收藏

ad243e6ccad7634401399cd67458d055.gif

点点赞

c87b5ff9fccced9fa1ce82953be6dd41.gif

点在看

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/512363.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Go 语言网络库 getty 的那些事

简介&#xff1a; Getty 维护团队不追求无意义的 benchmark 数据&#xff0c;不做无意义的炫技式优化&#xff0c;只根据生产环境需求来进行自身改进。只要维护团队在&#xff0c;Getty 稳定性和性能定会越来越优秀。 个人从事互联网基础架构系统研发十年余&#xff0c;包括我…

std中稳定排序算法_源代码库已开放 | 哈工大硕士生用 Python 实现了 11 种经典数据降维算法...

转自&#xff1a;AI开发者网上关于各种降维算法的资料参差不齐&#xff0c;同时大部分不提供源代码。这里有个 GitHub 项目整理了使用 Python 实现了 11 种经典的数据抽取(数据降维)算法&#xff0c;包括&#xff1a;PCA、LDA、MDS、LLE、TSNE 等&#xff0c;并附有相关资料、展…

曲师大教务系统服务器,曲师大教务处信息门户入口地址

为了规范财务行为&#xff0c;加强财务管理&#xff0c;提高代管经费使用效益&#xff0c;提高项目建设质量&#xff0c;根据上级和学校有关财务规定&#xff0c;结合我校实际情况&#xff0c;特制定本办法。一、教务处代管的项目经费品牌特色专业建设经费、精品课程建设经费、…

云网管—云上构建网络自动化体系

简介&#xff1a; 云网管是基于阿里云网络多年技术和经验沉淀打造的云上智能网络管理运维平台&#xff0c;提供企业网络全生命周期管理运维的能力&#xff0c;让部署更快捷、运维更高效、网络更透明。 1.背景 云网管是基于阿里云网络多年技术和经验沉淀打造的云上智能网络管理…

【C++练级之路】【Lv.5】动态内存管理(都2023年了,不会有人还不知道new吧?)

目录 一、C/C内存分布二、new和delete的使用方式2.1 C语言内存管理2.2 C内存管理2.2.1 new和delete操作内置类型2.2.2 new和delete操作自定义类型 三、new和delete的底层原理3.1 operator new与operator delete函数3.2 原理总结3.2.1 内置类型3.2.2 自定义类型 四、定位new表达…

开工啦~Spring 完美导入 IDEA

作者 | 阿Q来源 | 阿Q说代码有小伙伴私信我说想要研究下Spring的源码&#xff0c;想让我出一期教程来实现IDEA导入Spring源码&#xff0c;今天它来了~版本 &#xff1a;IDEA 2020.2.3 &#xff1b;Spring 5.0.x &#xff1b;gradle 4.4.1 &#xff1b;先从github上面把 spring …

基于MaxCompute分布式Python能力的大规模数据科学分析

简介&#xff1a; 如何利用云上分布式 Python 加速数据科学。 如果你熟悉 numpy、pandas 或者 sklearn 这样的数据科学技术栈&#xff0c;同时又受限于平台的计算性能无法处理&#xff0c;本文介绍的 MaxCompute 可以让您利用并行和分布式技术来加速数据科学。也就是说只要会用…

5新建没有头文件_开垦绿茵版图迎来“真金白银”保障,新建足球场地可获财政补贴...

本周二&#xff0c;国家发改委、体育总局、国务院足球改革发展部际联席会议办公室共同制定了《全国社会足球场地设施建设专项行动实施方案(试行)》。《方案》指出&#xff0c;对新建11人制标准足球场&#xff0c;每个球场补助200万元&#xff1b;对新建5人制、7人制(8人制)足球…

网站免费空间和服务器的区别,网站空间和服务器的区别

网站空间和服务器的区别 内容精选换一换汇总对象存储服务OBS的各项功能&#xff0c;并对其进行简单的介绍&#xff0c;帮助您从整体上了解OBS的功能特性。CCE Turbo集群是基于云原生基础设施构建的云原生2.0容器引擎服务&#xff0c;具备软硬协同、网络无损、安全可靠、调度智能…

基于 MaxCompute + Hologres 的人群圈选和数据服务实践

简介&#xff1a; 本文主要介绍如何通过 MaxCompute 进行海量人群的标签加工&#xff0c;通过 Hologres 进行分析建模&#xff0c;从而支持大规模人群复杂圈选场景下的交互式体验&#xff0c;以及基于API的数据服务最佳实践。 本文作者 刘一鸣 阿里云智能 高级产品专家 人群圈…

一款强大的 Kubernetes API 流量查看神器

作者 | 小碗汤来源 | 我的小碗汤mizu 是为 Kubernetes 提供的一个简单而强大的 API 流量查看器&#xff0c;可以查看微服务之间的所有 API 通信&#xff0c;以帮助调试和排除故障。相当于 Kubernetes 的 TCPDump 和 Wireshark。简单而强大的 CLI丰富的过滤规则API 调用实时监控…

Redis 巧用数据类型实现亿级数据统计

作者 | 码哥字节来源 | 码哥字节在移动应用的业务场景中&#xff0c;我们需要保存这样的信息&#xff1a;一个 key 关联了一个数据集合&#xff0c;同时还要对集合中的数据进行统计排序。常见的场景如下&#xff1a;给一个 userId &#xff0c;判断用户登陆状态&#xff1b;两亿…

2021杭州·云栖大会来了!门票免费预约!

2021杭州云栖大会&#xff0c;定了&#xff01; 10月19日-22日&#xff0c;就在杭州云栖小镇 2场重磅主论坛上百场分论坛 超4万平米科技展 今年&#xff0c;云栖大会将首次免费开放 门票可在官网免费预约 入口现已开启 戳此预约&#xff0c;我们不见不散&#xff01; ​ …

js 可以做什么东西_Deno需要做什么才能取代Node.js?

全文共1843字&#xff0c;预计学习时长5分钟Deno是一个Javascript/TypeScript的运行时&#xff0c;旨在取代Node.js的地位。它拥有广泛功能&#xff0c;讨论度非常高&#xff0c;在Github上有将近68000个星星&#xff1a;既然这么受欢迎&#xff0c;那么有人要问了&#xff1a;…

37 手游基于 Flink CDC + Hudi 湖仓一体方案实践

简介&#xff1a; 介绍了 37 手游为何选择 Flink 作为计算引擎&#xff0c;并如何基于 Flink CDC Hudi 构建新的湖仓一体方案。 本文作者是 37 手游大数据开发徐润柏&#xff0c;介绍了 37 手游为何选择 Flink 作为计算引擎&#xff0c;并如何基于 Flink CDC Hudi 构建新的湖…

手把手搭建一个容器化+代理网关+可视化管理环境

作者 | togettoyou来源 | SuperGopher前言本文主要分享个人服务器的应用部署方案现状&#xff0c;容器化代理网关可视化管理。准备阶段我购买的是腾讯云服务器&#xff08;2 核 4GB 3Mbps&#xff09;域名也是在腾讯云备案过的&#xff0c;提前准备域名解析配置环境安装 Docker…

漫画 | 一口气搞懂 Serverless !

简介&#xff1a; 第二届云原生编程挑战赛为热爱技术的年轻人提供一个挑战世界级技术问题的舞台&#xff0c;希望用技术为全社会创造更大价值。 作者 | 刘欣 呃&#xff0c;我可能是别人眼中所说的不用奋斗的一代。 大家喜欢听的什么多姿多彩的生活&#xff0c;我都经历过一…

OpenKruise v0.10.0 新特性 WorkloadSpread 解读

简介&#xff1a; 针对需求&#xff0c;OpenKruise 在 v0.10.0 版本中新增了 WorkloadSpread 特性。目前它支持配合 Deployment、ReplicaSet、CloneSet 这些 workload&#xff0c;来管理它们下属 Pod 的分区部署与弹性伸缩。下文会深入介绍 WorkloadSpread 的应用场景和实现原理…

CSS 状态管理,玩出花样了!

作者 | 零一来源 | 前端印象CSS用于交互的方式无非就那么几种&#xff1a;伪类&#xff1a;:hover、:link、:active ...动画&#xff1a;animation过渡动画&#xff1a;transition这些交互方式组合起来&#xff0c;真的可以玩出一些花样&#xff0c;例如我们本文的主题&#xf…

告别Kafka Stream,让轻量级流处理更加简单

简介&#xff1a; 还在花精力去选型Kafka组件去做清洗转化&#xff1f;来试试Kafka ETL任务功能&#xff01; 一说到数据孤岛&#xff0c;所有技术人都不陌生。在 IT 发展过程中&#xff0c;企业不可避免地搭建了各种业务系统&#xff0c;这些系统独立运行且所产生的数据彼此独…