Traefik 作为反向代理,在转发请求到后端服务时,需要将客户端真实 IP 地址通过 HTTP 头(如 X-Forwarded-For、X-Real-IP)传递给后端。

探针应用

为了方便排查问题,我们部署一个探针应用,例如kennethreitz/httpbin 或 traefik/whoami,通过 Traefik 访问这个服务,可以返回 HTTP 头信息。

编写 Manifest 编排文件。

# echo-server.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: whoami
  labels:
    app: whoami
spec:
  replicas: 1
  selector:
    matchLabels:
      app: whoami
  template:
    metadata:
      labels:
        app: whoami
    spec:
      containers:
      - name: whoami
        image: traefik/whoami
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: whoami
spec:
  ports:
  - port: 80
    targetPort: 80
  selector:
    app: whoami
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: whoami
spec:
  entryPoints:
    - websecure # 或者 web
  routes:
    - match: Host(`whoami.simaek.com`) # 替换为你的测试域名
      kind: Rule
      services:
        - name: whoami
          port: 80
  tls: {} # 如果是 websecure,需要有证书
kubectl apply -f whoami.yaml

访问 https://whoami.simaek.com,查看 X-Forwarded-For 头的值。

curl https://whoami.simaek.com

Hostname: whoami-7cc8b8c9b-mp24n
IP: 127.0.0.1
IP: ::1
IP: 10.42.0.137
IP: fe80::c85f:94ff:fe42:c10a
RemoteAddr: 10.42.0.105:46164
GET / HTTP/1.1
Host: whoami.simaek.com
User-Agent: curl/7.88.1
Accept: */*
Accept-Encoding: gzip
X-Forwarded-For: 10.42.0.1
X-Forwarded-Host: whoami.simaek.com
X-Forwarded-Port: 443
X-Forwarded-Proto: https
X-Forwarded-Server: traefik-595475fd9d-xmfwz
X-Real-Ip: 10.42.0.1

问题分析

经过上述测试发现,Traefik 请求头包含了反向代理所需要的头。X-Forwarded-For: 10.42.0.1指出地址为 Kubernetes Pod CIDR 地址范围内的地址。表明在客户端请求达到 Traefik 之前,还有一层代理或者网络设备,传递了错误的 IP。

因此问题出在了 Kubernetes Service 或底层网络模型没有正确将客户端 IP 传递给 Pod。具体来说,与 Traefik Service 类型和外部流量进入 k3s 集群的方式有关。

当您通过 NodePortLoadBalancer 类型的 Service 将外部流量导入 k3s 集群时,Kubernetes 默认的行为可能会导致源 IP 地址丢失。这是因为 kube-proxy (或 K3s 内置的 Kube-Router/Flannel/Calico 等 CNI 插件) 在转发流量时会进行 SNAT (Source Network Address Translation),将客户端 IP 替换为 Node IP 或 Pod IP。

为了保留客户端真实 IP,需要配置 Traefik Service 的 externalTrafficPolicy。首先,查看 Traefik 的 Service 定义。通常 K3s 会默认创建一个 LoadBalancer 类型的 Service。

kubectl get svc -n kube-system
  • 如果 externalTrafficPolicy: Cluster (默认值):这就是问题所在。当设置为 Cluster 时,kube-proxy 会在将流量转发到后端 Pod 之前进行 SNAT,导致源 IP 丢失。它会将流量分散到所有健康的后端 Pod,即使它们不在同一个节点上。
  • 如果 externalTrafficPolicy: Local:这是您需要的设置。当设置为 Local 时,kube-proxy 不会进行 SNAT。它只会将流量转发到当前节点上的后端 Pod。如果当前节点没有健康的后端 Pod,流量将直接丢弃。这样可以确保 Traefik Pod 收到的是客户端的真实 IP。

修改 Traefik Service 的 externalTrafficPolicyLocal

您需要编辑 Traefik Service,将其 externalTrafficPolicy 设置为 Local

kubectl edit svc k3s-traefik -n kube-system # 替换为您的 Traefik Service 名称

这里的配置不是永久的,为了避免丢失配置,还需要修改 k3s 配置文件。

打开文件 /var/lib/rancher/k3s/server/manifests/traefik.yaml 添加内容。

apiVersion: helm.cattle.io/v1
kind: HelmChartConfig
metadata:
  name: traefik
  namespace: kube-system
spec:
  valuesContent: |-
    # -- snip --
    service:
      spec:
        ipFamilyPolicy: "PreferDualStack"
        # 追加下面的内容
        externalTrafficPolicy: Local

重启 K3s 让配置生效,不影响现有服务运行。

最后修改:2026 年 02 月 08 日
如果觉得我的文章对你有用,请随意赞赏