前几天接了个烫手山芋,给研发部门分配个云上的负载均衡。我以为分完就完了,结果那边的外包开发缠了我几天,说什么后端服务器拿不到客户端的真实IP,可能不符合安测要求,搞得好像是我整负载均衡时弄出的问题。

来回对线了两天,我说跟后端服务器建立TCP连接的是负载均衡实例,不是客户端,你当然没法从TCP连接上获取客户端IP,只能从HTTP请求头里面转发过来。对面实施经理说不行,自己又不懂计算机网络,反正就是说请求头里客户端真实IP传到后端服务器就没了。周四下午我忍不了了,怎么有这种小可爱,索性直接跑到他们楼去问候。

生气.webp

跟他们面对面沟通,画出整个系统的简化架构,是tm这个样子的。我这几天一直在给他们配负载均衡A,从监听器配到主机组,没完没了的。

我说客户端的IP经过A的转发过后,已经写在HTTP请求头里面的X-Forwarded-For属性上了,发给B了。

开发组表示B只是nginx,没有很强的处理能力,拿到了没用,是C需要客户端真实IP,但是C这边就接收不到客户端IP。

继续跟开发组沟通,问为什么在云上负载均衡后,还要再搞一个nginx,做两层反向代理呢?开发组说得用nginx写些较为负载的转发策略,例如限制域名访问访问等。图中的B和C服务器都不是只有一台,是有多台的,只是为了简化模型,只各用了一台作测试。

架构图.webp

CNM,过了云上负载均衡A这关,后面的东西关我屁事啊,做不了就滚,帮你干也不见给我多发工资

我心善,见不得小可爱受苦。便回来捣鼓捣鼓整个架构,为何后端Web服务器拿不到客户端IP,问题出在何处,以及怎么解决。

此处补充一下我对反向代理和负载均衡的理解。

反向代理是一种模式,一种思想,一种方法,不是一个具体的实在的东西。只要在应用层面,客户端的请求,到服务端之间隔着一个东西,这东西就叫反向代理。而负载均衡是一个实实在在的东西,是反向代理的实现,就是客户端和服务端中隔着的东西,其负责将客户端的请求按照一定算法分配给服务端处理。

晚上,我利用自己的两台电脑加上手机,实现了上图中的架构,并根据开发组发来的nginx.conf配置文件,复现了整个数据传输链路,并确定了问题所在。在整条链路中,每台机器发送和接收的数据包如下图中所示 这哔图画得丑了点便是 图中箭头旁的框表示数据包,分为上下两半,上面一半是应用层http请求头中带的IP,下面一半是传输层tcp的源IP。

架构图加数据包.webp

客户端向负载均衡A发送http请求时,http中的请求头是不带X-Forwarded-For属性(简写XFF)的,这个属性的意思是用于标识通过代理服务器连接到 web 服务器的客户端的原始 IP 地址,这段定义出自Mozilla的百科文档,可以作为拓展阅读去理解,简而言之就是经过代理服务器后的http请求头,才会带上这个属性,并且该属性表示的是客户端真实IP。在该请求中,TCP层面的连接,是客户端跟负载均衡A的连接,这段连接的源IP也就是客户端的真实IP,10.233.233.233。

负载均衡A向其后的负载均衡B转发该请求,事实上也是跟B建立了TCP连接。我在云上对负载均衡A进行了配置,将客户端真实IP,也就是10.233.233.233,写在http头的XFF属性中,向后转发。这一段连接,源IP就是负载均衡A的IP,172.21.0.1。

然后问题就来了,负载均衡B将HTTP请求转发给后端服务器C的时候,重写了请求头中的XFF属性,改为了负载均衡A的IP,172.21.0.1。这段连接在TCP层面的源IP,则是负载均衡B的IP,172.31.0.1。后端服务器无论是从HTTP请求头中,还是从TCP连接中,都拿不到客户端的真实IP了。

这个图一画,数据包一分析,问题其实很清晰了,就TM不关爷的事,纯粹就是开发组那边不会配机器B的nginx,导致客户端IP没有正确转发到后端服务器C,还TM天天缠着我。

我看看这些个小可爱的nginx.conf配置文件怎么写的反向代理。

波提欧-银河粗口.webp

nginx.conf中的语法跟shell脚本非常相似,可以认为是一种弱shell,支持set, echo之类的指令操作变量。

瞅一眼怎么转发给后面机器的。

nginx.conf配置.webp

byd,转发就算了,还不知道从哪里抄来的配置,把XFF属性给重写了,重写为前一层设备(也就是云上负载均衡A)的IP。关于$remote_addr这个变量的定义,可以查阅官方文档中对remote_addr变量的定义

单一负载均衡的情况下才需要这么干,但现在是两层,这么一复写,就把客户端真实IP给完全抹掉了,后端C不可能拿得到真实IP。

解决方案也很简单,把这一行注释掉,不要重写XFF属性。让Nginx把从负载均衡A来的HTTP请求,(更确切地说是请求中的XFF属性),原封不动地给后端C转发,就成了。

修改后nginx.conf配置.webp

后一天,我把这个方案告诉他们的时候,他们说试一下,然后当天再也没有来打扰我了。大抵是解决了吧。

愿各位同学/同事/同僚/同行,努力精进自身开发或软件部署技能,不要一味照抄网上的代码,具体问题具体分析,发问前先理清架构、定位问题,发问前先思考。