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 让配置生效,不影响现有服务运行。