前言
在 Kubernetes 集群中,获取用户真实 IP 地址是一个至关重要的需求。然而,在 K3s 部署中,很多运维人员可能会遇到一些困扰,尤其是在使用 K3s 默认的 CNI Flannel 作为网络插件时,难以准确获取到用户的真实 IP 地址。
本文将分享在解决这一问题的过程中的实践经验,尤其是通过使用 Calico 替代 Flannel 的方式,成功实现了真实 IP 的传递。值得注意的是,虽然 Flannel 也可以实现获取真实 IP 的功能,但我选择使用 Calico 来代替 Flannel,以获得更灵活和可定制的网络方案。
环境准备
在阿里云环境中进行测试,使用机器内网网段为 172.19.112.0/20,推荐使用全新机器以避免潜在问题。
测试环境 (sip.pro.com)
- 一台 master(10.42.0.0),三台 node(10.42.1.0 10.42.2.0 10.42.3.0)
- K3s 版本:1.25.12
- 操作系统:Ubuntu 20.04.6
- Traefik 版本:2.9.10(通过 Helm 安装)
- Rancher 版本:2.7.9
问题发现
在部署中发现 x-forwarded-for
和 x-real-ip
无法获取到用户请求的真实 IP 地址。
探寻原因
通过查阅 K3s 的相关讨论,发现使用内置的 Flannel 无法获取到用户真实 IP,最多只能到 LB 的 IP。解决方案可以禁用掉默认的 flannel,通过手动部署的形式修改 flannel 参数,或者是使用 Calico 替代 Flannel。
测试和解决方案尝试
为验证真实 IP 是否正确获取,统一使用 SIP 进行测试。测试环境映射到 https://sip.pro.com。
kubectl create deployment source-ip-app --image=corelab/echoserver:1.4
1. 默认配置
验证: 访问 https://sip.pro.com
CLIENT VALUES:
client_address=10.42.0.27
...
x-forwarded-for=10.42.3.0
x-real-ip=10.42.3.0
未更改 CNI,使用默认的 K3s 安装的 Flannel,只能获取到 Pod 的网段地址 10.42.3.0。
2. Traefik Service 配置 externalTrafficPolicy: Local
利用 HelmChartConfig 自定义 Traefik Service:
cat << EOL > /var/lib/rancher/k3s/server/manifests/traefik-config.yaml
apiVersion: helm.cattle.io/v1
kind: HelmChartConfig
metadata:
name: traefik
namespace: kube-system
spec:
valuesContent: |-
service:
spec:
externalTrafficPolicy: Local
EOL
验证: 访问 https://sip.pro.com
CLIENT VALUES:
client_address=10.42.0.27
...
x-forwarded-for=10.42.2.203
x-real-ip=10.42.2.203
可以获取到指定 node 的 Klipper-lb (Service LB)的 pod ip
3. 测试环境,安装方式改变
禁用默认的 Flannel,同时禁用网络策略,安装 Calico。
curl -sfL https://rancher-mirror.rancher.cn/k3s/k3s-install.sh | \
INSTALL_K3S_VERSION=v1.25.12+k3s1 \
INSTALL_K3S_MIRROR=cn \
INSTALL_K3S_EXEC='--flannel-backend=none --disable-network-policy --cluster-cidr=192.168.0.0/16' \
sh -
安装 Calico(Manifest 版本):
kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.26.4/manifests/calico.yaml
等待节点安装完成即可。
单节点测试 Calico:
这次按照上文中 K3s 讨论 中说的,换成 calico,真实 ip 已经能到达节点 ip 了,不过现在这个实验场景是使用的单个 k3s 节点。没改 traefik svc 的 Local,可以取到节点 ip,改掉之后,也会取到真实公网 ip
#sip.pro.com
CLIENT VALUES:
client_address=192.168.105.200
command=GET
real path=/
query=nil
request_version=1.1
request_uri=http://sip.pro.com:8080/
SERVER VALUES:
server_version=nginx: 1.10.0 - lua: 10001
HEADERS RECEIVED:
accept=text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
accept-encoding=gzip, deflate
accept-language=zh-CN,zh;q=0.9,en;q=0.8
cache-control=max-age=0
host=777.xydao.cn
upgrade-insecure-requests=1
user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36
x-forwarded-for=172.19.112.37
x-forwarded-host=sip.pro.com
x-forwarded-port=80
x-forwarded-proto=http
x-forwarded-server=traefik-66fd46ccd-n9qzd
x-real-ip=172.19.112.37
BODY:
-no body in request-
多节点测试:
发现已经拿到公网 ip,成功~可以上生产啦!
#sip.pro.com
CLIENT VALUES:
client_address=192.168.105.200
command=GET
real path=/
query=nil
request_version=1.1
request_uri=http://sip.pro.com:8080/
SERVER VALUES:
server_version=nginx: 1.10.0 - lua: 10001
HEADERS RECEIVED:
accept=text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
accept-encoding=gzip, deflate
accept-language=zh-CN,zh;q=0.9,en;q=0.8
host=sip.pro.com
upgrade-insecure-requests=1
user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36
x-forwarded-for=171.214.1.1
x-forwarded-host=sip.pro.com
x-forwarded-port=80
x-forwarded-proto=http
x-forwarded-server=traefik-66fd46ccd-n9qzd
x-real-ip=171.214.1.1
BODY:
-no body in request-
结论
通过替代 Flannel 为 K3s 选择 Calico,我们成功解决了在生产环境中获取真实 IP 的问题。这个优化不仅确保了 IP 的准确性,还提高了 Kubernetes 集群的可用性。
进一步阅读
若想深入了解本文所涉及的技术和问题,建议阅读以下参考链接:
- Kubernetes 官方文档:Source IP for Services with Type=LoadBalancer
- 解决 K3s 下 Traefik 无法获取真实客户端 IP 的问题
- K3s 部署中使用 Calico 的快速入门
- Calico 官方文档
- K3s 使用 Calico 的 Manifest 示例
后记
感谢大家阅读本文,希望能为 K3s 用户在解决真实 IP 获取问题上提供一些有用的经验。欢迎大家分享和讨论,一起探讨 Kubernetes 在实际应用中的优化与实践。