找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

1337

积分

0

好友

186

主题
发表于 3 天前 | 查看: 6| 回复: 0

在Kubernetes环境中,当我们通过Service访问后端Pod时,如何追踪真实的请求目的地是一个常见需求。之前探讨过使用nginx-ingress-controller来记录后端真实IP的场景。然而,有用户反馈其使用的是原生Nginx而非Ingress Controller,那么在这种情况下,如何有效记录后端Pod的真实IP呢?本文将基于这一具体场景,深入探讨在iptablesipvs两种Service代理模式下,如何利用操作系统底层能力进行流量链路追踪。

环境准备

首先,我们搭建一个简单的测试环境。前端使用一个原生Nginx作为代理,后端是一个简单的应用服务。

Nginx 配置 (nginx.conf片段):

upstream backend_ups {
    server backend-service:10000;
}
server {
    listen       80;
    listen  [::]:80;
    server_name  localhost;
    location /test {
        proxy_pass http://backend_ups;
    }
}

Nginx Deployment & Service:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-test
  namespace: default
spec:
  selector:
    matchLabels:
      app: nginx-test
  template:
    metadata:
      labels:
        app: nginx-test
    spec:
      containers:
      - image: registry.cn-beijing.aliyuncs.com/wilsonchai/nginx:latest
        imagePullPolicy: IfNotPresent
        name: nginx-test
        ports:
        - containerPort: 80
          protocol: TCP
        volumeMounts:
        - mountPath: /etc/nginx/conf.d/default.conf
          name: nginx-config
          subPath: default.conf
        - mountPath: /etc/nginx/nginx.conf
          name: nginx-base-config
          subPath: nginx.conf
      volumes:
      - configMap:
          defaultMode: 420
          name: nginx-config
        name: nginx-config
      - configMap:
          defaultMode: 420
          name: nginx-base-config
        name: nginx-base-config
---
apiVersion: v1
kind: Service
metadata:
  name: nginx-test
  namespace: default
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: nginx-test
  type: NodePort

后端应用 Deployment & Service:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend
  namespace: default
spec:
  replicas: 2
  selector:
    matchLabels:
      app: backend
  template:
    metadata:
      labels:
        app: backend
    spec:
      containers:
      - image: backend-service:v1
        imagePullPolicy: Never
        name: backend
        ports:
        - containerPort: 10000
          protocol: TCP
---
apiVersion: v1
kind: Service
metadata:
  name: backend-service
  namespace: default
spec:
  ports:
  - port: 10000
    protocol: TCP
    targetPort: 10000
  selector:
    app: backend
  type: ClusterIP

部署完成后,查看资源状态:

▶ kubectl get pod -owide
NAME                             READY   STATUS    RESTARTS      AGE     IP            NODE     NOMINATED NODE   READINESS GATES
backend-6d4cdd4c68-mqzgj     1/1     Running   0             6m3s    10.244.0.64   wilson   <none>           <none>
backend-6d4cdd4c68-qjp9m     1/1     Running   0             6m5s    10.244.0.66   wilson   <none>           <none>
nginx-test-b9bcf66d7-2phvh   1/1     Running   0             6m20s   10.244.0.67   wilson   <none>           <none>

▶ kubectl get svc
NAME              TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)                          AGE
backend-service   ClusterIP   10.105.148.194   <none>        10000/TCP                        5m
nginx-test        NodePort    10.110.71.55     <none>        80:30785/TCP                     5m2s

当前架构示意图如下:
watermarked-iptables_ipvs_1

通过NodePort访问Nginx服务:

▶ curl 127.0.0.1:30785/test
i am backend in backend-6d4cdd4c68-qjp9m

请求已成功代理至后端应用。此时查看Nginx访问日志:

10.244.0.1 - - [04/Dec/2025:07:27:17 +0000] “GET /test HTTP/1.1” 200 10.105.148.194:10000 40 “-” “curl/7.81.0” “-”

可以看到,Nginx记录的上游地址是10.105.148.194,即backend-service的ClusterIP,而非后端Pod的真实IP。这是因为Nginx配置中的upstream指向了Kubernetes Service,流量会先经过Service的负载均衡机制。因此,若想获取真实的后端Pod IP,需要在Service实际执行负载均衡的那一层(iptablesipvs)开启日志记录。

iptables模式下的流量追踪

如果Kubernetes集群的Service代理模式为iptables,我们需要从其工作原理入手。理解Linux网络栈中iptables的Netfilter框架是进行此类网络问题排查的基础。

数据包在iptables中的流转简化路径如下:

  1. 进入网卡,经过PREROUTING链。
  2. 进行路由判断:
    • 若目标地址为本机,则进入INPUT链,交由上层应用处理。
    • 若目标地址非本机,则进入FORWARD链,再经POSTROUTING链转发。

watermarked-iptables_ipvs_2

规则探索与实践

我们通过具体规则来追踪backend-service的转发路径。

  1. 查找PREROUTING链:首先看到Kubernetes添加的KUBE-SERVICES链。

    ▶ sudo iptables -L PREROUTING -t nat
    Chain PREROUTING (policy ACCEPT)
    target     prot opt source               destination
    KUBE-SERVICES  all  --  anywhere             anywhere             /* kubernetes service portals */
  2. 定位目标Service规则:在KUBE-SERVICES链中,找到backend-service ClusterIP (10.105.148.194)对应的自定义链KUBE-SVC-ZZAJ2COS27FT6J6V

    ▶ sudo iptables -L KUBE-SERVICES -t nat | grep 10.105.148.194
    KUBE-SVC-ZZAJ2COS27FT6J6V  tcp  --  anywhere             10.105.148.194       /* default/backend-service cluster IP */
  3. 查看Service链规则:该链定义了负载均衡规则,将流量以50%的概率分到两个后端Pod对应的链(KUBE-SEP-*)。

    ▶ sudo iptables -L KUBE-SVC-ZZAJ2COS27FT6J6V -t nat
    Chain KUBE-SVC-ZZAJ2COS27FT6J6V (1 references)
    target     prot opt source               destination
    KUBE-MARK-MASQ  tcp  -- !10.244.0.0/16        10.105.148.194       /* default/backend-service cluster IP */
    KUBE-SEP-GYMSSX4TMUPRH3OB  all  --  anywhere             anywhere             /* default/backend-service -> 10.244.0.64:10000 */ statistic mode random probability 0.50000000000
    KUBE-SEP-EZWMSI6QFXP3WRHV  all  --  anywhere             anywhere             /* default/backend-service -> 10.244.0.66:10000 */
  4. 查看具体Pod端点链规则:以KUBE-SEP-GYMSSX4TMUPRH3OB为例,其核心动作是DNAT,将目标地址转换为Pod IP (10.244.0.64:10000)。

    ▶ sudo iptables -L KUBE-SEP-GYMSSX4TMUPRH3OB -t nat
    Chain KUBE-SEP-GYMSSX4TMUPRH3OB (1 references)
    target     prot opt source               destination
    KUBE-MARK-MASQ  all  --  10.244.0.64          anywhere             /* default/backend-service */
    DNAT       tcp  --  anywhere             anywhere             /* default/backend-service */ tcp to:10.244.0.64:10000

我们的目标是在这里记录日志。最初的尝试是在DNAT规则前插入LOG规则:

sudo iptables -t nat -I KUBE-SEP-GYMSSX4TMUPRH3OB 1 -j LOG --log-prefix "backend-service-pod: " --log-level 4
sudo iptables -t nat -I KUBE-SEP-EZWMSI6QFXP3WRHV 1 -j LOG --log-prefix "backend-service-pod: " --log-level 4

添加后再次发起请求,查看系统日志(/var/log/syslog),却发现记录的DST(目标地址)仍然是Service IP (10.105.148.194)。这是因为LOG规则插入在DNAT规则之前,记录日志时DNAT尚未执行。

问题解决

根据iptables工作流程,DNAT转换发生在PREROUTING链中。因此,我们可以在后续的FORWARD或POSTROUTING链中记录日志,此时DNAT已经完成。这里选择在POSTROUTING链中,针对转换后的Pod IP添加日志规则:

sudo iptables -t nat -I POSTROUTING -p tcp -d 10.244.0.64 -j LOG --log-prefix “backend-service: ”
sudo iptables -t nat -I POSTROUTING -p tcp -d 10.244.0.66 -j LOG --log-prefix “backend-service: ”

再次测试,可以在日志中清晰看到目标地址已变为Pod IP (10.244.0.6410.244.0.66):

Dec  4 17:33:23 wilson kernel: [110211.770728] backend-service: IN= OUT=cni0 PHYSIN=veth1dc60dd3 PHYSOUT=vetha515043b SRC=10.244.0.70 DST=10.244.0.64 LEN=60 TOS=0x00 PREC=0x00 TTL=64 ID=59709 DF PROTO=TCP SPT=33454 DPT=10000 WINDOW=64860 RES=0x00 SYN URGP=0
iptables模式小结

这个方法验证了通过iptables规则可以追踪到Service转发流量的真实后端Pod。然而,它在生产环境中有明显的局限性:

  1. 维护成本高:Pod IP动态变化,规则需随之手动更新。
  2. 扩展性差:新增节点需重复配置。
  3. 风险高:直接操作核心转发规则,误操作可能导致集群网络异常。
    因此,这只适合用于原理验证和临时调试,不适合作为生产环境运维的常规手段。

ipvs模式下的流量追踪

当Kubernetes集群使用ipvs模式时,其核心转发工作由内核的IPVS模块完成。IPVS工作于Netfilter的INPUT链之前,当数据包目的IP匹配Service的VIP(ClusterIP)时,IPVS会介入并进行DNAT等处理。

ipvs模式下,iptables依然会协同工作(主要用于设置MARK等辅助功能)。IPVS处理完DNAT后:

  • 若目标IP为本机,则直接上交。
  • 若目标IP非本机(如Pod IP),数据包会重新进入路由,经FORWARD和POSTROUTING链转发。

watermarked-iptables_ipvs_3

因此,在ipvs模式下,我们同样可以在iptables的POSTROUTING链,针对已知的Pod IP添加LOG规则来追踪流量,其原理和操作与上文在iptables模式POSTROUTING链的实践一致。可以使用ipvsadm命令查看IPVS规则来确认后端Pod地址:

TCP  10.105.148.194:10000 rr
  -> 10.244.0.73:10000            Masq    1      0          0
  -> 10.244.0.74:10000            Masq    1      0          0

总结与展望

本文回顾了iptablesipvs的基本工作原理,并详细探讨了在两种Kubernetes Service代理模式下,通过操作系统底层能力追踪后端Pod真实IP的方法。这些方法本质上是“在负载均衡器(Service)执行转发的瞬间记录日志”。

尽管这些方法在技术原理上可行,但它们都因强依赖底层、维护困难、风险高等问题,难以应用于动态复杂的云原生生产环境。那么,是否存在更优雅、可维护的插件或组件,能够在不用Ingress Controller或记录服务间调用时,帮助我们轻松完成这项工作呢?答案是肯定的,我们将在后续内容中继续探讨。




上一篇:独立开发者转型指南:用AI构建技术资产与副业变现
下一篇:CSS面试题核心知识点解析:布局、动画与性能优化实战指南
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2025-12-24 12:47 , Processed in 0.190031 second(s), 39 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2025 云栈社区.

快速回复 返回顶部 返回列表