问题背景
开发的项目中有IP定位的需求,当我接入腾讯地图相关IP定位及逆地址解析API并发布到外网后,却发现获取的IP一直是固定的内网IP(10.255.0.2)导致无法定位
问题排查
我的项目采用nginx作为反向代理,在代理中做以下配置转发请求原始IP
proxy_set_header X-Real-IP $remote_addr
然后在nginx打印的日志中$remote_addr已经是此内网IP,导致后续设置的请求头X-Real-IP就是此IP。我的应用是发布在Docker的swarm集群中,因此判定问题出在swarm集群。经过网上搜索资料后发现:
原始IP请求进入Ingress网络(swarm load balancer)后,原始IP被SNAT修改为内部Ingress网络的IP段,如10.255.0.x,之后才会被转发到service container。这会导致在service container中获取的远端IP始终是Ingress内部的虚拟IP段。此时会导致白名单、外网IP访问日志记录等依托正确外网IP解析的功能全部失效。
解决方案
方案一
将反向代理nginx以独立容器形式部署,不再部署在swarm集群中。此时nginx获取到的$remote_addr就是真实的原始IP,转发到swarm集群后的请求的请求头X-Real-IP已经在反向代理中设置过了,所以后续服务中就可以获取到真实的原始IP。PS:作者是采用的此方案
方案二
采用host模式+打标签,这种解决方案其实本质上和方案一是一样的,只是实现方式不同。在发布反向代理服务时,仍发布到swarm集群中,但是开放端口时指定mode=host。例如:
docker service create --name nginx --publish "mode=host,target=80,published=80" --mode global --network mynet image/nginx:1.0.0
方案三
docker ingress routing daemon的后台脚本增强方案,是一个轻量级的解决方案,需要在swarm各个节点实现运行一个后台脚本。该脚本需要指定一个ingress-gateway-ips的参数,可通过裸运行该脚本查看每一个swarm节点的ingress内部ip,通常为10.255.0.x。
Usage: /home/swarm/docker-ingress-routing-daemon.sh [--install [OPTIONS] | --uninstall | --help]
--services <services> - service names to disable masquerading for
--tcp-ports <ports> - TCP ports to disable masquerading for
--udp-ports <ports> - UDP ports to disable masquerading for
--ingress-gateway-ips <ips> - specify load-balance ingress IPs
--no-performance - disable performance optimisations
(services, ports and IPs may be comma or space-separated or may be specified
multiple times)
!!! Ingress subnet: 10.255.0.0/16
!!! This node's ingress network IP: 10.255.0.2
特别注意:
- 该脚本需要在部署应用前提前运行,如果在已有应用集群上运行无法生效。
- 如果在应用集群运行后,该后台进程被杀掉,仍然不影响其作用(路由表依旧生效),但是后续新部署/更新的应用会失效。
- 运维应该通过机制确保该脚本对应的后台守护进程始终存在,如果被误杀需要在下次更新部署前拉起。
此脚本原始项目链接
该后台脚本如下,参考命令:
nohup /root/docker-ingress-routing-daemon.sh --ingress-gateway-ips 10.255.0.2,10.255.0.3,10.255.0.4 --install &