K8S系列-Kubernetes网络

一、Kubernetes网络模型

​ Kubernetes网络模型设计的一个基础原则是:每个Pod都拥有一个独立的IP地址,并假定所有Pod都在一个可以直接连通的、扁平的网络空间中,不管它们是否运行在同一个Node(宿主机)中,都要求它们可以直接通过对方的IP进行访问。设计这个原则的原因是,用户不需要额外考虑如何建立Pod之间的连接,也不需要考虑如何将容器端口映射到主机端口等问题。

​ 实际上,在Kubernetes世界里,IP是以Pod为单位进行分配的。一个Pod内部的所有容器共享一个网络堆栈(相当于一个网络命名空间,它们的地址、网络设备、配置等都是共享的)。按照这个网络原则抽象出来的为每个Pod都设置一个IP地址的模型也被称作IP-per-Pod模型。IP-per-Pod模型是一个简单的兼容性较好的模型。从该模型的网络的端口分配、域名解析、服务发现、负载均衡、应用配置和迁移等角度来看,Pod都能够被看作一台独立的虚拟机或物理机。

​ 由于Kubernetes的网络模型假设Pod之间访问时使用的是对方Pod的实际地址,所以一个Pod内部的应用程序看到的自已的IP地址和端口与集群内其他Pod看到的一样。它们都是Pod实际分配的IP地址。

二、linux网络基础

​ k8s部署到linux,其网络模型的具体实现是基于linux的,下面我们就来看下linux的网络处理。

2.1、网络命名空间

​ 为了支持网络协议栈的多个实例,Linux在网络栈中引入了网络命名空间,这些独立的协议栈被隔离到不同的命名空间中。处于不同命名空间中的网络栈是完全隔离的,彼此之间无法通信。Docker正是利用了网络的命名空间特性,实现了不同容器之间的网络隔离。

在Linux的网络命名空间中可以有自已独立的路由表及独立的iptables设置来提供包转发、NAT及IP包过滤等功能。

为了隔离出独立的协议栈,需要纳人命名空间的元素有进程、套接字、网络设备等。进程创建的套接字必须属于某个命名空间,套接字的操作也必须在命名空间中进行。

​ 由于网络命名空间代表的是一个独立的协议栈,它们之间是相互隔离的,彼此无法通信。在协议栈内部都看不到对方。我们如果需要让处于不同命名空间中的网络相互通信,基至与外部的网络进行通信呢?就需要应用Veth 设备对。Veth设备对的一个重要作用就是打通了相互看不到的协议栈之间的壁垒,它就像一条管子,一端莲着这个网络命名空间的协议栈,一端连看另一个网络命名空间的协议栈。

2.2、Veth设备对

​ Veth设备是Virtual Ethernet Device的缩写,即虚拟以太网设备,是Linux系统下的一种虚拟网络接口设备。它总是成对出现,形成veth pair(veth对),可以理解为是用一根虚拟的网线将两台设备(通常是两个命名空间或网络空间)直接连接起来。

sh 复制代码
[root@node4 feverasa]# 
[root@node4 feverasa]# 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
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
...............
[root@node4 feverasa]# ip link add veth1 type veth peer name veth2
[root@node4 feverasa]# 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
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
..........
    link/ether 52:54:00:5d:fa:5d brd ff:ff:ff:ff:ff:ff
5: veth2@veth1: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 4e:62:41:af:70:84 brd ff:ff:ff:ff:ff:ff
6: veth1@veth2: <BROADCAST,MULTICAST,M-DOWN> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
[root@node4 feverasa]# 

​ 这里我们可以看到有一对veth了,veth1其的peer是veth2。现在我们创建两个网络命名空间netns1、netns2,然后将veth1移到netns1、veth2移到netns2。

sh 复制代码
[root@node4 feverasa]# ip netns add netns1
[root@node4 feverasa]# ip netns add netns2
[root@node4 feverasa]# ip link set veth1 netns netns1
[root@node4 feverasa]# ip link set veth2 netns netns2
[root@node4 feverasa]# 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
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP mode DEFAULT group default qlen 1000
	.............
[root@node4 feverasa]# 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
6: veth1@if5: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether e6:db:87:c7:64:4f brd ff:ff:ff:ff:ff:ff link-netnsid 1
[root@node4 feverasa]# ip netns exec netns2 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
5: veth2@if6: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 4e:62:41:af:70:84 brd ff:ff:ff:ff:ff:ff link-netnsid 0
[root@node4 feverasa]# 

​ 下面我们分别给veth1、veth2分配ip,在启动它们,并ping通其网络。

sh 复制代码
[root@node4 feverasa]# ip netns exec netns1 ip addr add 192.168.2.1/24 dev veth1
[root@node4 feverasa]# ip netns exec netns2 ip addr add 192.168.2.2/24 dev veth2
[root@node4 feverasa]# ip netns exec netns1 ip link set dev veth1 up
[root@node4 feverasa]# ip netns exec netns2 ip link set dev veth2 up
[root@node4 feverasa]# ip netns exec netns1 ping 192.168.2.2
PING 192.168.2.2 (192.168.2.2) 56(84) bytes of data.
64 bytes from 192.168.2.2: icmp_seq=1 ttl=64 time=0.104 ms
64 bytes from 192.168.2.2: icmp_seq=2 ttl=64 time=0.067 ms
64 bytes from 192.168.2.2: icmp_seq=3 ttl=64 time=0.069 ms
^C
--- 192.168.2.2 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2007ms
rtt min/avg/max/mdev = 0.067/0.080/0.104/0.017 ms
[root@node4 feverasa]# 

​ 在 Docker 内部,就是通过 Veth 设备对连通容器与宿主机的网络。

2.2、虚拟网桥

​ Linux可以支持多个不同的网络,怎样将这些网络连接起来并实现各网络中主机的相互通信呢,就可以使用用网桥。网桥是一个二层的虚拟网络设备,把若干个网络接口"连接"起来,以使得网络接口之间的报文能够相互转发。网桥能够解析收发的报文,读取目标MAC地址的信息,将其与自已记录的MAC表结合,来决策报文的转发目标网络接口。为了实现这些功能,网桥会学习源MAC地址(二层网桥转发的依据就是MAC地址)。在转发报文时,网桥只需向特定的网口进行转发,来避免不必要的网络交互。如果它遇到一个自已从未学习到的地址,就无法知道这个报文应该向哪个网络接口转发,将报文广播给所有的网络接口。

​ 同时,当我们给网桥分配一个ip地址后,其也可以作用于第三层也就是网络层,也就是判断当前的数据包是发送给当前子网内部中,还是发到给其他的网络。

2.3、容器 Bridge网络

​ 当我们使用Bridge网络模式,也就是直接使用宿主机的 IP 地址与外界进行通信,就是上面这种网络连接,通过宿主机的物理网卡eth0与外面的网络进行连接,而宿主机里面的各个容器实例之间,则通过容器网桥br0进行连接。

1、网络veth

sh 复制代码
[root@k8s-node1 feverasa]# docker run -d spring_docker_test:0.0.7 /bin/bash
6831bc2ad0801649a0350777cc299c51a4fb59dcea54678a693c2533f84b188f
[root@k8s-node1 feverasa]# docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' 6831bc2ad0801649a0350777cc299c51a4fb59dcea54678a693c2533f84b188f
172.17.0.2
[root@k8s-node1 feverasa]# 
[root@k8s-node1 feverasa]# ifconfig 
docker0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.17.0.1  netmask 255.255.0.0  broadcast 172.17.255.255
..................
ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.127.129  netmask 255.255.255.0  broadcast 192.168.127.255
.............
lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
.............
veth0e4fa72: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
...........
[root@k8s-node1 feverasa]# docker run -d --name springboot2 spring_docker_test:0.0.7
2333a32ecedd9ec07555856fe5fbce75e1d864160fef3f0a8e5f1f54a68fcb42
[root@k8s-node1 feverasa]# docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' 2333a32ecedd9ec07555856fe5fbce75e1d864160fef3f0a8e5f1f54a68fcb42
172.17.0.3
[root@k8s-node1 feverasa]# ifconfig
docker0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.17.0.1  netmask 255.255.0.0  broadcast 172.17.255.255
...........
ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.127.129  netmask 255.255.255.0  broadcast 192.168.127.255
..............
lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
        inet 127.0.0.1  netmask 255.0.0.0
...............
veth020e120: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
.............
veth0e4fa72: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
	............
[root@k8s-node1 feverasa]# 

​ 在上面的案例中,我们的linux虚拟宿主机的ip是192.168.127.129,我们的网桥就是docker0,其的ip是172.17.0.1,我们启动一个容器实例其给这个实例分配的ip是172.17.0.2,同是有一个新的veth-veth0e4fa72与这个容器连通。之后我们再起一个容器实例,这个实例的ip是172.17.0.3,新的veth-veth020e120。下面我们进入到2333a32ecedd9ec075558这个容器实例里面

sh 复制代码
[root@k8s-node1 feverasa]# docker exec -it 2333a32ecedd9ec07555856fe5fbce75e1d864160fef3f0a8e5f1f54a68fcb42 /bin/sh
/wls/appsystems # ping 172.17.0.2
PING 172.17.0.2 (172.17.0.2): 56 data bytes
64 bytes from 172.17.0.2: seq=0 ttl=64 time=0.198 ms
64 bytes from 172.17.0.2: seq=1 ttl=64 time=0.089 ms
^C
--- 172.17.0.2 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.089/0.143/0.198 ms
/wls/appsystems # 
/wls/appsystems # ping 172.17.0.1
PING 172.17.0.1 (172.17.0.1): 56 data bytes
64 bytes from 172.17.0.1: seq=0 ttl=64 time=0.123 ms
64 bytes from 172.17.0.1: seq=1 ttl=64 time=0.090 ms
^C
--- 172.17.0.1 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.090/0.106/0.123 ms
/wls/appsystems # ping 192.168.127.129
PING 192.168.127.129 (192.168.127.129): 56 data bytes
64 bytes from 192.168.127.129: seq=0 ttl=64 time=0.095 ms
64 bytes from 192.168.127.129: seq=1 ttl=64 time=0.148 ms
^C
--- 192.168.127.129 ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 0.095/0.121/0.148 ms
/wls/appsystems # ifconfig
eth0      Link encap:Ethernet  HWaddr 02:42:AC:11:00:03  
          inet addr:172.17.0.3  Bcast:172.17.255.255  Mask:255.255.0.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:17 errors:0 dropped:0 overruns:0 frame:0
          TX packets:9 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0 
          RX bytes:1370 (1.3 KiB)  TX bytes:714 (714.0 B)

lo        Link encap:Local Loopback  
          inet addr:127.0.0.1  Mask:255.0.0.0
......................

​ 可以看到我们各个网络都是通的。

2、网络路由

sh 复制代码
[root@k8s-node1 feverasa]# ip route 
default via 192.168.127.2 dev ens33 proto dhcp metric 100 
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 
192.168.122.0/24 dev virbr0 proto kernel scope link src 192.168.122.1 
192.168.127.0/24 dev ens33 proto kernel scope link src 192.168.127.129 metric 100 
[root@k8s-node1 feverasa]# 

​ 1.1、第一条表示的是默认路由,没有找到对应目的ip的路由,就交给这个路由信息,这个ens33就是我们的宿主机的etho,前面我们的ipconfig也可以看到这个ens33,同时这个192.168.127.2是我们vmware虚拟机对应的网关信息

​ 1.2、然后第二条172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 ,就表示对应的172.17对应的网段就交给docker0docker0就是我们上面的容器网桥。这个信息文心一言解释一下

2.4、网络防火墙Netfilter和iptables

​ 下面我们的这个内容会与linux的Netfilter和iptables相关,具体的可以搜索下其他的文章了解。

​ 简单来说,就是linux在通过网络进行交互的时候,不管是进入主机的网络数据包、还是出去的,都能进行对应网络处理,进行过滤、修改和转发这些。例如网络进入主机前进行对应规则校验,看这个数据包要不要进入到主机、能不能进入到主机,网络出去的时候修改对应的源IP、或者目的IP啥的。上面提到的Netfilter就是在linux内核内部进行网络相关的处理,而iptables是提供的工具,你通过iptables进行的操作都是交给Netfilter来进行工作运行的。

​ 对于本机的网络报文数据,一般三条链路:流入本机的报文、从本机流出的报文、流到本机但本机只是起到转发的作用。对于这些,Netfilter主要提供了5个钩子函数:PREROUTING、INPUT、FORWARD、OUTPUT和POSTROUTING,分别对应数据包进入和离开网络栈的不同阶段。同时还有对应的表来记录对应的规则用来对网络做对应的处理,例如 filter表、nat表、mangle表。

3.1各种规则表介绍

​ 1、filter表是Netfilter默认的表,也是最常用的表。它主要用于实现数据包过滤和网络访问控制,允许或拒绝数据包的传输。例如通过filter表可以定义规则,允许或拒绝特定类型的数据包进入或离开系统,从而增强系统的安全性。

​ 2、nat表用于实现网络地址转换(NAT)。它允许对数据包的源地址和目标地址进行转换,通常用于连接多个私有网络到公共网络(例如,将内部IP地址映射为公共IP地址、将外部对某个公共IP地址和端口的访问转发到内部网络中的特定IP地址和端口上)。

​ 3、mangle表用于修改数据包的头部信息,包括TTL(Time To Live)、TOS(Type Of Service)等字段。它主要用于对数据包进行特殊处理或标记,例如在数据包通过mangle表时,可以修改其TTL值,以控制数据包的生存时间。

​ 4、raw表用于配置数据包绕过连接追踪(connection tracking)机制。当数据包需要完全自主处理时,可以使用raw表。

​ 5、security表通常用于与安全模块SELinux(Security-Enhanced Linux)一起工作,用于基于安全策略的数据包过滤和处理。

3.2、PREROUTING

​ PREROUTING链是用于处理刚到达本机并在路由转发前的数据包的链,其主要是在数据包被路由到最终目的地之前,能对数据包进行目的地址转换(DNAT)、端口转发等操作。处理这个阶段的主要是mangle和nat表的规则,例如进行端口映射,将一个外部请求的端口映射到另一个端口。

3.3、INPUT

​ INPUT链是filter表中用于处理进入本地主机的数据包的链,可以对进入的数据包进行过滤和规则匹配,以决定是否允许数据包进入本地系统。例如网络防火墙、或者其他的访问控制。

3.4、FORWARD

​ FORWARD链是filter表中用于处理经过本机进行转发的数据包的链。这些数据包既不是进入本地系统的,也不是从本地系统发出的,而是经过本机路由到其他网络或主机的。

3.5、OUTPUT

​ OUTPUT链是filter表中用于处理从本地主机发出的数据包的链。这些数据包一般是主机应用程序生成的,需要发送到网络上的其他主机或服务器。这个的话,我们一般可以网络数据包对外的发出,例如我们在公司的标机就可以对访问外网进行控制,控制访问哪些目的ip能出去,哪些不能发出。

3.6、POSTROUTING

​ POSTROUTING链是mangle、nat和raw表中用于处理将要离开本机的数据包的链。在数据包离开本地主机后,POSTROUTING链允许对数据包进行源地址转换(SNAT)、修改报头信息等操作。

3.7、容器网络Netfilter链路案例

​ 下面我们来看下使用的案例

sh 复制代码
[root@k8s-node1 feverasa]# docker run -d -p 8083:8087 spring_docker_test:0.0.7 /bin/bash
2c8d417aa02776bd89f9b236bcb2464e53ddbe247cd97919ace114fa21f82fe2
[root@k8s-node1 feverasa]# docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' 2c8d417aa02776bd89f9b236bcb2464e53ddbe247cd97919ace114fa21f82fe2
172.17.0.2
[root@k8s-node1 feverasa]# iptables-save 
# Generated by iptables-save v1.4.21 on Sun Sep 22 11:41:54 2024
*mangle
:PREROUTING ACCEPT [10797:3011175]
:INPUT ACCEPT [10797:3011175]
..........
COMMIT
# Completed on Sun Sep 22 11:41:54 2024
# Generated by iptables-save v1.4.21 on Sun Sep 22 11:41:54 2024
*nat
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
...........
-A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
-A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER
.........
-A POSTROUTING -s 172.17.0.2/32 -d 172.17.0.2/32 -p tcp -m tcp --dport 8087 -j MASQUERADE
-A DOCKER -i docker0 -j RETURN
-A DOCKER ! -i docker0 -p tcp -m tcp --dport 8083 -j DNAT --to-destination 172.17.0.2:8087
..............
COMMIT
# Completed on Sun Sep 22 11:41:54 2024
# Generated by iptables-save v1.4.21 on Sun Sep 22 11:41:54 2024
*filter
:INPUT ACCEPT [96:8698]
:FORWARD ACCEPT [0:0]
................
-A DOCKER -d 172.17.0.2/32 ! -i docker0 -o docker0 -p tcp -m tcp --dport 8087 -j ACCEPT
...................
COMMIT
# Completed on Sun Sep 22 11:41:54 2024
[root@k8s-node1 feverasa]# ip route
default via 192.168.127.2 dev ens33 proto dhcp metric 100 
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 
192.168.122.0/24 dev virbr0 proto kernel scope link src 192.168.122.1 
192.168.127.0/24 dev ens33 proto kernel scope link src 192.168.127.129 metric 100 
[root@k8s-node1 feverasa]# 

​ 上面这里我们是启动了一个容器实例,然后进行了端口映射-p 8083:8087,也就是将请求到宿主机的8083映射到容器的8087端口。然后我们看下面的规则:我们可以看到这里主要用来两个表natfilter规则:

sh 复制代码
*nat
-A DOCKER ! -i docker0 -p tcp -m tcp --dport 8083 -j DNAT --to-destination 172.17.0.2:8087
*filter
-A DOCKER -d 172.17.0.2/32 ! -i docker0 -o docker0 -p tcp -m tcp --dport 8087 -j ACCEPT

​ 对于nat的规则:也就是将所有不是来自docker0接口、目标端口为8083的TCP数据包的目标地址和端口更改为172.17.0.2:8087,这样的话,再结合路由172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 规则,就能交给容器网桥来处理,从而请求就能到我们对应的容器实例来处理了。

sh 复制代码
[root@k8s-node1 feverasa]# iptables -t nat -L DOCKER -n -v
Chain DOCKER (2 references)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 RETURN     all  --  docker0 *       0.0.0.0/0            0.0.0.0/0           
    0     0 DNAT       tcp  --  !docker0 *       0.0.0.0/0            0.0.0.0/0            tcp dpt:8083 to:172.17.0.2:8087
[root@k8s-node1 feverasa]# 

​ 对于filter规则:这条规则的作用是允许从宿主机(或其他网络接口,除了docker0)发往容器IP地址(172.17.0.2)和端口(8087)的TCP数据包,通过docker0桥接接口进行转发,而不被iptables的默认策略所拒绝

sh 复制代码
[root@k8s-node1 feverasa]# iptables -t filter -L DOCKER -n -v
Chain DOCKER (1 references)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 ACCEPT     tcp  --  !docker0 docker0  0.0.0.0/0            172.17.0.2           tcp dpt:8087
[root@k8s-node1 feverasa]# 

三、Kubernetes 的网络实现

​ Kubernetes集群会设计到Node内部、Pod内部、不同Node之间的问题,其主要是需要解决:

复制代码
(1)容器到容器之间的直接通信。
(2)Pod到Pod之间的通信。
(3)Pod到Service之间的通信。
(4)集群内部与外部组件之间的通信。

1、同pod容器到容器

​ 同一个Pod内的容器(Pod内的容器是不会跨宿主机的)共享同一个网络命名空间,共享同一个Liux协议栈。所以对于网络的各类操作,就和它们在同一台机器上一样,它们之间借助容器网桥进行通信。

2、同Node上的Pod到Pod的通信

​ 同Node上的Pod的通信,就是借助容器网桥来进行通信。

3、不同Node上的Pod的通信

​ 我们知道一般容器、Pod都是通过网桥模式来与宿主机通信的,容器网络与宿主机网卡的网络一般都是不同的,他们不是同一个网络。所以我们不同Node之间的通信要通过宿主机的物理网卡进行,因此要想实现不同Node上Pod容器之间的通信,就必须想办法通过主机的这个IP地址进行寻址和通信。同时我们也要知道哪个Pod的Ip是在哪台Node主机上,也就是说,先要找到Node对应宿主机的IP地址,将数据发送到这个宿主机的网卡,然后在宿主机上将相应的数据转发到具体的dockero上。当数据到达宿主机Node,那个Node内部的dockero便知道如何将数据发送到Pod了。

​ 下面我们就通过iptable根据来看下其对网络的处理过程:

​ 我们启动部署3个pod、同时配置对应的service,添加对应的NodePort

shell 复制代码
[root@k8s-master k8s]# cat my_spring_deploy.yaml 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: spring-test-deployment
#  namespace: demo
  labels:
    app: spring-test
spec:
  replicas: 3
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 1
  selector:
    matchLabels:
      app: spring-test
  template:
    metadata:
      labels:
        app: spring-test
    spec:
      containers:
      - name: spring-test-container
        image: spring_docker_test:0.0.7
        command: [ "java","-jar","app.jar" ]    #启动命令
        imagePullPolicy: Never
        readinessProbe:
          tcpSocket:
            port: 8087
          initialDelaySeconds: 30
          periodSeconds: 5
          failureThreshold: 10
          timeoutSeconds: 5
          successThreshold: 1
        livenessProbe:
          tcpSocket:
            port: 8087
          initialDelaySeconds: 30
          periodSeconds: 3
          failureThreshold: 1
          timeoutSeconds: 3
          successThreshold: 1
        ports:
        - containerPort: 8087
[root@k8s-master k8s]# cat my_spring_service.yaml 
apiVersion: v1
kind: Service
metadata:
  name: spring-test-service
  # namespace: demo
spec:
  selector:
    app: spring-test
  ports:
    - protocol: TCP
      port: 8081
      targetPort: 8087
      nodePort: 30080 # 如果你想要通过 NodePort 访问,可以添加这一行
  type: NodePort # 如果你想要通过 NodePort 访问,使用 NodePort;否则使用 ClusterIP
[root@k8s-master k8s]# 

​ 我们再通过iptables-save查看nat表对目的地址的处理,这里有一大堆东西,我们主要分析nat表对目的地址的处理。

shell 复制代码
[root@k8s-node1 feverasa]# iptables-save
# Generated by iptables-save v1.4.21 on Mon Oct  7 17:16:10 2024
*mangle
:PREROUTING ACCEPT [11294:3551519]
.............
-A POSTROUTING -o virbr0 -p udp -m udp --dport 68 -j CHECKSUM --checksum-fill
COMMIT
# Completed on Mon Oct  7 17:16:10 2024
# Generated by iptables-save v1.4.21 on Mon Oct  7 17:16:10 2024
*nat
:PREROUTING ACCEPT [1:67]
	.......................
-A PREROUTING -m comment --comment "kubernetes service portals" -j KUBE-SERVICES
-A PREROUTING -m addrtype --dst-type LOCAL -j DOCKER
-A OUTPUT -m comment --comment "kubernetes service portals" -j KUBE-SERVICES
-A OUTPUT ! -d 127.0.0.0/8 -m addrtype --dst-type LOCAL -j DOCKER
-A POSTROUTING -m comment --comment "kubernetes postrouting rules" -j KUBE-POSTROUTING
-A POSTROUTING -s 172.17.0.0/16 ! -o docker0 -j MASQUERADE
-A POSTROUTING -s 192.168.122.0/24 -d 224.0.0.0/24 -j RETURN
-A POSTROUTING -s 192.168.122.0/24 -d 255.255.255.255/32 -j RETURN
-A POSTROUTING -s 192.168.122.0/24 ! -d 192.168.122.0/24 -p tcp -j MASQUERADE --to-ports 1024-65535
-A POSTROUTING -s 192.168.122.0/24 ! -d 192.168.122.0/24 -p udp -j MASQUERADE --to-ports 1024-65535
-A POSTROUTING -s 192.168.122.0/24 ! -d 192.168.122.0/24 -j MASQUERADE
-A POSTROUTING -m comment --comment "flanneld masq" -j FLANNEL-POSTRTG
-A DOCKER -i docker0 -j RETURN
-A FLANNEL-POSTRTG -m mark --mark 0x4000/0x4000 -m comment --comment "flanneld masq" -j RETURN
-A FLANNEL-POSTRTG -s 10.244.1.0/24 -d 10.244.0.0/16 -m comment --comment "flanneld masq" -j RETURN
-A FLANNEL-POSTRTG -s 10.244.0.0/16 -d 10.244.1.0/24 -m comment --comment "flanneld masq" -j RETURN
-A FLANNEL-POSTRTG ! -s 10.244.0.0/16 -d 10.244.1.0/24 -m comment --comment "flanneld masq" -j RETURN
-A FLANNEL-POSTRTG -s 10.244.0.0/16 ! -d 224.0.0.0/4 -m comment --comment "flanneld masq" -j MASQUERADE
-A FLANNEL-POSTRTG ! -s 10.244.0.0/16 -d 10.244.0.0/16 -m comment --comment "flanneld masq" -j MASQUERADE
-A KUBE-MARK-DROP -j MARK --set-xmark 0x8000/0x8000
-A KUBE-MARK-MASQ -j MARK --set-xmark 0x4000/0x4000
-A KUBE-NODEPORTS -p tcp -m comment --comment "default/spring-test-service:" -m tcp --dport 30080 -j KUBE-MARK-MASQ
-A KUBE-NODEPORTS -p tcp -m comment --comment "default/spring-test-service:" -m tcp --dport 30080 -j KUBE-SVC-QVYGXMY2EHJ7RAFE
-A KUBE-POSTROUTING -m comment --comment "kubernetes service traffic requiring SNAT" -m mark --mark 0x4000/0x4000 -j MASQUERADE
-A KUBE-SEP-3O4DEWC3J3BWX72A -s 10.244.1.116/32 -m comment --comment "default/spring-test-service:" -j KUBE-MARK-MASQ
-A KUBE-SEP-3O4DEWC3J3BWX72A -p tcp -m comment --comment "default/spring-test-service:" -m tcp -j DNAT --to-destination 10.244.1.116:8087
-A KUBE-SEP-56MKUVBF4GYNJCVZ -s 192.168.127.133/32 -m comment --comment "default/kubernetes:https" -j KUBE-MARK-MASQ
-A KUBE-SEP-56MKUVBF4GYNJCVZ -p tcp -m comment --comment "default/kubernetes:https" -m tcp -j DNAT --to-destination 192.168.127.133:6443
-A KUBE-SEP-BIYY7O5X6UR6HHXF -s 10.244.1.113/32 -m comment --comment "kube-system/kube-dns:dns-tcp" -j KUBE-MARK-MASQ
-A KUBE-SEP-BIYY7O5X6UR6HHXF -p tcp -m comment --comment "kube-system/kube-dns:dns-tcp" -m tcp -j DNAT --to-destination 10.244.1.113:53
-A KUBE-SEP-G3MN2O4GTAP3PDK2 -s 10.244.1.117/32 -m comment --comment "default/spring-test-service:" -j KUBE-MARK-MASQ
-A KUBE-SEP-G3MN2O4GTAP3PDK2 -p tcp -m comment --comment "default/spring-test-service:" -m tcp -j DNAT --to-destination 10.244.1.117:8087
-A KUBE-SEP-LKOJGVHBLSDGTBKL -s 10.244.1.115/32 -m comment --comment "default/spring-test-service:" -j KUBE-MARK-MASQ
-A KUBE-SEP-LKOJGVHBLSDGTBKL -p tcp -m comment --comment "default/spring-test-service:" -m tcp -j DNAT --to-destination 10.244.1.115:8087
-A KUBE-SEP-NJEYY2XOHHMI4352 -s 10.244.1.114/32 -m comment --comment "kube-system/kube-dns:dns" -j KUBE-MARK-MASQ
-A KUBE-SEP-NJEYY2XOHHMI4352 -p udp -m comment --comment "kube-system/kube-dns:dns" -m udp -j DNAT --to-destination 10.244.1.114:53
-A KUBE-SEP-NLMKBI6PSBPGIFSO -s 10.244.1.114/32 -m comment --comment "kube-system/kube-dns:metrics" -j KUBE-MARK-MASQ
-A KUBE-SEP-NLMKBI6PSBPGIFSO -p tcp -m comment --comment "kube-system/kube-dns:metrics" -m tcp -j DNAT --to-destination 10.244.1.114:9153
-A KUBE-SEP-PRCWKCQTPENAJ4KN -s 10.244.1.113/32 -m comment --comment "kube-system/kube-dns:dns" -j KUBE-MARK-MASQ
-A KUBE-SEP-PRCWKCQTPENAJ4KN -p udp -m comment --comment "kube-system/kube-dns:dns" -m udp -j DNAT --to-destination 10.244.1.113:53
-A KUBE-SEP-RCY2T2CQ6GPUVOKB -s 10.244.1.114/32 -m comment --comment "kube-system/kube-dns:dns-tcp" -j KUBE-MARK-MASQ
-A KUBE-SEP-RCY2T2CQ6GPUVOKB -p tcp -m comment --comment "kube-system/kube-dns:dns-tcp" -m tcp -j DNAT --to-destination 10.244.1.114:53
-A KUBE-SEP-TKZGQ5DGIXMLDELT -s 10.244.1.113/32 -m comment --comment "kube-system/kube-dns:metrics" -j KUBE-MARK-MASQ
-A KUBE-SEP-TKZGQ5DGIXMLDELT -p tcp -m comment --comment "kube-system/kube-dns:metrics" -m tcp -j DNAT --to-destination 10.244.1.113:9153
-A KUBE-SERVICES ! -s 10.244.0.0/16 -d 10.107.178.62/32 -p tcp -m comment --comment "default/spring-test-service: cluster IP" -m tcp --dport 8081 -j KUBE-MARK-MASQ
-A KUBE-SERVICES -d 10.107.178.62/32 -p tcp -m comment --comment "default/spring-test-service: cluster IP" -m tcp --dport 8081 -j KUBE-SVC-QVYGXMY2EHJ7RAFE
-A KUBE-SERVICES ! -s 10.244.0.0/16 -d 10.96.0.10/32 -p tcp -m comment --comment "kube-system/kube-dns:dns-tcp cluster IP" -m tcp --dport 53 -j KUBE-MARK-MASQ
-A KUBE-SERVICES -d 10.96.0.10/32 -p tcp -m comment --comment "kube-system/kube-dns:dns-tcp cluster IP" -m tcp --dport 53 -j KUBE-SVC-ERIFXISQEP7F7OF4
-A KUBE-SERVICES ! -s 10.244.0.0/16 -d 10.96.0.10/32 -p tcp -m comment --comment "kube-system/kube-dns:metrics cluster IP" -m tcp --dport 9153 -j KUBE-MARK-MASQ
-A KUBE-SERVICES -d 10.96.0.10/32 -p tcp -m comment --comment "kube-system/kube-dns:metrics cluster IP" -m tcp --dport 9153 -j KUBE-SVC-JD5MR3NA4I4DYORP
-A KUBE-SERVICES ! -s 10.244.0.0/16 -d 10.96.0.10/32 -p udp -m comment --comment "kube-system/kube-dns:dns cluster IP" -m udp --dport 53 -j KUBE-MARK-MASQ
-A KUBE-SERVICES -d 10.96.0.10/32 -p udp -m comment --comment "kube-system/kube-dns:dns cluster IP" -m udp --dport 53 -j KUBE-SVC-TCOU7JCQXEZGVUNU
-A KUBE-SERVICES ! -s 10.244.0.0/16 -d 10.96.0.1/32 -p tcp -m comment --comment "default/kubernetes:https cluster IP" -m tcp --dport 443 -j KUBE-MARK-MASQ
-A KUBE-SERVICES -d 10.96.0.1/32 -p tcp -m comment --comment "default/kubernetes:https cluster IP" -m tcp --dport 443 -j KUBE-SVC-NPX46M4PTMTKRN6Y
-A KUBE-SERVICES -m comment --comment "kubernetes service nodeports; NOTE: this must be the last rule in this chain" -m addrtype --dst-type LOCAL -j KUBE-NODEPORTS
-A KUBE-SVC-ERIFXISQEP7F7OF4 -m comment --comment "kube-system/kube-dns:dns-tcp" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-BIYY7O5X6UR6HHXF
-A KUBE-SVC-ERIFXISQEP7F7OF4 -m comment --comment "kube-system/kube-dns:dns-tcp" -j KUBE-SEP-RCY2T2CQ6GPUVOKB
-A KUBE-SVC-JD5MR3NA4I4DYORP -m comment --comment "kube-system/kube-dns:metrics" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-TKZGQ5DGIXMLDELT
-A KUBE-SVC-JD5MR3NA4I4DYORP -m comment --comment "kube-system/kube-dns:metrics" -j KUBE-SEP-NLMKBI6PSBPGIFSO
-A KUBE-SVC-NPX46M4PTMTKRN6Y -m comment --comment "default/kubernetes:https" -j KUBE-SEP-56MKUVBF4GYNJCVZ
-A KUBE-SVC-QVYGXMY2EHJ7RAFE -m comment --comment "default/spring-test-service:" -m statistic --mode random --probability 0.33333333349 -j KUBE-SEP-LKOJGVHBLSDGTBKL
-A KUBE-SVC-QVYGXMY2EHJ7RAFE -m comment --comment "default/spring-test-service:" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-3O4DEWC3J3BWX72A
-A KUBE-SVC-QVYGXMY2EHJ7RAFE -m comment --comment "default/spring-test-service:" -j KUBE-SEP-G3MN2O4GTAP3PDK2
-A KUBE-SVC-TCOU7JCQXEZGVUNU -m comment --comment "kube-system/kube-dns:dns" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-PRCWKCQTPENAJ4KN
-A KUBE-SVC-TCOU7JCQXEZGVUNU -m comment --comment "kube-system/kube-dns:dns" -j KUBE-SEP-NJEYY2XOHHMI4352
COMMIT
# Completed on Mon Oct  7 17:16:10 2024
# Generated by iptables-save v1.4.21 on Mon Oct  7 17:16:10 2024
*filter
	...........
COMMIT
# Completed on Mon Oct  7 17:16:10 2024
[root@k8s-node1 feverasa]# 
[root@k8s-node1 feverasa]# 
[root@k8s-node1 feverasa]# ps -ef | grep kube-proxy
root       6786   6766  0 17:01 ?        00:00:00 /usr/local/bin/kube-proxy --config=/var/lib/kube-proxy/config.conf --hostname-override=k8s-node1
root      14447   3576  0 17:17 pts/0    00:00:00 grep --color=auto kube-proxy

​ 首先通过我们定义的Service,其的NodePort是30080,然后其的ClusterPort是8081,3个pod对应的端口是8087是。

复制代码
[root@k8s-master k8s]# kubectl get pod -A  -o wide 
NAMESPACE      NAME                                      READY   STATUS    RESTARTS   AGE    IP                NODE         NOMINATED NODE   READINESS GATES
default        spring-test-deployment-5d5c8869bf-5ssvm   1/1     Running   0          76m    10.244.1.116      k8s-node1    <none>           <none>
default        spring-test-deployment-5d5c8869bf-jtblz   1/1     Running   0          76m    10.244.1.117      k8s-node1    <none>           <none>
default        spring-test-deployment-5d5c8869bf-t6tnp   1/1     Running   0          76m    10.244.1.115      k8s-node1    <none>           <none>
............

​ 在这里面我们提前这些信息

复制代码
-A KUBE-NODEPORTS -p tcp -m comment --comment "default/spring-test-service:" -m tcp --dport 30080 -j KUBE-SVC-QVYGXMY2EHJ7RAFE
-A KUBE-SERVICES -d 10.107.178.62/32 -p tcp -m comment --comment "default/spring-test-service: cluster IP" -m tcp --dport 8081 -j KUBE-SVC-QVYGXMY2EHJ7RAFE
-A KUBE-SVC-QVYGXMY2EHJ7RAFE -m comment --comment "default/spring-test-service:" -m statistic --mode random --probability 0.33333333349 -j KUBE-SEP-LKOJGVHBLSDGTBKL
-A KUBE-SVC-QVYGXMY2EHJ7RAFE -m comment --comment "default/spring-test-service:" -m statistic --mode random --probability 0.50000000000 -j KUBE-SEP-3O4DEWC3J3BWX72A
-A KUBE-SVC-QVYGXMY2EHJ7RAFE -m comment --comment "default/spring-test-service:" -j KUBE-SEP-G3MN2O4GTAP3PDK2
-A KUBE-SEP-LKOJGVHBLSDGTBKL -p tcp -m comment --comment "default/spring-test-service:" -m tcp -j DNAT --to-destination 10.244.1.115:8087
-A KUBE-SEP-3O4DEWC3J3BWX72A -p tcp -m comment --comment "default/spring-test-service:" -m tcp -j DNAT --to-destination 10.244.1.116:8087
-A KUBE-SEP-G3MN2O4GTAP3PDK2 -p tcp -m comment --comment "default/spring-test-service:" -m tcp -j DNAT --to-destination 10.244.1.117:8087

​ 这里当我们对300808081对口请求的分发就是通过KUBE-SVC-QVYGXMY2EHJ7RAFE来进行分发处理,其有三条分发策略,定义不同的概率0.33333333349对应的是LKOJGVHBLSDGTBKL也就是10.244.1.115:8087这个pod进行处理、0.50000000000对应的是3O4DEWC3J3BWX72A处理的是10.244.1.116:8087这个pod、剩下的就是G3MN2O4GTAP3PDK2这个默认的处理pod10.244.1.117:8087,然后我们可以看到这里对应的追加的3条nat数据KUBE-SEP-LKOJGVHBLSDGTBKLKUBE-SEP-3O4DEWC3J3BWX72AKUBE-SEP-G3MN2O4GTAP3PDK2,这3条就是修改请求,将其的目的地址修改为对应的pod引用的请求地址+端口。

​ 而对于3个pod地址路由的处理:

复制代码
[root@k8s-node1 feverasa]# ifconfig
cni0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1450
        inet 10.244.1.1  netmask 255.255.255.0  broadcast 10.244.1.255
        inet6 fe80::b03c:7aff:fed2:76d8  prefixlen 64  scopeid 0x20<link>
        ether b2:3c:7a:d2:76:d8  txqueuelen 1000  (Ethernet)
        RX packets 384  bytes 27505 (26.8 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 438  bytes 146544 (143.1 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
............
ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.127.129  netmask 255.255.255.0  broadcast 192.168.127.255
        inet6 fe80::7637:f8c5:6495:e97  prefixlen 64  scopeid 0x20<link>
        ether 00:0c:29:57:69:f9  txqueuelen 1000  (Ethernet)
        RX packets 2221  bytes 1332152 (1.2 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 1926  bytes 243378 (237.6 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

flannel.1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1450
        inet 10.244.1.0  netmask 255.255.255.255  broadcast 0.0.0.0
        inet6 fe80::e87a:45ff:fe75:653c  prefixlen 64  scopeid 0x20<link>
        ether ea:7a:45:75:65:3c  txqueuelen 0  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 27 overruns 0  carrier 0  collisions 0
...............
virbr0: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
        inet 192.168.122.1  netmask 255.255.255.0  broadcast 192.168.122.255
        ether 52:54:00:5d:fa:5d  txqueuelen 1000  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

[root@k8s-node1 feverasa]# ip route
default via 192.168.127.2 dev ens33 proto dhcp metric 100 
10.244.0.0/24 via 10.244.0.0 dev flannel.1 onlink 
10.244.1.0/24 dev cni0 proto kernel scope link src 10.244.1.1 
10.244.2.0/24 via 10.244.2.0 dev flannel.1 onlink 
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 
192.168.127.0/24 dev ens33 proto kernel scope link src 192.168.127.129 metric 100 
[root@k8s-node1 feverasa]# 

​ 这上面cni0这个就是对应k8s定义的容器网络接口规范CNI产生的一个虚拟网桥,用于连接同一节点上的容器或Pod。而对应CNI的具体实现,我目前安装的插件是flannel,flannel.1是插件flannel产生的。然后对于路由10.244.0.0/24 via 10.244.0.0 dev flannel.1 onlink ,则是网络发送的接口是flannel.1。具体内部对应不同Node的网络通信实现就是CNI与具体的网络实现相关,例如flannel,这里我们就不深究了,应该也是对于pod的ip也NodeIP的一些映射关系,或者其他其他的什么方案。当我们通过上面的这些nat规则,也能大概了解其的处理过程。

相关推荐
sunfove3 小时前
光网络的立交桥:光开关 (Optical Switch) 原理与主流技术解析
网络
唯情于酒3 小时前
Docker学习
学习·docker·容器
喵叔哟3 小时前
20.部署与运维
运维·docker·容器·.net
Kevin Wang7275 小时前
欧拉系统服务部署注意事项
网络·windows
min1811234565 小时前
深度伪造内容的检测与溯源技术
大数据·网络·人工智能
汤愈韬6 小时前
Full Cone Nat
网络·网络协议·网络安全·security·huawei
zbtlink6 小时前
现在还需要带电池的路由器吗?是用来干嘛的?
网络·智能路由器
桌面运维家6 小时前
vDisk配置漂移怎么办?VOI/IDV架构故障快速修复
网络·架构
dalerkd6 小时前
忙里偷闲叙-谈谈最近两年
网络·安全·web安全
汤愈韬7 小时前
NAT ALG (应用层网关)
网络·网络协议·网络安全·security·huawei