在Web云平台中,web服务器往往会随着应用负载或者故障而动态伸缩,而web服务器上的业务程序(比如C/C++ CGI,PHP,JSP等)往往会调用多个层次的服务,有些服务的访问,是需要基于IP地址预先授权的,否则无法正常调用。为了解决由于web服务器动态伸缩带来的访问未授权问题,一个简单可行的方法是在中间加一个代理层,让代理层服务器事先申请获得授权,这样web服务器就能通过代理服务器顺利调用这些底层服务。
但是对于Web云平台来说,有如下两个难点:
1、云平台本身根本不理解web服务器中的业务程序,也无权修改这些业务程序,所以增加的这一层代理服务层需要对业务程序完全透明,无感知。
2、某些服务接入服务器也是动态的,所以代理服务器也需要根据web服务器中业务程序的路由而访问相应的目标服务器(即如果业务程序中要访问目标服务器A,那么经过代理服务器,代理服务器也需要访问目标服务器A,而不能访问目标服务器B,即使A与B是同质的),需要解决代理的一致性问题。
一个可选方案是,基于DNAT进行构建。在web服务器上,对特定的对外访问(根据一定的match规则),DNAT到代理服务器,然后再由代理服务器根据事先配置好的代理规则(代理服务器侦听多个端口,每个端口对应不同的目标服务器)访问目标服务器。这种方案如下图所示:
但是这种方案有一个致命的缺点,就是代理服务器通过不同的侦听端口来确定不同的目标服务器,不仅在运维配置上很复杂(需要事先获取业务程序中所有要访问到的目标服务器,进行端口映射,实际上这个条件很多时候并不能满足)。另外端口数也是有限的,预先分配的端口数一般也不会太多,这样也造成了代理服务器的利用率可能不会太高(不能做到端口复用)。
由于DNAT会修改发出的IP报文的dest address或dest port,这样就造成了当IP报文到达代理服务器的时候,代理服务器无法获知原始的dest address和dest port,只能根据先验的配置进行转发。因此,需要寻找一种更加简单且透明的代理方案。通过在web服务器上修改IP报文头部的options,把原始dest address和dest port编码到IP Options中,然后当报文到达代理服务器的时候,代理服务器取得IP报文的options,还原原始dest address和dest port,实现对业务程序完全正确的透明代理。如下图所示:
在具体实现中,Mangle处理部分是采用iptables、ip_queue内核模块(采用queue的机制主要是节省很多工作并避免内核里hack)和一个应用态小程序(实际分析并修改报文IP报文)几个模块组成的,主要是修改IP报文,编码私有信息到IP options中;NAT处理部分则是直接用iptables实现DNAT(当然这些模块的粘合和iptables规则设置都是由系统来控制的)。代理服务器中的DIV部分实际上是获取IP报文的options信息(包含原始dest address和dest port)决定连接哪个目标RS的(关于IP报文的options,可以参考RFC 791,实际使用的是RR类型承载私有信息)。
后记:1、由于一般的IP报文都不带options,因为对于IP options的内容,在交换机上的专用转发芯片直接处理不了,需要上升到CPU处理,会有一点点消耗;2、在linux内核2.6.28之后,有一个nf_tproxy模块,但是这个模块做的事情恰好相反,主要用在一些反向代理服务器上,可以使用客户端的IP地址和端口发起到目标服务器的访问,实现对目标服务器来说透明的代理访问,如果要实现用户态的类似LVS则很有用。