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 集群的方式有关。

当您通过 NodePort 或 LoadBalancer 类型的 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 的 externalTrafficPolicy 为 Local

您需要编辑 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 日
如果觉得我的文章对你有用,请随意赞赏