云原生之深入解析Kubernetes网络流量的流转路径

news2024/11/25 21:49:49

一、Kubernetes 网络要求

  • Kubernetes 网络模型定义了一组基本规则:
    • 在不使用网络地址转换 (NAT) 的情况下,集群中的 Pod 能够与任意其他 Pod 进行通信;
    • 在不使用网络地址转换 (NAT) 的情况下,在集群节点上运行的程序能与同一节点上的任何 Pod 进行通信;
    • 每个 Pod 都有自己的 IP 地址(IP-per-Pod),并且任意其他 Pod 都可以通过相同的这个地址访问它。
  • 这些要求,不会将具体实现限制在某种解决方案上。相反,它们笼统地描述了集群网络的特性,为了满足这些限制,必须解决以下挑战:
    • 如何确保同一个 Pod 中的容器行为就像它们在同一个主机上一样?
    • 集群中的 Pod 能否访问其他 Pod?
    • Pod 可以访问服务吗?服务是负载均衡的吗?
    • Pod 可以接收集群外部的流量吗?

二、Linux 网络命名空间如何在 Pod 中工作 ?

  • 来看一个运行应用的主容器和伴随一起的另一个容器,在示例中,有一个带有 nginx 和 busybox 容器的 Pod:
apiVersion: v1
kind: Pod
metadata:
  name: multi-container-Pod
spec:
  containers:
    - name: container-1
      image: busybox
      command: ['/bin/sh', '-c', 'sleep 1d']
    - name: container-2
      image: nginx
  • 部署时,会发生以下事情:
    • Pod 在节点上拥有独立的网络命名空间;
    • 分配一个 IP 地址给 Pod ,两个容器之间共享端口;
    • 两个容器共享相同的网络命名空间,并在本地彼此可见。
  • 网络配置在后台迅速完成,但让我们退后一步,尝试理解为什么运行容器需要上述动作?在 Linux 中,网络命名空间是独立的、隔离的逻辑空间,可以将网络命名空间视为,将物理网络接口分割小块之后的独立部分,每个部分都可以单独配置,并拥有自己的网络规则和资源,这些包括防火墙规则、接口(虚拟的或物理的)、路由以及与网络相关的所有内容。
  • 物理网络接口持有根网络命名空间:

在这里插入图片描述

  • 也可以使用 Linux 网络命名空间来创建独立的网络,每个网络都是独立的,除非进行配置,默认不会与其他网络互通:

在这里插入图片描述

  • 但最终,还是需要物理接口处理所有真实的数据包,所有虚拟接口都是基于物理接口创建的。网络命名空间可以通过 ip-netns 进行管理,使用 ip netns list 可以列出主机上的命名空间。
  • 需要注意的是,创建的网络命名空间会出现在 /var/run/netns 下面,但 Docker 并没有遵循这一规则。例如,如下是 Kubernetes 节点的一些命名空间(cni- 前缀意味着命名空间是由 CNI 插件创建):
$ 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)
  • 当创建一个 Pod,Pod 被分配给一个节点后,CNI 将分配 IP 地址并将容器连接到网络。如果 Pod 包含多个容器,那么这些容器都将被放在同一个命名空间中。
  • 当创建 Pod 时,容器运行时会给容器创建一个网络命名空间:

在这里插入图片描述

  • 然后 CNI 负责给 Pod 分配一个 IP 地址:

在这里插入图片描述

  • 最后 CNI 将容器连接到网络的其余部分:

在这里插入图片描述

  • 那么,当列出节点上的容器的命名空间会发生什么呢?可以通过 SSH 连接到 Kubernetes 节点并查看命名空间:
$ lsns -t net

        NS 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 中有多种命名空间类,那么 Nginx 容器在哪里?那些 pause 容器是什么?

三、在 Pod 中,pause 容器创建了网络命名空间

  • 先列出节点上的所有命名空间,看看能否找到 Nginx 容器:
$ lsns
        NS 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;
  • Nginx 容器在挂载 (mnt)、Unix time-sharing (uts) 和 PID (pid) 命名空间中,但不在网络命名空间 (net) 中。不幸的是,lsns 只显示每个进程最小的 PID,但你可以根据这个进程 ID 进一步过滤。
  • 使用以下命令,在所有命名空间中检索 Nginx 容器:
$ sudo lsns -p 5777

       NS 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 容器负责创建和维持网络命名空间,底层容器运行时会完成网络命名空间的创建,通常是由 containerd 或 CRI-O 完成。在部署 Pod 和创建容器之前,由运行时创建网络命名空间,容器运行时会自动完成这些,不需要手工执行 ip netns 创建命名空间。
  • pause 容器包含非常少的代码,并且在部署后立即进入睡眠状态。但是,它是必不可少的,并且在 Kubernetes 生态系统中起着至关重要的作用。
  • 创建 Pod 时,容器运行时会创建一个带有睡眠容器的网络命名空间:

在这里插入图片描述

  • Pod 中的其他容器都会加入由 pause 容器创建的网络名称空间:

在这里插入图片描述

  • 此时,CNI 分配 IP 地址并将容器连接到网络:

在这里插入图片描述

  • 一个进入睡眠状态的容器有什么用?为了理解它的用途,可以想象一个 Pod 有两个容器,就像前面的例子一样,但没有 pause 容器,一旦容器启动,CNI 将会:
    • 使 busybox 容器加入之前的网络命名空间;
    • 分配 IP 地址;
    • 将容器连接到网络。
  • 如果 Nginx 崩溃了怎么办?CNI 将不得不再次执行所有步骤,并且两个容器的网络都将中断。由于睡眠容器不太可能有任何错误,因此创建网络命名空间通常是一种更安全、更健壮的选择(如果 Pod 中的一个容器崩溃,剩下的仍然可以回复其它网络请求)。

四、分配一个 IP 地址给 Pod

  • 现在我们知道 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 default
    link/ether 16:a4:f8:4f:56:77 brd ff:ff:ff:ff:ff:ff link-netnsid 0
    inet 10.244.4.40/32 brd 10.244.4.40 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::14a4:f8ff:fe4f:5677/64 scope link
       valid_lft forever preferred_lft forever
  • 这个 IP 就是 Pod 的 IP 地址!通过查找 @if12 中的 12 找到网络接口
$ ip link | grep -A1 ^12

12: vethweplb3f36a0@if16: mtu 1376 qdisc noqueue master weave state UP mode DEFAULT group default
    link/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 容器的 shell 并直接在内部使用 ip 和 netstat 命令。

五、查看集群中 Pod 到 Pod 的流量

  • Pod 到 Pod 的通信有两种可能的情况:
    • Pod 流量的目的地是同一节点上的 Pod;
    • Pod 流量的目的地是在不同节点上的 Pod。
  • 整个工作流依赖于虚拟接口对和网桥,为了让一个 Pod 与其他 Pod 通信,它必须先访问节点的根命名空间。通过虚拟以太网对来实现 Pod 和根命名空间的连接,这些虚拟接口设备(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 default
    link/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 default
    link/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 对两端的桥接器。

六、Pod 网络命名空间连接到以太网桥

  • 网桥会汇聚位于根命名空间中的每一个虚拟接口,这个网桥允许虚拟 pair 之间的流量,也允许穿过公共根命名空间的流量。其相关的原理是:以太网桥位于 OSI 网络模型 的第 2 层,可以将网桥视为接受来自不同命名空间和接口的连接的虚拟交换机,以太网桥可以连接节点上的多个可用网络。
  • 因此,可以使用网桥连接两个接口,即 Pod 命名空间的 veth 连接到同一节点上另一个 Pod 的 veth:

在这里插入图片描述

七、跟踪在同一节点上 Pod 到 Pod 的流量

  • 假设同一个节点上有两个 Pod,Pod-A 向 Pod-B 发送消息,由于访问目标不在同一个命名空间,Pod-A 将数据包发送到其默认接口 eth0,这个接口与 veth 对的一端绑定,作为隧道。这样,数据包会被转发到节点上的根命名空间:

在这里插入图片描述

  • 以太网网桥作为一个虚拟交换机,需要目标 Pod-B 的 MAC 地址才能工作:

在这里插入图片描述

  • ARP 协议会解决这个问题。当帧到达网桥时,会向所有连接的设备发送 ARP 广播,网桥广播询问持有 Pod-B 的 IP 地址:

在这里插入图片描述

  • 此时会收到一个带有 Pod-B IP 的 MAC 地址应答,这条消息会被存储在桥接 ARP 缓存(查找表)中:

在这里插入图片描述

  • IP 地址和 MAC 地址的映射关系存储之后,网桥就在表中查找,并将数据包转发到正确的端点,数据包到达根命名空间内 Pod-B 的 veth 之后,很快又到达 Pod-B 命名空间内的 eth0 接口:

在这里插入图片描述

  • 至此,Pod-A 和 Pod-B 之间的通信就成功。

八、跟踪不同节点上的 Pod 到 Pod 通信

  • 对于跨节点 Pod 之间的通信,会经过额外的通信跳跃。与“跟踪在同一节点上 Pod 到 Pod 的流量”的步骤一致,直到数据包到达根命名空间并需要发送到 Pod-B:

在这里插入图片描述

  • 当目的 IP 不在本地网络中时,报文被转发到节点的默认网关,节点的出口网关或默认网关,通常位于节点与网络相连的物理接口 eth0 上:

在这里插入图片描述

  • 此时 不会发生 ARP 解析,因为源 IP 和目标 IP 不在同一个网段中,网段的检查是使用按位运算完成的。当目的 IP 不在当前网络段时,数据包被转发到节点的默认网关。

九、按位运算的工作原理

  • 在确定数据包的转发位置时,源节点必须执行位运算,这也称为与操作。
  • 按位与运算的规则,如下所示,除了 1 与 1 以外的都是 false:
0 AND 0 = 0
0 AND 1 = 0
1 AND 0 = 0
1 AND 1 = 1
  • 如果源节点的 IP 为 192.168.1.1,子网掩码为 /24,目标 IP 为 172.16.1.1/16,则按位与运算将得知它们位于不同的网段上。这意味着目标 IP 与数据包的源不在同一个网络上,数据包将通过默认网关转发。
  • 需要从二进制的 32 位地址开始进行 AND 操作,先找出源 IP 网络和目标 IP 网段:
TypeBinaryConverted
Src. IP Address11000000.10101000.00000001.00000001192.168.1.1
Src. Subnet Mask11111111.11111111.11111111.00000000255.255.255.0(/24)
Src. Network11000000.10101000.00000001.00000000192.168.1.0
Dst. IP Address10101100.00010000.00000001.00000001172.16.1.1
Dst. Subnet Mask11111111.11111111.00000000.00000000255.255.0.0(/16)
Dst. Network10101100.00010000.00000000.00000000172.16.0.0
  • 按位运算之后,需要将目标 IP 与数据包源节点的子网进行比较:
TypeBinaryConverted
Dst. IP Address10101100.00010000.00000001.00000001172.16.1.1
Src. Subnet Mask11111111.11111111.11111111.00000000255.255.255.0(/24)
Network Result10101100.00010000.00000001.00000000172.16.1.0
  • 运算的结果是 172.16.1.0,不等于 192.168.1.0(源节点的网络),说明源 IP 地址和目标 IP 地址不在同一个网络上。如果目标 IP 是 192.168.1.2,即与发送 IP 在同一子网中,则 AND 操作将得到节点的本地网络。
TypeBinaryConverted
Dst. IP Address11000000.10101000.00000001.00000010192.168.1.2
Src. Subnet Mask11111111.11111111.11111111.00000000255.255.255.0(/24)
Network11000000.10101000.00000001.00000000192.168.1.0
  • 进行逐位比较后,ARP 通过查找表查找默认网关的 MAC 地址。如果有条目,将立即转发数据包,否则,先进行广播以找到网关的 MAC 地址。
  • 现在,数据包路由到另一个节点的默认接口,称为 Node-B:

在这里插入图片描述

  • 以相反的顺序,现在数据包位于 Node-B 的根命名空间,并到达网桥,会进行 ARP 解析:

在这里插入图片描述

  • 路由系统将返回与 Pod-B 相连的接口的 MAC 地址:

在这里插入图片描述

  • 网桥通过 Pod-B 的 veth 设备转发帧,并到达 Pod-B 的命名空间:

在这里插入图片描述

  • 至此,大家应该都已经熟悉了 Pod 之间的流量是如何流转的。

十、容器网络接口 - CNI

  • 容器网络接口(CNI)主要关注的是当前节点中的网络:

在这里插入图片描述

  • 可以将 CNI 看作为解决 Kubernetes 网络需求,而遵循的一组规则。有这些 CNI 实现可供使用:Calico、Cillium、Flannel、Weave Net 和其他网络插件,它们都遵循相同的 CNI 标准。
  • 如果没有 CNI,需要人工完成如下操作:
    • 创建接口;
    • 创建 veth 对;
    • 设置网络命名空间;
    • 设置静态路由;
    • 配置以太网桥;
    • 分配 IP 地址;
    • 创建 NAT 规则;
    • 还有其它大量事情。
  • 这还不包括,在删除或重启 Pod 时,需要进行类似的全部操作。
  • CNI 必须支持四种不同的操作:
    • ADD - 向网络添加一个容器;
    • DEL - 从网络中删除一个容器;
    • CHECK - 如果容器的网络出现问题,则返回错误;
    • VERSION - 显示插件的版本。
  • 来一起看下,CNI 是如何工作的?当 Pod 被分配到特定节点时,Kubelet 自身不会初始化网络,相反,Kubelet 将这个任务交给 CNI。但是,Kubelet 以 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}}
  ]
}
  • 每个 CNI 插件都会使用不同类型的网络配置。例如,Calico 使用基于 BGP 的三层网络连接 Pod,Cilium 从三层到七层使用的是基于 eBPF 的 overlay 网络,与 Calico 一样,Cilium 也支持通过配置网络策略来限制流量。那么应该使用哪一个呢?主要有两类 CNI:
    • 在第一类中,使用基本网络设置(也称为平面网络),从集群的 IP 池为 Pod 分配 IP 地址的 CNI,这种方式可能很快耗尽 IP 地址,而成为负担;
    • 另一类是使用 overlay 网络,简单来说,overlay 网络是主(底层)网络之上的重建网络,overlay 网络通过封装来自底层网络的数据包工作,这些数据包被发送到另一个节点上的 Pod,overlay 网络的一种流行技术是 VXLAN,它可以在 L3 网络上建立 L2 域的隧道。
  • 那么哪个更好呢?没有单一的答案,这取决于你的需求,是否正在构建具有数万个节点的大型集群?也许 overlay 网络更好;是否在意更简单的配置和审查网络流量,而不会愿意在复杂网络中丢失这种能力?那么扁平网络更适合你。

十一、检查 Pod 到 Service 的流量

  • 由于 Pod 在 Kubernetes 中是动态的,分配给 Pod 的 IP 地址不是静态的,Pod 的 IP 是短暂的,每次创建或删除 Pod 时都会发生变化。Kubernetes 中的 Service 解决了这个问题,为连接一组 Pod 提供了可靠的机制:

在这里插入图片描述

  • 默认情况下,在 Kubernetes 中创建 Service 时,被分配一个虚拟 IP。在 Service 中,可以使用选择器将 Service 与目标 Pod 相关联。当删除或添加一个 Pod 时会发生什么呢?Service 的虚拟 IP 保持静态不变。但流量可以再无需干预的情况下,到达新创建的 Pod。换句话说,Kubernetes 中的 Service 类似于负载均衡器,但它们是如何工作的呢?

十二、使用 Netfilter 和 Iptables 拦截和重写流量

  • Kubernetes 中的 Service 是基于 Linux 内核中的两个组件构建的:网络过滤器和iptables。Netfilter 是一个可以配置数据包过滤、创建 NAT 、端口转发规则以及管理网络中流量的框架。此外,它可以屏蔽和禁止未经同意的访问。另一方面,iptables 是一个用户态程序,可以用来配置 Linux 内核防火墙的 IP 数据包过滤规则。
  • iptables 是作为不同的 Netfilter 模块实现的,可以使用 iptables CLI 即时修改过滤规则,并将它们插入 netfilters 挂载点。过滤器配置在不同的表中,其中包含用于处理网络流量数据包的链。不同的协议使用不同的内核模块和程序,当提到 iptables 时,通常指的是 IPv4;对于 IPv6 ,终端工具是 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 的原因,以上所有内容都是通过使用 Service 抽象出来的,并且一个简单的 YAML 定义可以自动设置这些规则。
  • 查看 iptables 规则,可以连接到节点并运行:
$ iptables-save
  • 还可以使用这个工具来可视化节点上的 iptables 链,这是来自 GKE 节点上的可视化 iptables 链的示例图:

在这里插入图片描述

  • 至此,我们已经了解了,相同节点上的 Pod 和不同节点上 Pod 之间是如何通信的,在 Pod 与 Service 的通信中,链路的前半部分是一样的:

在这里插入图片描述

  • 当请求从 Pod-A 走向 Pod-B 时,由于 Pod-B 在 Service 的 “后面”,在传输的过程中,会有一些不一样。原始的请求,在 Pod-A 命名空间的 eth0 接口发出,接着请求通过 veth到达根名称空间的网桥。一旦到达网桥,数据包就会立即通过默认网关转发。与 Pod-to-Pod 部分一样,主机进行按位比较。由于服务的虚拟 IP 不是节点 CIDR 的一部分,因此数据包将立即通过默认网关转发。如果默认网关的 MAC 地址尚未出现在查找表中,则会进行 ARP 解析找出默认网关的 MAC 地址。现在神奇的事情发生了,在数据包通过节点的路由之前,Netfilter 的 NF_IP_PRE_ROUTING 挂钩被触发,并执行 iptables 规则,这个规则会修改 Pod-A 数据包的目标 IP 地址 DNAT。

在这里插入图片描述

  • 前面服务的虚拟 IP 地址被重写为 Pod-B 的 IP 地址,接下来数据包路由过程与 Pod 到 Pod 的通信一样:

在这里插入图片描述

  • 数据包重写后,通信是 Pod 到 Pod。然而,在所有这些通信中,使用了一个第三方的功能,此功能称为 conntrack 或链路跟踪,当 Pod-B 发回响应时,conntrack 会将数据包与链路相关联,并跟踪其来源,NAT 严重依赖于 conntrack,如果没有链路跟踪,将不知道将包含响应的数据包发回何处,使用 conntrack 时,数据包的返回路径很容易设置为相同的源或目标 NAT 更改。通信的另一部分与现在的链路相反,Pod-B 接收并处理了请求,现在将数据发送回 Pod-A。

十三、检查来自服务的响应

  • Pod-B 发送响应,将其 IP 地址设置为源地址,并将 Pod-A 的 IP 地址设置为目标地址:

在这里插入图片描述

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

在这里插入图片描述

  • 这时,conntrack 开始工作,修改源 IP 地址,iptables 规则执行 SNAT,并将 Pod-B 的源 IP 地址修改为原始服务的虚拟 IP:

在这里插入图片描述

  • 对于 Pod-A 来说,响应是来自于 Service 而不是 Pod-B,其余的都是一样的,一旦 SNAT 完成,数据包就会到达根命名空间中的网桥,并通过 veth 对转发到 Pod-A。

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

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

相关文章

王道《计算机网络》思维导图汇总

第一章 1.1.1 概念与功能 1.1.2 组成与分类 1.1.3 标准化工作及相关组织 1.1.4 性能指标 速率 带宽 吞吐量 时延 时延带宽积 往返时延RTT 利用率 1.2.1 分层结构、协议、接口、服务 1.2.2 OSI参考模型 应用层 表示层 会话层 传输层 网络层 数据链路层 物理层 1.2.4 TCP/IP 参…

内核角度看IO模型

聊聊Netty那些事儿之从内核角度看IO模型 网络包接收流程 当网络数据帧通过网络传输到达网卡时&#xff0c;网卡会将网络数据帧通过DMA的方式放到环形缓冲区RingBuffer中。RingBuffer是网卡在启动的时候分配和初始化的环形缓冲队列。当RingBuffer满的时候&#xff0c;新来的数据…

【AUTOSAR】BMS开发实际项目讲解(十三)----电池管理系统碰撞安全功能和SFR

SG-BMS-7 : BMS系统应避免碰撞保护功能异常引起的安全事故&#xff08;ASIL A&#xff09; 功能框图&#xff08;SG-BMS-7&#xff09; 功能组件说明 功能组件ID 功能组件名称 描述 ASIL等级 FSC-FC-05 Relay Drive 驱动继电器开启和关断 ASIL A FSC-FC-11 Detection …

【vue】可选链运算符(?.)和空值合并运算符(??):

文章目录 一、问题一:二、问题二:三、使用:【1】空值合并运算符&#xff08;??&#xff09;【2】可选链运算符&#xff08;?.&#xff09; 一、问题一: http://www.codebaoku.com/question/question-sd-1010000042870944.html //1、npm安装 npm install babel/plugin-propo…

批量修改文件命名的shell脚本

Android 制作开机动画的方法参考&#xff1a;linux开机动画制作教程 其中往往会把里面的png图片命名位XX_0001.png , 002.png……等 Window批量修改文件名时会带有空格和括号。 这里写了一个脚本&#xff0c;可以在批量修改文件名后&#xff0c;将文件名转换为XX_00001 格式&…

基于matlab使用 YOLO V2深度学习进行多类对象检测(附源码)

一、前言 此示例演示如何训练多类对象检测器。 深度学习是一种强大的机器学习技术&#xff0c;可用于训练强大的多类对象检测器&#xff0c;例如 YOLO v2、YOLO v4、SSD 和 Faster R-CNN。此示例使用该函数训练 YOLO v2 多类室内对象检测器。经过训练的物体检测器能够检测和识…

ModaHub魔搭社区:Milvus 监控指标和使用 Grafana 展示 Milvus 监控指标

目录 Milvus 监控指标 Milvus 性能指标 系统运行指标 硬件存储指标 Milvus 监控指标 Milvus 会生成关于系统运行状态的详细时序 metrics。你可以通过 Prometheus、Grafana 或任何可视化工具展现以下指标&#xff1a; Milvus 性能指标系统运行指标&#xff1a;CPU/GPU 使用…

单片机学习 11-中断系统(定时器中断+外部中断)

中断系统 中断介绍 ​ 中断是为使单片机具有对外部或内部随机发生的事件实时处理而设置的&#xff0c;中断功能的存在&#xff0c;很大程度上提高了单片机处理外部或内部事件的能力。它也是单片机最重要的功能之一&#xff0c;是我们学习单片机必须要掌握的。很多初学者被困在…

全网超全,Pytest自动化测试框架pytest-xdist分布式测试插件(实战)

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 平常我们功能测试…

Markdown 扩展语法

✅作者简介&#xff1a;人工智能专业本科在读&#xff0c;喜欢计算机与编程&#xff0c;写博客记录自己的学习历程。 &#x1f34e;个人主页&#xff1a;小嗷犬的个人主页 &#x1f34a;个人网站&#xff1a;小嗷犬的技术小站 &#x1f96d;个人信条&#xff1a;为天地立心&…

Python在命令行模式下如何退出命令行

文章目录 python退出命令行模式结语 刚学习python的时候是用的命令行的方式&#xff0c;刚接触不知道如何退出命令行&#xff0c;百度参考了好几篇文章&#xff0c;这里记录一下&#xff0c;希望能帮助到有需要的小伙伴们。 python退出命令行模式 总结一下&#xff0c;共有三种…

Redis处理⾼并发 实现分布式锁

Redisson Redisson是架设在Redis基础上的⼀个Java驻内存数据⽹格&#xff08;In-Memory Data Grid&#xff09;。 Redisson在基于NIO的Netty框架上&#xff0c;充分的利⽤了Redis键值数据库提供的⼀系列优势&#xff0c;在Java实⽤⼯具包中常⽤ 接⼝的基础上&#xff0c;为使⽤…

高压功率放大器在光学测量中的应用有哪些

高压功率放大器在光学测量中有许多应用&#xff0c;例如在激光器和LED驱动、光电探测器和光电转换器中等。这些应用大多需要将输入信号放大到高电平输出&#xff0c;以便驱动高电压或大功率负载。 在激光器和LED驱动应用中&#xff0c;高压功率放大器可以将低电平的控制信号放大…

nginx纳入skywalking调用链监控

nginx纳入skywalking调用链监控 一、说明二、nginx部署2.1 OpenResty介绍2.2 准备SkyWalking Nginx Agent2.3 docker方式部署OpenResty2.3.1 修改配置文件2.3.2 启动OpenResty容器 2.4 验证 一、说明 服务器中已部署好skywalking&#xff0c;并将tomcat纳入skywalking监控(tom…

JavaSE基础语法--接口

接口在现实生活中比比皆是。比如电脑的USB接口&#xff0c;插座的接口。这些接口我们发现都是一样的规范。比如插座的有双孔插&#xff0c;有三孔插。那么对应就有双脚设备&#xff0c;和三脚的设备。从这我们就能摸清楚规律&#xff1a;接口就是统一规范的提供服务。Java中接口…

七年老Android推荐 : 日常开发中好用的工具 (二)

1. 前言 作为一名拥有七年经验的Android开发工程师&#xff0c;在日常开发中&#xff0c;总希望能提升自己的开发效率&#xff0c;对此也积累了一些工具&#xff0c;本文对此总结了一些好用的工具。 2. draw.io draw.io用来编写流程图非常好用&#xff0c;是一个免费的在线图…

Splunk Enterprise 9.1.0 (macOS, Linux, Windows) - 机器数据管理和分析

Splunk Enterprise 9.1.0 (macOS, Linux, Windows) - 机器数据管理和分析 请访问原文链接&#xff1a;https://sysin.org/blog/splunk-9/&#xff0c;查看最新版。原创作品&#xff0c;转载请保留出处。 作者主页&#xff1a;sysin.org 混合世界的数据平台 快速、大规模地从…

【海思SS528】MPP媒体处理软件V5.0 | 音频模块 - 学习笔记

目录 &#x1f384;一、概述&#x1f384;二、音频输入(AI) 和 音频输出(AO)✨2.1 音频接口和 AI、 AO 设备✨2.2 录音和播放原理✨2.3 AI、AO 通道✨2.4 重采样 &#x1f384;三、音频编码和解码✨3.1 音频编解码流程✨3.2 音频编解码协议✨3.3 语音帧结构 &#x1f384;四、总…

【Spring | 事件监听概述】

本篇主要对Spring 的 事件监听机制简单介绍下。 事件监听 概述 概述 ApplicationContext中的事件处理是通过ApplicationEvent 类和ApplicationListener接口提供的。如果将实现该 ApplicationListener 接口的 bean 部署到上下文中&#xff0c;那么每当 ApplicationEvent 发布到 …

Android Jetpack Compose - 深入了解 AlertDialog

在开发 Android 应用时&#xff0c;对话框是一个常见的 UI 元素&#xff0c;用于向用户显示信息或获取用户的反馈。在 Jetpack Compose 中&#xff0c;我们可以使用 AlertDialog 组件来创建对话框。在这篇博客中&#xff0c;我们将深入了解如何使用 Jetpack Compose 的 AlertDi…