分析用户请求K8S里ingress-nginx提供的ingress流量路径
前言
本文是个人的小小见解,欢迎大佬指出我文章的问题,一起讨论进步~
我个人的疑问点
- 进入的流量是如何自动判断进入iptables的四表?
- k8s nodeport模式的原理?
一 本机环境介绍
节点名 | 节点IP | K8S版本 | CNI插件 |
---|---|---|---|
Master | 192.168.44.141 | 1.29.2 | calico(IPIP模式) |
k8s-slave | 192.168.44.142 | 1.29.2 | calico(IPIP模式) |
二 容器介绍
ingress-nginx与 jupyter分别在2台服务器上
节点名 | 节点IP | 容器名 | 容器IP | svc地址 |
---|---|---|---|---|
master | 192.168.44.141 | ingress-nginx-controller-556d4fff79-9jdmq | 172.16.219.89 | NodePort 10.100.104.51 80:80/TCP,443:443/TCP |
k8s-slave | 192.168.44.142 | jupyter-deployment-57454c6795-xpgfk | 172.16.64.206 | ClusterIP 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:443Chain 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设备介绍
- Pod 流量的目的地是同一节点上的 Pod。
- Pod 流量的目的地是在不同节点上的 Pod。
整个工作流依赖于虚拟接口对和网桥,下面先来了解一下这部分的内容。
为了让一个 Pod 与其他 Pod 通信,它必须先访问节点的根命名空间。
通过虚拟以太网对来实现 Pod 和根命名空间的连接。
这些虚拟接口设备(veth 中的 v)连接并充当两个命名空间之间的隧道。
查看master节点上的网卡情况
ip a3: 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:ffinet 10.96.0.2/32 scope global kube-ipvs0valid_lft forever preferred_lft foreverinet 10.97.82.53/32 scope global kube-ipvs0valid_lft forever preferred_lft foreverinet 10.96.0.1/32 scope global kube-ipvs0valid_lft forever preferred_lft foreverinet 10.100.104.51/32 scope global kube-ipvs0valid_lft forever preferred_lft foreverinet 10.108.32.102/32 scope global kube-ipvs0valid_lft forever preferred_lft forever
在 Calico 的 IPIP 模式 中,kube-ipvs0
、tunl0
和 eth0
网卡分别有不同的角色,它们与集群中的流量转发和路由机制密切相关。下面我会解释它们各自的作用以及它们之间的关系。
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.20
6)。
- 在 Kubernetes 中,
5.2 tunl0 网卡
-
作用:
tunl0
是 Calico 在启用 IPIP 模式 时创建的虚拟网卡,用于支持 IPIP 隧道。当 Pod 需要通过 IPIP 隧道跨节点通信时,流量会通过tunl0
网卡进行封装。 -
工作原理:
- 在 Calico IPIP 模式下,当流量从一个节点上的 Pod 发往另一个节点上的 Pod 时,流量会通过
tunl0
网卡进行封装。 tunl0
网卡会将流量封装为 IPIP 包,将其发送到目标节点的tunl0
网卡。在目标节点上,calico-node
会解封装这个 IPIP 包,将流量转发到目标 Pod。tunl0
网卡本身并不用于直接接收或发送应用层数据流量,它只是一个传输层的封装/解封装网卡。
- 在 Calico IPIP 模式下,当流量从一个节点上的 Pod 发往另一个节点上的 Pod 时,流量会通过
5.3 eth0 网卡
-
作用:
eth0
是 Kubernetes 节点上的 物理网卡,用于与集群外部或其他节点进行通信。 -
工作原理:
eth0
是节点与外部网络的主要接口。它用于处理来自外部网络的流量或集群内的普通数据流量。- 如果 Pod 在同一节点上,它们的流量通常通过
eth0
进行路由,而不需要经过 IPIP 隧道。 - eth0 也可能用于与其他节点之间的通信(比如集群外部的访问请求),但是这取决于集群的路由策略和 Calico 配置。
5.4 这三者之间的关系
-
服务流量的转发(
kube-ipvs0
):- 当 Pod 或外部请求访问某个服务的虚拟 IP(例如
10.97.82.53
),流量会首先到达节点上的kube-ipvs0
网卡。 kube-ipvs0
根据 IPVS 规则将流量转发到后端 Pod 的真实 IP(如172.16.64.20
6)。这个转发过程是在同一节点内进行的,不需要 IPIP 隧道。
- 当 Pod 或外部请求访问某个服务的虚拟 IP(例如
-
跨节点的 Pod 通信(
tunl0
):- 如果目标 Pod 位于 不同节点,流量就需要通过 IPIP 隧道 进行跨节点传输。这时,
tunl0
网卡会参与流量的封装。 - 例如,流量从
ingress-nginx-controller
Pod 发出,目标 Pod 在其他节点,流量会通过tunl0
封装为 IPIP 包,通过节点的eth0
网卡发送到目标节点。在目标节点,tunl0
解封装流量并将其转发给目标 Pod。
- 如果目标 Pod 位于 不同节点,流量就需要通过 IPIP 隧道 进行跨节点传输。这时,
-
物理节点通信(
eth0
):eth0
是用于节点之间的通信(比如节点间的路由和外部通信)。- 对于 IPIP 模式下的跨节点流量,
eth0
作为物理网络接口用于承载封装的 IPIP 流量,帮助传输流量到目标节点。
5.5 流量路径示例
在 ingress-nginx-controller
Pod 中,流量访问服务 10.97.82.53:8888
,目标 Pod IP 是 172.16.64.206:8888
,并且目标 Pod 位于 不同节点:
-
流量经过
kube-ipvs0
:- 请求首先到达
kube-ipvs0
网卡,kube-proxy
将流量从服务虚拟 IP(10.97.82.53
)转发到后端 Pod IP(172.16.64.20
6)。
- 请求首先到达
-
流量需要跨节点传输,进入
tunl0
隧道:- 由于目标 Pod 位于另一个节点,
calico-node
在源节点通过tunl0
网卡将流量封装为 IPIP 包。
- 由于目标 Pod 位于另一个节点,
-
流量通过
eth0
传输到目标节点:- 封装后的 IPIP 包通过源节点的
eth0
网卡发送到目标节点。
- 封装后的 IPIP 包通过源节点的
-
目标节点通过
tunl0
解封装流量并转发给目标 Pod:- 在目标节点,
calico-node
通过tunl0
解封装 IPIP 包,并将流量转发(ARP)目标 Pod172.16.64.206:8888
。
- 在目标节点,
5.6 总结
kube-ipvs0
:由kube-proxy
创建,负责处理服务虚拟 IP 的负载均衡,将流量转发到后端 Pod。tunl0
:由 Calico 创建,负责在跨节点通信时封装和解封装 IPIP 包,确保流量能够穿越节点之间的隧道。eth0
:物理网卡,用于节点间的通信,承载封装后的 IPIP 包并将流量传输到目标节点(ARP)。
它们之间的关系是:kube-ipvs0
负责服务负载均衡,tunl0
负责 IPIP 隧道的封装与解封装,而 eth0
作为物理网卡用于在节点之间传输流量。
需要跨节点的时候,需要进行tunl0进行封包
六 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.comserver {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 connectionsproxy_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-Forproxy_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 serverproxy_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 errorproxy_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地址
服务名 | 方向 | 节点IP | SVC地址 | 端口 | 容器IP |
---|---|---|---|---|---|
ingress-nginx-controller-556d4fff79-9jdmq | 源地址 | 192.168.44.141 | 10.100.104.51 | 80(本次使用的) | 172.16.219.89(本次使用的) |
jupyter-deployment-57454c6795-xpgfk | 目标地址 | 192.168.44.142 | 10.97.82.53(本次使用的) | 8888 | 172.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:ffinet 172.16.219.83/32 scope global eth0valid_lft forever preferred_lft foreverinet6 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节点流出的流量分析
服务名 | 方向 | 节点IP | IP地址 | 端口 |
---|---|---|---|---|
ingress-nginx-controller-556d4fff79-9jdmq | 源地址 | 192.168.44.141 | 172.16.219.89(使用) | 80 |
jupyter-deployment-57454c6795-xpgfk | 目标地址 | 192.168.44.142 | 10.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网卡进行分析
-
IPIP封装:
- 由于
172.16.64.206
位于另一个节点,Calico会使用IPIP隧道将流量封装。 - 原始IP包(源IP为
172.16.219.
89,目标IP为172.16.64.206
)会被封装在一个新的IP包中。 - 新的IP包的源IP是
192.168.44.141
(master
节点的IP),目标IP是192.168.44.142
(slave
节点的IP)。 - 这个新的IP包会通过
tunl0
网卡发送出去。
- 由于
封装后的IP包结构
-
外层IP头:
- 源IP:
192.168.44.141
(master
节点的IP) - 目标IP:
192.168.44.142
(slave
节点的IP) - 协议类型:IPIP(协议号4)
- 源IP:
-
内层IP头:
- 源IP:
172.16.219.
89(原始Pod的IP) - 目标IP:
172.16.64.206
(目标Pod的IP) - 协议类型:TCP/UDP等(取决于原始流量)
- 源IP:
九 k8s-slave节点分析
-
流量到达
slave
节点:slave
节点192.168.44.142
接收到这个IPIP封装的包。tunl0
网卡会解封装这个包,提取出原始的IP包(源IP为172.16.219.
89,目标IP为172.16.64.206
)。- 然后,
slave
节点会根据本地的路由表将流量转发到目标Pod172.16.64.206
。
省流版总结
源主机 | 源端口 | 目标地址 | 目标端口 | 注释 | |
---|---|---|---|---|---|
发起者 | 10.250.0.32 | 随机端口 | jupyter.jcrose.com | 80 | |
Windows /etc/host解析域名 | 10.250.0.32 | 随机端口 | 192.168.44.141 | 80 | |
Iptables规则解析Nodeport | 192.168.44.141 | 随机端口 | 172.16.219.89 | 80 | 通过nodeport的相关iptables获取到 ingress-nginx的pod IP |
ingress-nginx发起请求 | 172.16.219.89 | 80 | 10.97.82.53 | 8888 | 通过 nginx.conf获取 jupyter.jcrose.com的信息,通过coredns获取到 SVC地址。 |
ingress-nginx发起请求 | 192.168.44.141 | 随机端口 | 192.168.44.142 | 8888 | 这里流量经过了tunl0网卡把封包,把源地址和目标地址都修改为了目标主机 |
jupyter容器处理流量 | 172.16.219.89 | 随机端口 | 172.16.64.206 | 8888 | 经过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
相关文章:
分析用户请求K8S里ingress-nginx提供的ingress流量路径
前言 本文是个人的小小见解,欢迎大佬指出我文章的问题,一起讨论进步~ 我个人的疑问点 进入的流量是如何自动判断进入iptables的四表?k8s nodeport模式的原理? 一 本机环境介绍 节点名节点IPK8S版本CNI插件Master192.168.44.1…...
TensorFlow是个啥玩意?
TensorFlow是一个开源的机器学习框架,由Google开发。它可以帮助开发者构建和训练各种机器学习模型,包括神经网络和深度学习模型。TensorFlow的设计理念是使用数据流图来表示计算过程,其中节点表示数学运算,边表示数据流动。 Tens…...
初识C语言、C语言的学习方向总述与入门
目录 1. 什么是C语言? 2. 第一个C语言程序 3. 数据类型 4. 变量、常量 4.1 定义变量的方法 4.2 变量的命名 4.3 变量的分类 4.4 变量的作用域和生命周期 4.5 常量 5. 字符串转义字符注释 5.1 字符串 5.2 转义字符 6. 注释 7. 选择语句 8. 循环语句 …...
零基础学习书生.浦语大模型-入门岛
第一关:Linux基础知识 任务一:Cursor连接SSH运行代码 使用Remote - SSH插件即可 运行指令 python hello_world.py端口映射 ssh -p 46561 rootssh.intern-ai.org.cn -CNg -L 7860:127.0.0.1:7860 -o StrictHostKeyCheckingno 注:46561&a…...
【R语言】获取数据
R语言自带2种数据存储格式:*.RData和*.rds。 这两者的区别是:前者既可以存储数据,也可以存储当前工作空间中的所有变量,属于非标准化存储;后者仅用于存储单个R对象,且存储时可以创建标准化档案,…...
MongoDB学习笔记-解析jsonCommand内容
如果需要屏蔽其他项目对MongoDB的直接访问操作,统一由一个入口访问操作MongoDB,可以考虑直接传入jsonCommand语句解析执行。 相关依赖包 <!-- SpringBootDataMongodb依赖包 --> <dependency><groupId>org.springframework.boot</…...
国产编辑器EverEdit - 工具栏说明
1 工具栏 1.1 应用场景 当用户想显示/隐藏界面的标签栏、工具栏、状态栏、主菜单等界面元素时,可以通过EverEdit的菜单选项进行设置。 1.2 使用方法 选择菜单查看 -> 工具栏,在工具栏的子菜单中选择勾选或去掉勾选对应的选项。 标签栏࿱…...
linux 函数 sem_init () 信号量、sem_destroy()
(1) (2) 代码举例: #include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <semaphore.h> #include <unistd.h>sem_t semaphore;void* thread_function(void* arg) …...
制造业设备状态监控与生产优化实战:基于SQL的序列分析与状态机建模
目录 1. 背景与挑战 2. 数据建模与采集 2.1 数据表设计 设备状态表(记录设备实时状态变更)...
密码学的数学基础1-素数和RSA加密
数学公式推导是密码学的基础, 故开一个新的课题 – 密码学的数学基础系列 素数 / 质数 质数又称素数。 一个大于1的自然数,除了1和它自身外,不能被其他自然数整除的数叫做质数;否则称为合数(规定1既不是质数也不是合数࿰…...
C++ Primer 算术运算符
欢迎阅读我的 【CPrimer】专栏 专栏简介:本专栏主要面向C初学者,解释C的一些基本概念和基础语言特性,涉及C标准库的用法,面向对象特性,泛型特性高级用法。通过使用标准库中定义的抽象设施,使你更加适应高级…...
计算图 Compute Graph 和自动求导 Autograd | PyTorch 深度学习实战
前一篇文章,Tensor 基本操作5 device 管理,使用 GPU 设备 | PyTorch 深度学习实战 本系列文章 GitHub Repo: https://github.com/hailiang-wang/pytorch-get-started PyTorch 计算图和 Autograd 微积分之于机器学习Computational Graphs 计算图Autograd…...
Vue 图片引用方式详解:静态资源与动态路径访问
目录 前言1. 引用 public/ 目录2. assets/ 目录3. 远程服务器4. Vue Router 动态访问5. 总结6. 扩展(图片不显示) 前言 🤟 找工作,来万码优才:👉 #小程序://万码优才/r6rqmzDaXpYkJZF 在 Vue 开发中&#x…...
熟练掌握Http协议
目录 基本概念请求数据Get请求方式和Post请求方式 响应数据响应状态码 基本概念 Http协议全称超文本传输协议(HyperText Transfer Protocol),是网络通信中应用层的协议,规定了浏览器和web服务器数据传输的格式和规则 Http应用层协议具有以下特点&#…...
爬虫学习笔记之Robots协议相关整理
定义 Robots协议也称作爬虫协议、机器人协议,全名为网络爬虫排除标准,用来告诉爬虫和搜索引擎哪些页面可以爬取、哪些不可以。它通常是一个叫做robots.txt的文本文件,一般放在网站的根目录下。 robots.txt文件的样例 对有所爬虫均生效&#…...
血压计OCR文字检测数据集VOC+YOLO格式2147张11类别
数据集格式:Pascal VOC格式YOLO格式(不包含分割路径的txt文件,仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数):2147 标注数量(xml文件个数):2147 标注数量(txt文件个数):2147 …...
正则表达式详细介绍
目录 正则表达式详细介绍什么是正则表达式?元字符转义字符字符类限定字符字符分枝字符分组懒惰匹配和贪婪匹配零宽断言 正则表达式详细介绍 什么是正则表达式? 正则表达式是一组由字母和符号组成的特殊文本,它可以用来从文本中找出满足你想…...
初识ArkTS语言
文章目录 ArkTS是HarmonyOS优选的主力应用开发语言。ArkTS围绕应用开发在TypeScript(简称TS)生态基础上做了进一步扩展,保持了TS的基本风格,同时通过规范定义强化开发期静态检查和分析,提升程序执行稳定性和性能。 从…...
Go语言并发之美:构建高性能键值存储系统
摘要 本文介绍了基于Go语言实现的高性能并发键值存储系统。通过深入探讨Go语言在并发编程中的优势,文章详细阐述了系统的锁机制、分片优化、内存管理和持久化设计等关键环节。这些设计展示了如何在系统开发中进行有效的权衡,以确保最优性能。该系统不仅充…...
6. k8s二进制集群之各节点部署
获取kubernetes源码安装主节点(分别执行以下各节点命令)安装工作节点(同步kebelet和kube-proxy到各工作节点)总结 继续上一篇文章《k8s二进制集群之ETCD集群部署》下面介绍一下各节点的部署与配置。 获取kubernetes源码 https:/…...
从0开始使用面对对象C语言搭建一个基于OLED的图形显示框架
目录 前言 环境介绍 代码与动机 架构设计,优缺点 博客系列指引 前言 笔者前段时间花费了一周,整理了一下自从TM1637开始打算的,使用OLED来搭建一个通用的显示库的一个工程。笔者的OLED库已经开源到Github上了,地址在…...
spring基础总结
先修知识:依赖注入,反转控制,生命周期 IDEA快捷键 Ctrl Altm:提取方法,设置trycatch 通用快捷键: Ctrl F:在当前文件中查找文本。Ctrl R:在当前文件中替换文本。Ctrl Z:撤销…...
基础相对薄弱怎么考研
复习总体规划 明确目标 选择专业和院校:根据你的兴趣、职业规划和自身实力,选择适合自己的专业和院校。可以参考往年的分数线、报录比、复试难度等。了解考试科目:不同专业考试科目不同,一般包括: 公共课:…...
代码随想录36 动态规划
leetcode 343.整数拆分 给定一个正整数 n ,将其拆分为 k 个 正整数 的和( k > 2 ),并使这些整数的乘积最大化。 返回 你可以获得的最大乘积 。 示例 1: 输入: n 2 输出: 1 解释: 2 1 1, 1 1 1。 示例 2: 输入: n 1…...
p5r预告信生成器API
p5r预告信生成器API 本人将js生成的p5r预告信使用go语言进行了重写和部署,并开放了其api,可以直接通过get方法获取预告信的png。 快速开始 http://api.viogami.tech/p5cc/:text eg: http://api.viogami.tech/p5cc/persona5 感谢p5r风格字体的制作者和…...
React图标库: 使用React Icons实现定制化图标效果
React图标库: 使用React Icons实现定制化图标效果 图标库介绍 是一个专门为React应用设计的图标库,它包含了丰富的图标集合,覆盖了常用的图标类型,如FontAwesome、Material Design等。React Icons可以让开发者在React应用中轻松地添加、定制各…...
说说Redis的内存淘汰策略?
大家好,我是锋哥。今天分享关于【说说Redis的内存淘汰策略?】面试题。希望对大家有帮助; 说说Redis的内存淘汰策略? 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 Redis 提供了多种内存淘汰策略,用于在内存达到限制时决定如何…...
【C语言】自定义类型讲解
文章目录 一、前言二、结构体2.1 概念2.2 定义2.2.1 通常情况下的定义2.2.2 匿名结构体 2.3 结构体的自引用和嵌套2.4 结构体变量的定义与初始化2.5 结构体的内存对齐2.6 结构体传参2.7 结构体实现位段 三、枚举3.1 概念3.2 定义3.3 枚举的优点3.3.1 提高代码的可读性3.3.2 防止…...
机器学习8-卷积和卷积核1
机器学习8-卷积和卷积核1 卷积与图像去噪卷积的定义与性质定义性质卷积的原理卷积步骤卷积的示例与应用卷积的优缺点优点缺点 总结 高斯卷积核卷积核尺寸的设置依据任务类型考虑数据特性实验与调优 高斯函数标准差的设置依据平滑需求结合卷积核尺寸实际应用场景 总结 图像噪声与…...
3、C#基于.net framework的应用开发实战编程 - 实现(三、三) - 编程手把手系列文章...
三、 实现; 三.三、编写应用程序; 此文主要是实现应用的主要编码工作。 1、 分层; 此例子主要分为UI、Helper、DAL等层。UI负责便签的界面显示;Helper主要是链接UI和数据库操作的中间层;DAL为对数据库的操…...
PHP 中 `foreach` 循环结合引用使用时可能出现的问题
问题背景 假设你有如下 PHP 代码: <?php $arr array(1, 2, 3, 4);// 使用引用遍历并修改数组元素 foreach ($arr as &$value) {$value $value * 2; } // 此时 $arr 变为 array(2, 4, 6, 8)// 再使用非引用方式遍历数组 foreach ($arr as $key > $val…...
go gin配置air
一、依赖下载 安装最新,且在你工作区下进行安装,我的是D:/GO是我的工作区,所有项目都在目录下的src, go install github.com/air-verse/airlatest 如果出现类似报错: 将图中第三行 github.com/air-verse/air 替换最…...
在 Spring Boot 项目中,bootstrap.yml 和 application.yml文件区别
在 Spring Boot 项目中,bootstrap.yml 和 application.yml 是两个常用的配置文件,它们的作用和加载顺序有所不同。以下是它们的详细说明: 1. bootstrap.yml 作用: bootstrap.yml 是 Spring Cloud 项目中的配置文件,用于…...
Excel中Address函数的用法
Excel中Address函数的用法 1. 函数详细讲解1.1 函数解释1.2 使用格式1.3 参数定义1.4 要点 2. 实用演示示例2.1 函数需求2.2 公式编写2.3 计算过程 3. 注意事项4. 文档下载5. 其他文章6. 获取全部Excel练习素材快来试试吧🥰 函数练习素材👈点击即可进行下…...
游戏引擎 Unity - Unity 打开项目、Unity Editor 添加简体中文语言包模块、Unity 项目设置为简体中文
Unity Unity 首次发布于 2005 年,属于 Unity Technologies Unity 使用的开发技术有:C# Unity 的适用平台:PC、主机、移动设备、VR / AR、Web 等 Unity 的适用领域:开发中等画质中小型项目 Unity 适合初学者或需要快速上手的开…...
智能家居监控系统数据收集积压优化
亮点:RocketMQ 消息大量积压问题的解决 假设我们正在开发一个智能家居监控系统。该系统从数百万个智能设备(如温度传感器、安全摄像头、烟雾探测器等)收集数据,并通过 RocketMQ 将这些数据传输到后端进行处理和分析。 在某些情况下…...
Zabbix7.0安装(Ubuntu24.04+LNMP)
1.选择版本 下载Zabbix 2.安装虚拟机 这里选择在Ubuntu24.04上安装Zabbix. 安装链接https://blog.csdn.net/weixin_58189050/article/details/145446065 配置源 vim /etc/apt/sources.list deb https://mirrors.aliyun.com/ubuntu/ noble main restricted universe multive…...
SpringBoot+Dubbo+zookeeper 急速入门案例
项目目录结构: 第一步:创建一个SpringBoot项目,这里选择Maven项目或者Spring Initializer都可以,这里创建了一个Maven项目(SpringBoot-Dubbo),pom.xml文件如下: <?xml versio…...
6种MySQL高可用方案对比分析
大家好,我是 V 哥,关于 MySQL 高可用方案,在面试中频频出现,有同学在字节面试就遇到过,主要考察你在高可用项目中是如何应用的,V 哥整理了6种方案,供你参考。 V 哥推荐:2024 最适合入…...
CLK敏感源和完整GND平面
SPI Flash芯片辐射发射(RE)问题: 某款产品在3米法电波暗室进行辐射(RE)发射测试时,发现多个频点余量不满足6dB管控要求. 通过频谱分析仪近场探头分析定位到干扰频点来自于SPI Flash时钟信号的高次谐波干扰,深入分析发…...
团体程序设计天梯赛-练习集——L1-034 点赞
前言 20分的题目题目不难,理解也不难,做起来有点问题 L1-034 点赞 微博上有个“点赞”功能,你可以为你喜欢的博文点个赞表示支持。每篇博文都有一些刻画其特性的标签,而你点赞的博文的类型,也间接刻画了你的特性。本…...
独立成分分析 (ICA):用于信号分离或降维
人工智能例子汇总:AI常见的算法和例子-CSDN博客 独立成分分析 (Independent Component Analysis, ICA) 是一种用于信号分离和降维的统计方法,常用于盲源分离 (Blind Source Separation, BSS) 问题,例如音频信号分离或脑电信号 (EEG) 处理。…...
【EdgeAI实战】(2)STM32 AI 扩展包的安装与使用
【EdgeAI实战】(1)STM32 边缘 AI 生态系统 【EdgeAI实战】(2)STM32 AI 扩展包的安装与使用 【EdgeAI实战】(2)STM32 AI 扩展包的安装与使用 1. STM32Cube.AI 简介1.1 STM32Cube.AI 简介1.2 X-CUBE-AI 内核引…...
【Java】MyBatis动态SQL
在MyBatis中使用动态SQL语句。 动态SQL是指根据参数数据动态组织SQL的技术。 生活中的案例: 在京东上买东西时,用户搜索商品,可以选择筛选条件,比如品牌,价格,材质等,也可以不使用筛选条件。这时…...
Spring Boot篇
为什么要用Spring Boot Spring Boot 优点非常多,如: 独立运行 Spring Boot 而且内嵌了各种 servlet 容器,Tomcat、Jetty 等,现在不再需要打成 war 包部署到 容器 中,Spring Boot 只要打成一个可执行的 jar 包就能独…...
深入理解Node.js_架构与最佳实践
1. 引言 1.1 什么是Node.js Node.js简介:Node.js是一个基于Chrome V8引擎的JavaScript运行时,用于构建快速、可扩展的网络应用。Node.js的历史背景和发展:Node.js最初由Ryan Dahl在2009年发布,旨在解决I/O密集型应用的性能问题。随着时间的推移,Node.js社区不断壮大,提供…...
使用mockttp库模拟HTTP服务器和客户端进行单元测试
简介 mockttp 是一个用于在 Node.js 中模拟 HTTP 服务器和客户端的库。它可以帮助我们进行单元测试和集成测试,而不需要实际发送 HTTP 请求。 安装 npm install mockttp types/mockttp模拟http服务测试 首先导入并创建一个本地服务器实例 import { getLocal } …...
响应式编程_02基本概念:背压机制 Backpressure
文章目录 Pre流流的处理模型拉模式推模式 流量控制产者生产数据的速率小于消费者的场景生产者生产数据的速率大于消费者消费数据无界队列有界丢弃队列有界阻塞队列 背压机制响应式流规范响应式流的核心接口PublisherSubscriberSubscription 响应式流的技术生态圈 小结 Pre 响应…...
Jason配置环境变量
jason官网 https://jason-lang.github.io/ https://github.com/jason-lang/jason/releases 步骤 安装 Java 21 或更高版本 安装 Visual Studio Code 根据操作系统,请按照以下具体步骤操作 视窗 下载 Jason 的最新版本,选择“jason-bin-3.3.0.zip”…...
[ESP32:Vscode+PlatformIO]新建工程 常用配置与设置
2025-1-29 一、新建工程 选择一个要创建工程文件夹的地方,在空白处鼠标右键选择通过Code打开 打开Vscode,点击platformIO图标,选择PIO Home下的open,最后点击new project 按照下图进行设置 第一个是工程文件夹的名称 第二个是…...