分析用户请求K8S里ingress-nginx提供的ingress流量路径

news2025/2/7 20:17:07

前言

本文是个人的小小见解,欢迎大佬指出我文章的问题,一起讨论进步~

我个人的疑问点

  1. 进入的流量是如何自动判断进入iptables的四表?
  2. k8s nodeport模式的原理?

一 本机环境介绍

节点名节点IPK8S版本CNI插件
Master192.168.44.1411.29.2calico(IPIP模式)
k8s-slave192.168.44.1421.29.2calico(IPIP模式)

二 容器介绍

ingress-nginx与 jupyter分别在2台服务器上

节点名节点IP容器名容器IPsvc地址
master192.168.44.141ingress-nginx-controller-556d4fff79-9jdmq172.16.219.89NodePort 10.100.104.51 80:80/TCP,443:443/TCP
k8s-slave192.168.44.142jupyter-deployment-57454c6795-xpgfk172.16.64.206ClusterIP 10.97.82.53 8888/TCP

三 请求方介绍

本地IP地址 10.250.0.34,通过添加/etc/hosts解析域名。因为ingress-nginx的SVC是Nodeport使用的是本机 80与443端口,所以访问 192.168.44.141 的80 443端口即可访问域名

192.168.44.141 						nginx.jcrose.com

四 请求链路分析

开始分析iptables iptables -nL -t nat

iptables是按照规则从上至下逐条匹配,开始分析Prerouting

流入的报文在路由决策

raw.PREROUTING -> mangle.PREROUTING -> nat.PREROUTING -> mangle.FORWARD -> filter.FORWARD -> mangle.POSTROUTING -> nat.POSTROUTING
  • raw.PREROUTING:设置流量标记,跳过连接跟踪(可选)。
  • mangle.PREROUTING:修改流量属性或标记流量(如 QoS 或 TTL)。
  • nat.PREROUTING:目标地址转换(DNAT),将流量重定向到正确的后端服务。
  • mangle.FORWARD:在转发流量时修改流量属性(如标记),用于流量控制。
  • filter.FORWARD:过滤流量,决定是否允许流量继续转发(基于安全策略)。
  • mangle.POSTROUTING:在出站流量时修改流量属性(如标记、TTL)。
  • nat.POSTROUTING:源地址转换(SNAT),确保流量从正确的 IP 地址发送出去。

最重要的2个部分 nat.PREROUTING 目标地址转换(DNAT),nat.POSTROUTING源地址转换(SNAT)。

4.1 Nat.PREROUTING链

IP: 192.168.44.141:80

host: jupyter.jcrose.com

Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination         
cali-PREROUTING  all  --  0.0.0.0/0            0.0.0.0/0            /* cali:6gwbT8clXdHdC1b1 */
KUBE-SERVICES  all  --  0.0.0.0/0            0.0.0.0/0            /* kubernetes service portals */
CNI-HOSTPORT-DNAT  all  --  0.0.0.0/0            0.0.0.0/0            ADDRTYPE match dst-type LOCAL

4.1.1 cali-PREROUTING规则

这里使用了 cali-fip-dnat(Calico 使用 IP-in-IP 或 VXLAN 隧道模式则不会使用该链)

Chain cali-PREROUTING (1 references)
target     prot opt source               destination         
cali-fip-dnat  all  --  0.0.0.0/0            0.0.0.0/0            /* cali:r6XmIziWUJsdOK6Z */

4.1.2 KUBE-SERVICES规则

规则如下

Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination
.............................         
KUBE-SERVICES  all  --  0.0.0.0/0            0.0.0.0/0            /* kubernetes service portals */
.............................

分析下面Chain KUBE-SERVICES的4条规则

Chain KUBE-SERVICES (2 references)
target     prot opt source               destination         
RETURN     all  --  127.0.0.0/8          0.0.0.0/0           
KUBE-MARK-MASQ  all  -- !172.16.0.0/12        0.0.0.0/0            /* Kubernetes service cluster ip + port for masquerade purpose */ match-set KUBE-CLUSTER-IP dst,dst
KUBE-NODE-PORT  all  --  0.0.0.0/0            0.0.0.0/0            ADDRTYPE match dst-type LOCAL	# 这里只有当请求的node IP是本机才会查询
ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0            match-set KUBE-CLUSTER-IP dst,dst
① RETURN规则

返回一切来自于 127.0.0.0本地请求

② KUBE-MARK-MASQ规则

match-set KUBE-CLUSTER-IP dst,dst 需要匹配ipset里面的地址是否存在。

因为 KUBE-CLUSTER-IP 是用于访问集群内部的IP而不是nodeport,因此此条规则也没有,跳过

Name: KUBE-CLUSTER-IP
Type: hash:ip,port
Revision: 6
Header: family inet hashsize 1024 maxelem 65536 bucketsize 12 initval 0xd824d3cd
Size in memory: 536
References: 3
Number of entries: 7
Members:
10.97.82.53,tcp:8888
10.96.0.2,udp:53
10.96.0.2,tcp:53
10.100.104.51,tcp:80
10.100.104.51,tcp:443
10.108.32.102,tcp:443
10.96.0.1,tcp:443


Chain KUBE-MARK-MASQ (3 references)
target     prot opt source               destination         
MARK       all  --  0.0.0.0/0            0.0.0.0/0            MARK or 0x4000
③ KUBE-NODE-PORT规则

规则如下

Chain KUBE-SERVICES (2 references)
............
KUBE-NODE-PORT  all  --  0.0.0.0/0            0.0.0.0/0            ADDRTYPE match dst-type LOCAL	# 这里只有当请求的node IP是本机才会查询
.................

因为本机IP为 192.168.44.141,请求过来的IP满足,因此进入下一条链 KUBE-NODE-PORT

Chain KUBE-NODE-PORT (1 references)
target     prot opt source               destination         
KUBE-MARK-MASQ  tcp  --  0.0.0.0/0            0.0.0.0/0            /* Kubernetes nodeport TCP port for masquerade purpose */ match-set KUBE-NODE-PORT-TCP dst

KUBE-NODE-PORT-TCP 的ipset规则如下

root@master:~# ipset --list KUBE-NODE-PORT-TCP
Name: KUBE-NODE-PORT-TCP
Type: bitmap:port
Revision: 3
Header: range 0-65535
Size in memory: 8264
References: 1
Number of entries: 2
Members:
80
443

match-set KUBE-NODE-PORT-TCP dst 匹配 ipset的 KUBE-NODE-PORT-TCP,如果匹配到了则进入下一步

KUBE-MARK-MASQ

这里对经过的流量打上标签 0x4000 继续在当前链中向下匹配下一个规则

Chain KUBE-MARK-MASQ (3 references)
target     prot opt source               destination         
MARK       all  --  0.0.0.0/0            0.0.0.0/0            MARK or 0x4000
④ ACCEPT规则(在上一步对流量打了标记之后来到这里)
ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0            match-set KUBE-CLUSTER-IP dst,dst

KUBE-CLUSTER-IP的ipset规则如下

root@master:~# ipset --list KUBE-CLUSTER-IP
Name: KUBE-CLUSTER-IP
Type: hash:ip,port
Revision: 6
Header: family inet hashsize 1024 maxelem 65536 bucketsize 12 initval 0xd824d3cd
Size in memory: 536
References: 3
Number of entries: 7
Members:
10.97.82.53,tcp:8888
10.96.0.2,udp:53
10.96.0.2,tcp:53
10.100.104.51,tcp:80
10.100.104.51,tcp:443
10.108.32.102,tcp:443
10.96.0.1,tcp:443

没有找到相关目标:192.168.44.141的规则,所以跳过这条规则

4.1.3 CNI-HOSTPORT-DNAT

这里去 CNI-DN-50c24a55650b462c3ecc9 匹配 80与443端口

Chain CNI-HOSTPORT-DNAT (2 references)
target     prot opt source               destination         
CNI-DN-50c24a55650b462c3ecc9  tcp  --  0.0.0.0/0            0.0.0.0/0            /* dnat name: "k8s-pod-network" id: "f5c87b58cf224ed0b1ce05b973b73ab23ffdd9c5f92904961b08e7b09a8e6373" */ multiport dports 80,443
① CNI-DN-50c24a55650b462c3ecc9规则

这里有4个打标记 0x2000 的规则,这里不一一解释,只分析 2个DNAT的规则

Chain CNI-DN-50c24a55650b462c3ecc9 (1 references)
target     prot opt source               destination         
CNI-HOSTPORT-SETMARK  tcp  --  172.16.219.89        0.0.0.0/0            tcp dpt:80		#MARK  172.16.219.71   MARK or 0x2000
CNI-HOSTPORT-SETMARK  tcp  --  127.0.0.1            0.0.0.0/0            tcp dpt:80		#MARK  172.16.219.71   MARK or 0x2000
DNAT       tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:80 to:172.16.219.89:80
CNI-HOSTPORT-SETMARK  tcp  --  172.16.219.89        0.0.0.0/0            tcp dpt:443	#MARK  172.16.219.71   MARK or 0x2000
CNI-HOSTPORT-SETMARK  tcp  --  127.0.0.1            0.0.0.0/0            tcp dpt:443	#MARK  172.16.219.71   MARK or 0x2000
DNAT       tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:443 to:172.16.219.89:443

这2个DNAT把来源的目标:192.168.44.141:80 (请求头 http://jupyter.jcrose.com) 修改为目标 172.16.219.89:80(ingress-nginx的 pod IP地址)

五 veth设备介绍

  1. Pod 流量的目的地是同一节点上的 Pod。
  2. Pod 流量的目的地是在不同节点上的 Pod。

整个工作流依赖于虚拟接口对和网桥,下面先来了解一下这部分的内容。

为了让一个 Pod 与其他 Pod 通信,它必须先访问节点的根命名空间。

通过虚拟以太网对来实现 Pod 和根命名空间的连接。

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

查看master节点上的网卡情况

ip a

3: kube-ipvs0: <BROADCAST,NOARP> mtu 1500 qdisc noop state DOWN group default 
    link/ether d6:37:7a:8c:62:e2 brd ff:ff:ff:ff:ff:ff
    inet 10.96.0.2/32 scope global kube-ipvs0
       valid_lft forever preferred_lft forever
    inet 10.97.82.53/32 scope global kube-ipvs0
       valid_lft forever preferred_lft forever
    inet 10.96.0.1/32 scope global kube-ipvs0
       valid_lft forever preferred_lft forever
    inet 10.100.104.51/32 scope global kube-ipvs0
       valid_lft forever preferred_lft forever
    inet 10.108.32.102/32 scope global kube-ipvs0
       valid_lft forever preferred_lft forever

在 Calico 的 IPIP 模式 中,kube-ipvs0tunl0eth0 网卡分别有不同的角色,它们与集群中的流量转发和路由机制密切相关。下面我会解释它们各自的作用以及它们之间的关系。

5.1 kube-ipvs0 网卡

  • 作用kube-ipvs0 是在 IPVS 模式 下由 kube-proxy 创建的虚拟网卡。它用于实现 Kubernetes 服务的负载均衡。它会处理从集群外部(或其他 Pod)流向服务虚拟 IP(VIP)的流量,并将这些流量根据负载均衡规则转发到服务的后端 Pod。

  • 工作原理

    • 在 Kubernetes 中,kube-proxy 使用 IPVS(IP Virtual Server) 来管理负载均衡。kube-ipvs0 是用来承载这些负载均衡规则的网络设备,流量经过 kube-ipvs0 时会被转发到相应的后端 Pod。
    • kube-ipvs0 会将流量从服务的虚拟 IP(VIP)转发到实际的 Pod IP(如 172.16.64.206)。

5.2 tunl0 网卡

  • 作用tunl0 是 Calico 在启用 IPIP 模式 时创建的虚拟网卡,用于支持 IPIP 隧道。当 Pod 需要通过 IPIP 隧道跨节点通信时,流量会通过 tunl0 网卡进行封装。

  • 工作原理

    • 在 Calico IPIP 模式下,当流量从一个节点上的 Pod 发往另一个节点上的 Pod 时,流量会通过 tunl0 网卡进行封装。
    • tunl0 网卡会将流量封装为 IPIP 包,将其发送到目标节点的 tunl0 网卡。在目标节点上,calico-node 会解封装这个 IPIP 包,将流量转发到目标 Pod。
    • tunl0 网卡本身并不用于直接接收或发送应用层数据流量,它只是一个传输层的封装/解封装网卡。

5.3 eth0 网卡

  • 作用eth0 是 Kubernetes 节点上的 物理网卡,用于与集群外部或其他节点进行通信。

  • 工作原理

    • eth0 是节点与外部网络的主要接口。它用于处理来自外部网络的流量或集群内的普通数据流量。
    • 如果 Pod 在同一节点上,它们的流量通常通过 eth0 进行路由,而不需要经过 IPIP 隧道。
    • eth0 也可能用于与其他节点之间的通信(比如集群外部的访问请求),但是这取决于集群的路由策略和 Calico 配置。

5.4 这三者之间的关系

  1. 服务流量的转发(kube-ipvs0

    • 当 Pod 或外部请求访问某个服务的虚拟 IP(例如 10.97.82.53),流量会首先到达节点上的 kube-ipvs0 网卡。
    • kube-ipvs0 根据 IPVS 规则将流量转发到后端 Pod 的真实 IP(如 172.16.64.206)。这个转发过程是在同一节点内进行的,不需要 IPIP 隧道。
  2. 跨节点的 Pod 通信(tunl0

    • 如果目标 Pod 位于 不同节点,流量就需要通过 IPIP 隧道 进行跨节点传输。这时,tunl0 网卡会参与流量的封装。
    • 例如,流量从 ingress-nginx-controller Pod 发出,目标 Pod 在其他节点,流量会通过 tunl0 封装为 IPIP 包,通过节点的 eth0 网卡发送到目标节点。在目标节点,tunl0 解封装流量并将其转发给目标 Pod。
  3. 物理节点通信(eth0

    • eth0 是用于节点之间的通信(比如节点间的路由和外部通信)。
    • 对于 IPIP 模式下的跨节点流量,eth0 作为物理网络接口用于承载封装的 IPIP 流量,帮助传输流量到目标节点。

5.5 流量路径示例

ingress-nginx-controller Pod 中,流量访问服务 10.97.82.53:8888,目标 Pod IP 是 172.16.64.206:8888,并且目标 Pod 位于 不同节点

  1. 流量经过 kube-ipvs0

    • 请求首先到达 kube-ipvs0 网卡,kube-proxy 将流量从服务虚拟 IP(10.97.82.53)转发到后端 Pod IP(172.16.64.206)。
  2. 流量需要跨节点传输,进入 tunl0 隧道

    • 由于目标 Pod 位于另一个节点,calico-node 在源节点通过 tunl0 网卡将流量封装为 IPIP 包。
  3. 流量通过 eth0 传输到目标节点

    • 封装后的 IPIP 包通过源节点的 eth0 网卡发送到目标节点。
  4. 目标节点通过 tunl0 解封装流量并转发给目标 Pod

    • 在目标节点,calico-node 通过 tunl0 解封装 IPIP 包,并将流量转发(ARP)目标 Pod 172.16.64.206:8888

5.6 总结

  • kube-ipvs0:由 kube-proxy 创建,负责处理服务虚拟 IP 的负载均衡,将流量转发到后端 Pod。
  • tunl0:由 Calico 创建,负责在跨节点通信时封装和解封装 IPIP 包,确保流量能够穿越节点之间的隧道。
  • eth0:物理网卡,用于节点间的通信,承载封装后的 IPIP 包并将流量传输到目标节点(ARP)。

它们之间的关系是:kube-ipvs0 负责服务负载均衡,tunl0 负责 IPIP 隧道的封装与解封装,而 eth0 作为物理网卡用于在节点之间传输流量。

需要跨节点的时候,需要进行tunl0进行封包

1.png

六 ingress-nginx容器处理流量

容器 ingress-nginx-controller-556d4fff79-9jdmq

IP 172.16.219.89收到请求

进入容器 查看 nginx location规则找到 目标域名 jupyter.jcrose.com

kubectl exec -it ingress-nginx-controller-556d4fff79-9jdmq -n ingress-nginx -- cat /etc/nginx/nginx.conf

server jupyter.jcrose.com 部分指明了 namespace,service_name,service_port

        ## start server jupyter.jcrose.com
        server {
                server_name jupyter.jcrose.com ;

                listen 80  ;
                listen [::]:80  ;
                listen 443  ssl http2 ;
                listen [::]:443  ssl http2 ;

                set $proxy_upstream_name "-";

                ssl_certificate_by_lua_block {
                        certificate.call()
                }

                location / {

                        set $namespace      "default";
                        set $ingress_name   "demo-jupyter";
                        set $service_name   "jupyter-service";
                        set $service_port   "8888";
                        set $location_path  "/";
                        set $global_rate_limit_exceeding n;

                        rewrite_by_lua_block {
                                lua_ingress.rewrite({
                                        force_ssl_redirect = false,
                                        ssl_redirect = true,
                                        force_no_ssl_redirect = false,
                                        preserve_trailing_slash = false,
                                        use_port_in_redirects = false,
                                        global_throttle = { namespace = "", limit = 0, window_size = 0, key = { }, ignored_cidrs = { } },
                                })
                                balancer.rewrite()
                                plugins.run()
                        }

                        # be careful with `access_by_lua_block` and `satisfy any` directives as satisfy any
                        # will always succeed when there's `access_by_lua_block` that does not have any lua code doing `ngx.exit(ngx.DECLINED)`
                        # other authentication method such as basic auth or external auth useless - all requests will be allowed.
                        #access_by_lua_block {
                        #}

                        header_filter_by_lua_block {
                                lua_ingress.header()
                                plugins.run()
                        }

                        body_filter_by_lua_block {
                                plugins.run()
                        }

                        log_by_lua_block {
                                balancer.log()

                                monitor.call()

                                plugins.run()
                        }

                        port_in_redirect off;

                        set $balancer_ewma_score -1;
                        set $proxy_upstream_name "default-jupyter-service-8888";
                        set $proxy_host          $proxy_upstream_name;
                        set $pass_access_scheme  $scheme;

                        set $pass_server_port    $server_port;

                        set $best_http_host      $http_host;
                        set $pass_port           $pass_server_port;

                        set $proxy_alternative_upstream_name "";

                        client_max_body_size                    1m;

                        proxy_set_header Host                   $best_http_host;

                        # Pass the extracted client certificate to the backend

                        # Allow websocket connections
                        proxy_set_header                        Upgrade           $http_upgrade;

                        proxy_set_header                        Connection        $connection_upgrade;

                        proxy_set_header X-Request-ID           $req_id;
                        proxy_set_header X-Real-IP              $remote_addr;

                        proxy_set_header X-Forwarded-For        $remote_addr;

                        proxy_set_header X-Forwarded-Host       $best_http_host;
                        proxy_set_header X-Forwarded-Port       $pass_port;
                        proxy_set_header X-Forwarded-Proto      $pass_access_scheme;
                        proxy_set_header X-Forwarded-Scheme     $pass_access_scheme;

                        proxy_set_header X-Scheme               $pass_access_scheme;

                        # Pass the original X-Forwarded-For
                        proxy_set_header X-Original-Forwarded-For $http_x_forwarded_for;

                        # mitigate HTTPoxy Vulnerability
                        # https://www.nginx.com/blog/mitigating-the-httpoxy-vulnerability-with-nginx/
                        proxy_set_header Proxy                  "";

                        # Custom headers to proxied server

                        proxy_connect_timeout                   5s;
                        proxy_send_timeout                      60s;
                        proxy_read_timeout                      60s;

                        proxy_buffering                         off;
                        proxy_buffer_size                       4k;
                        proxy_buffers                           4 4k;

                        proxy_max_temp_file_size                1024m;

                        proxy_request_buffering                 on;
                        proxy_http_version                      1.1;

                        proxy_cookie_domain                     off;
                        proxy_cookie_path                       off;

                        # In case of errors try the next upstream server before returning an error
                        proxy_next_upstream                     error timeout;
                        proxy_next_upstream_timeout             0;
                        proxy_next_upstream_tries               3;

                        proxy_pass http://upstream_balancer;

                        proxy_redirect                          off;

                }

        }
        ## end server jupyter.jcrose.com

七 ingress-nginx容器请求流量

在nginx location获取到的数据如下

       set $namespace      "default";
       set $ingress_name   "demo-jupyter";
       set $service_name   "jupyter-service";
       set $service_port   "8888";

发送的请求

jupyter-service.default.svc.cluster.local:8888

7.1 由coredns获取到 ip地址

服务名方向节点IPSVC地址端口容器IP
ingress-nginx-controller-556d4fff79-9jdmq源地址192.168.44.14110.100.104.5180(本次使用的)172.16.219.89(本次使用的)
jupyter-deployment-57454c6795-xpgfk目标地址192.168.44.14210.97.82.53(本次使用的)8888172.16.64.206

来源: 172.16.219.89:80(ingress-nginx的 pod ip以及端口)

请求的地址 10.97.82.53:8888(jupyter的svc地址)

7.2 查看该容器的路由表

ingress-nginx-controller-556d4fff79-9jdmq:/etc/nginx$ route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         169.254.1.1     0.0.0.0         UG    0      0        0 eth0
169.254.1.1     0.0.0.0         255.255.255.255 UH    0      0        0 eth0

可以看到 0.0.0.0 指向了 169.254.1.1(eth0),查看容器ip地址

ingress-nginx-controller-556d4fff79-9jdmq:/etc/nginx$ ip addr 
4: eth0@if9: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1480 qdisc noqueue state UP 
    link/ether 3a:0d:53:fb:ec:92 brd ff:ff:ff:ff:ff:ff
    inet 172.16.219.83/32 scope global eth0
       valid_lft forever preferred_lft forever
    inet6 fe80::380d:53ff:fefb:ec92/64 scope link 
       valid_lft forever preferred_lft forever

这里容器获取的是 /32 位主机地址,表示将容器 A 作为一个单点的局域网。

eth0@if9为在宿主机上创建的虚拟网桥的一端,另一端为 calibfbdf93cf51。

calibfbdf93cf51又开启了proxy arp,所以流量就从pod内部到达了master 节点

解释:

当一个数据包的目的地址不是本机时,就会查询路由表,从路由表中查到网关后,它首先会通过 ARP获得网关的 MAC 地址,然后在发出的网络数据包中将目标 MAC 改为网关的 MAC,而网关的 IP 地址不会出现在任何网络包头中。也就是说,没有人在乎这个 IP 地址究竟是什么,只要能找到对应的 MAC 地址,能响应 ARP 就行了。

Calico 利用了网卡的代理 ARP 功能。代理 ARP 是 ARP 协议的一个变种,当 ARP 请求目标跨网段时,网关设备收到此 ARP 请求,会用自己的 MAC 地址返回给请求者,这便是代理 ARP(Proxy ARP)。

图片

八 从Master节点流出的流量分析

服务名方向节点IPIP地址端口
ingress-nginx-controller-556d4fff79-9jdmq源地址192.168.44.141172.16.219.89(使用)80
jupyter-deployment-57454c6795-xpgfk目标地址192.168.44.14210.97.82.53(使用)8888

容器eth0 => calico虚拟网卡 => ipvs网卡 => 需要跨节点则 tunl网卡,封装ip(可选,不一定会用它) => 宿主机 eth0

  • 查看svc网段是否通过tunl0网卡的网段? 查看本机的 ip a的ipvs网卡,存在这个IP 10.97.82.53

8.1 查看ipvsadm的负载均衡

root@master:~# ipvsadm --list
IP Virtual Server version 1.2.1 (size=4096)
Prot LocalAddress:Port Scheduler Flags
  -> RemoteAddress:Port           Forward Weight ActiveConn InActConn
.............
TCP  10.97.82.53:8888 rr
  -> 172.16.64.206:8888           Masq    1      0          0    
.....................

得到目标的pod地址,172.16.64.206

8.2 master节点的路由表

Master节点的路由表,可以看到有一条规则是

Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
172.16.64.192   192.168.44.142  255.255.255.192 UG    0      0        0 tunl0

给定的网络信息是:

  • 网络地址:172.16.64.192
  • 子网掩码:255.255.255.192

首先,可以将子网掩码转换为网络范围。255.255.255.192(即 /26)表示每个子网有64个地址(2^6)。因此,这个网段的范围是:

  • 网络地址:172.16.64.192
  • 广播地址:172.16.64.255
  • 可用的IP范围:172.16.64.193 到 172.16.64.254

因此,该网段的IP范围是 172.16.64.193 到 172.16.64.254

目标 172.16.64.206 IP存在 于这个IP范围,因此主机的下一跳地址为:192.168.44.142主机的 Tunl0网卡。

root@master:~# route -n
Kernel IP routing table
Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
0.0.0.0         192.168.44.2    0.0.0.0         UG    100    0        0 ens33
172.16.64.192   192.168.44.142  255.255.255.192 UG    0      0        0 tunl0
172.16.219.64   0.0.0.0         255.255.255.192 U     0      0        0 *
172.16.219.87   0.0.0.0         255.255.255.255 UH    0      0        0 cali141c3047c6b
172.16.219.88   0.0.0.0         255.255.255.255 UH    0      0        0 cali780b4ee0aa6
172.16.219.89   0.0.0.0         255.255.255.255 UH    0      0        0 calibfbdf93cf51
172.16.219.90   0.0.0.0         255.255.255.255 UH    0      0        0 calie7fabde01ba
192.168.44.0    0.0.0.0         255.255.255.0   U     100    0        0 ens33
192.168.44.2    0.0.0.0         255.255.255.255 UH    100    0        0 ens33

跨节点

  • IPIP模式下,node间的Pod访问会使用IPIP技术对出node的ip报进行隧道封装
  • Pod的ip都是由calico-node设置的IP地址池进行分配的,Calico会为每一个node分配一小段网络。

8.3 对tunl0网卡进行分析

  1. IPIP封装

    • 由于172.16.64.206位于另一个节点,Calico会使用IPIP隧道将流量封装。
    • 原始IP包(源IP为172.16.219.89,目标IP为172.16.64.206)会被封装在一个新的IP包中。
    • 新的IP包的源IP是192.168.44.141master节点的IP),目标IP是192.168.44.142slave节点的IP)。
    • 这个新的IP包会通过tunl0网卡发送出去。

封装后的IP包结构

  • 外层IP头

    • 源IP:192.168.44.141master节点的IP)
    • 目标IP:192.168.44.142slave节点的IP)
    • 协议类型:IPIP(协议号4)
  • 内层IP头

    • 源IP:172.16.219.89(原始Pod的IP)
    • 目标IP:172.16.64.206(目标Pod的IP)
    • 协议类型:TCP/UDP等(取决于原始流量)

九 k8s-slave节点分析

  • 流量到达slave节点

    • slave节点192.168.44.142接收到这个IPIP封装的包。
    • tunl0网卡会解封装这个包,提取出原始的IP包(源IP为172.16.219.89,目标IP为172.16.64.206)。
    • 然后,slave节点会根据本地的路由表将流量转发到目标Pod 172.16.64.206

2.png
3.png

省流版总结

源主机源端口目标地址目标端口注释
发起者10.250.0.32随机端口jupyter.jcrose.com80
Windows /etc/host解析域名10.250.0.32随机端口192.168.44.14180
Iptables规则解析Nodeport192.168.44.141随机端口172.16.219.8980通过nodeport的相关iptables获取到 ingress-nginx的pod IP
ingress-nginx发起请求172.16.219.898010.97.82.538888通过 nginx.conf获取 jupyter.jcrose.com的信息,通过coredns获取到 SVC地址。
ingress-nginx发起请求192.168.44.141随机端口192.168.44.1428888这里流量经过了tunl0网卡把封包,把源地址和目标地址都修改为了目标主机
jupyter容器处理流量172.16.219.89随机端口172.16.64.2068888经过slave tunl0网卡的流量自动解包 获取到真实的源地址和目标地址(ingress-nginx的配置可以修改显示源地址或者容器地址)

查看 ingress-nginx-controller的日志证实确实如下

192.168.44.141 - - [24/Jan/2025:09:23:17 +0000] "GET / HTTP/1.1" 302 0 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36" 459 0.005 [default-jupyter-service-8888] [] 172.16.64.206:8888 0 0.004 302 5a0bfabc5d693d4a7cae94921207f78c
192.168.44.141 - - [24/Jan/2025:09:23:17 +0000] "GET /lab? HTTP/1.1" 302 0 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36" 463 0.002 [default-jupyter-service-8888] [] 172.16.64.206:8888 0 0.001 302 b4218276d6b133d8c68c237ea856fe79
192.168.44.141 - - [24/Jan/2025:09:23:17 +0000] "GET /login?next=%2Flab%3F HTTP/1.1" 200 6251 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36" 479 0.022 [default-jupyter-service-8888] [] 172.16.64.206:8888 6251 0.022 200 24d3466f6b14ca223118edb7b58fd367

源地址: 192.168.44.141(端口这里看不到)

目标地址: 172.16.64.206:8888

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

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

相关文章

初阶数据结构:树---堆

目录 一、树的概念 二、树的构成 &#xff08;一&#xff09;、树的基本组成成分 &#xff08;二&#xff09;、树的实现方法 三、树的特殊结构------二叉树 &#xff08;一&#xff09;、二叉树的概念 &#xff08;二&#xff09;、二叉树的性质 &#xff08;三&#…

feign 远程调用详解

在平常的开发工作中&#xff0c;我们经常需要跟其他系统交互&#xff0c;比如调用用户系统的用户信息接口、调用支付系统的支付接口等。那么&#xff0c;我们应该通过什么方式进行系统之间的交互呢&#xff1f;今天&#xff0c;简单来总结下 feign 的用法。 1&#xff1a;引入依…

Sentinel的安装和做限流的使用

一、安装 Release v1.8.3 alibaba/Sentinel GitHubA powerful flow control component enabling reliability, resilience and monitoring for microservices. (面向云原生微服务的高可用流控防护组件) - Release v1.8.3 alibaba/Sentinelhttps://github.com/alibaba/Senti…

讯飞智作 AI 配音技术浅析(三):自然语言处理

自然语言处理&#xff08;NLP&#xff09;是讯飞智作 AI 配音技术的重要组成部分&#xff0c;负责将输入的文本转换为机器可理解的格式&#xff0c;并提取出文本的语义和情感信息&#xff0c;以便生成自然、富有表现力的语音。 一、基本原理 讯飞智作 AI 配音的 NLP 技术主要包…

wxWidgets生成HTML文件,带图片转base64数据

编译环境大家可以看我之前的文章,CodeBlocks + msys2 + wx3.2,win10 这里功能就是生成HTML文件,没用HTML库,因为是自己固定的格式,图片是一个vector,可以动态改变数量的。 效果如下: #include <wx/string.h> #include <wx/file.h> #include <wx/ima…

【matlab基本使用笔记】

ctrl a i 代码格式化 fzero求非线性函数的根 arrayfun将函数应用于每个数组元素 format long长格式输出 format long g取消科学计数法 linspace logspace 一、界面使用 1.创建matlab脚本 利用.m后缀的脚本文件&#xff08;又称为m文件&#xff09;编程&#xff1a; 点击…

机器学习--python基础库之Matplotlib (1) 超级详细!!!

机器学习--python基础库Matplotlib 机器学习--python基础库Matplotlib0 介绍1 实现基础绘图-某城市温度变化图1.1绘制基本图像1.2实现一些其他功能 2 再一个坐标系中绘制多个图像3 多个坐标系显示-plt.subplots(面向对象的画图方法)4 折线图的应用场景 机器学习–python基础库M…

bat脚本实现自动化漏洞挖掘

bat脚本 BAT脚本是一种批处理文件&#xff0c;可以在Windows操作系统中自动执行一系列命令。它们可以简化许多日常任务&#xff0c;如文件操作、系统配置等。 bat脚本执行命令 echo off#下面写要执行的命令 httpx 自动存活探测 echo off httpx.exe -l url.txt -o 0.txt nuc…

一文解释nn、nn.Module与nn.functional的用法与区别

&#x1f308; 个人主页&#xff1a;十二月的猫-CSDN博客 &#x1f525; 系列专栏&#xff1a; &#x1f3c0;零基础入门PyTorch框架_十二月的猫的博客-CSDN博客 &#x1f4aa;&#x1f3fb; 十二月的寒冬阻挡不了春天的脚步&#xff0c;十二点的黑夜遮蔽不住黎明的曙光 目录 …

Unity VideoPlayer播放视屏不清晰的一种情况

VideoPlayer的Rnder Texture可以设置Size,如果你的视屏是1920*1080那么就设置成1920*1080。 如果设置成其他分辨率比如800*600会导致视屏不清晰。

【玩转全栈】--创建一个自己的vue项目

目录 vue介绍 创建vue项目 vue页面介绍 element-plus组件库 启动项目 vue介绍 Vue.js 是一款轻量级、易于上手的前端 JavaScript 框架&#xff0c;旨在简化用户界面的开发。它采用了响应式数据绑定和组件化的设计理念&#xff0c;使得开发者可以通过声明式的方式轻松管理数据和…

揭秘区块链隐私黑科技:零知识证明如何改变未来

文章目录 1. 引言&#xff1a;什么是零知识证明&#xff1f;2. 零知识证明的核心概念与三大属性2.1 完备性&#xff08;Completeness&#xff09;2.2 可靠性&#xff08;Soundness&#xff09;2.3 零知识性&#xff08;Zero-Knowledge&#xff09; 3. 零知识证明的工作原理4. 零…

堆的实现——堆的应用(堆排序)

文章目录 1.堆的实现2.堆的应用--堆排序 大家在学堆的时候&#xff0c;需要有二叉树的基础知识&#xff0c;大家可以看我的二叉树文章&#xff1a;二叉树 1.堆的实现 如果有⼀个关键码的集合 K {k0 , k1 , k2 , …&#xff0c;kn−1 } &#xff0c;把它的所有元素按完全⼆叉树…

Ubuntu20.04 本地部署 DeepSeek-R1

一、下载ollama 打开 ollama链接&#xff0c;直接终端运行提供的命令即可。如获取的命令如下&#xff1a; curl -fsSL https://ollama.com/install.sh | sh确保是否安装成功可在终端输入如下命令&#xff1a; ollama -v注意&#xff1a; 如遇到Failed to connect to github.…

2025年2月6日笔记

第 12 届蓝桥杯 C 青少组中 / 高级组选拔赛&#xff08; STEMA &#xff09; 2020 年 11 月 22 日 真题第一题 解题思路&#xff1a; 第一&#xff1a;因为有整数集合的求和字样&#xff08;所以用for循环来做&#xff09; 第二&#xff1a;题中让我们累加1到N&#xff0c;所…

Linux: 网络基础

1.协议 为什么要有协议&#xff1a;减少通信成本。所有的网络问题&#xff0c;本质是传输距离变长了。 什么是协议&#xff1a;用计算机语言表达的约定。 2.分层 软件设计方面的优势—低耦合。 一般我们的分层依据&#xff1a;功能比较集中&#xff0c;耦合度比较高的模块层…

CSS 背景与边框:从基础到高级应用

CSS 背景与边框&#xff1a;从基础到高级应用 1. CSS 背景样式1.1 背景颜色示例代码&#xff1a;设置背景颜色 1.2 背景图像示例代码&#xff1a;设置背景图像 1.3 控制背景平铺行为示例代码&#xff1a;控制背景平铺 1.4 调整背景图像大小示例代码&#xff1a;调整背景图像大小…

大学资产管理系统中的下载功能设计与实现

大学资产管理系统是高校信息化建设的重要组成部分&#xff0c;它负责记录和管理学校内所有固定资产的信息。随着信息技术的发展&#xff0c;下载功能成为提高资产管理效率的关键环节之一。 系统架构的设计是实现下载功能的基础。一个良好的系统架构能够确保数据的高效传输和存储…

园区网设计与实战

想做一个自己学习的有关的csdn账号&#xff0c;努力奋斗......会更新我计算机网络实验课程的所有内容&#xff0c;还有其他的学习知识^_^&#xff0c;为自己巩固一下所学知识。 我是一个萌新小白&#xff0c;有误地方请大家指正&#xff0c;谢谢^_^ 文章目录 前言 这个实验主…

DeepSeek-R1 本地电脑部署 Windows系统 【轻松简易】

本文分享在自己的本地电脑部署 DeepSeek&#xff0c;而且轻松简易&#xff0c;快速上手。 这里借助Ollama工具&#xff0c;在Windows系统中进行大模型部署~ 1、安装Ollama 来到官网地址&#xff1a;Download Ollama on macOS 点击“Download for Windows”下载安装包&#x…