如何在Docker负载均衡后部署的普通TCP服务中获取真实客户端IP
解决方案
方案1:Docker Swarm原生端口发布配置(无需改代码,优先推荐)
Docker Swarm默认使用ingress路由网格做负载均衡,会自动做SNAT所以源IP会被替换成集群内部IP,你直接改用host模式发布端口即可保留真实源IP:
- 创建服务时指定端口发布模式为host:
docker service create \ --name 你的服务名 \ --publish mode=host,target=<TCP服务监听端口>,published=<对外暴露端口> \ 你的镜像名 - 适用场景:单节点单实例部署,或多节点部署时每个节点只跑1个实例、上层无额外负载均衡的场景
- 优点:零代码修改,配置简单性能无损耗
- 缺点:每个节点同一暴露端口只能跑一个服务实例,默认无法用Swarm自带的路由网格做多实例负载;如果需要多实例负载,可在上层额外搭四层负载均衡,将请求转发到各Swarm节点的对应暴露端口即可,源IP同样可以保留。
方案2:PROXY协议透传(适配多实例负载场景,无业务侵入)
PROXY协议是四层TCP专用的源IP传递标准,不属于HTTP请求头范畴,完全符合你的需求:
部署步骤:
- 上层部署四层负载均衡组件,可选HAProxy、Nginx、Traefik,以HAProxy为例,仅需要在后端配置中加
send-proxy参数即可开启PROXY协议透传;如果不想部署外部负载均衡,也可以将Traefik作为Swarm的ingress网关,通过Docker标签配置即可开启PROXY协议透传到后端TCP服务。 - Tornado服务端新增PROXY协议解析逻辑,拿到流之后先解析最开头的PROXY头即可获取真实客户端IP,Python有现成的
proxy-protocol第三方包可以直接调用,无需自己实现解析逻辑。
- 适用场景:多实例部署、需要负载均衡的生产场景
- 优点:性能损耗极低,是行业通用标准方案,不会被中间NAT设备篡改
- 缺点:需要额外配置负载均衡组件,服务端需要加少量解析代码
方案3:自定义TCP报文头传递(完全不依赖基础设施)
因为你允许修改客户端代码,可以直接在业务协议层做IP传递:
- 客户端建立TCP连接后,首个报文先发送加密签名后的自身出口IP信息
- 服务端接收到首个报文先校验签名,解密后拿到真实客户端IP,再走后续业务逻辑
- 适用场景:客户端可控、中间链路复杂无法部署标准负载均衡的场景
- 优点:完全不依赖底层基础设施,不管中间经过多少层NAT都能拿到正确IP
- 缺点:需要修改客户端、服务端的通信逻辑,必须做好签名校验防止IP被伪造
内容的提问来源于stack exchange,提问作者Adam Bright
相关产品推荐
相关产品推荐

