问题
在很多业务场景里,我们都需要知道客户端的真实IP来控制业务逻辑。但是从客户到真正的服务,中间总会进过多层的SLB(负载均衡),那么如何才能获取到客户的真实IP呢?
在Kubernetes容器的环境下,通常一个客户端访问到业务要经过云的SLB,Ingress才能达到业务的应用。所以我们需要每一层都能够透传客户端的IP才行。
原理
在基于HTTP的协议里,对于客户端原IP的透传,可以参考: RFC7239(Forwarded HTTP Extension, https://tools.ietf.org/html/rfc7239) 标准。没经过一层链路,都会把对应地址加入到X-Forwarded-For
这个http的扩展头里,以逗号隔开,最早的客户端是第一个地址。
例如:
X-Forwarded-For: 192.0.2.43, 2001:db8:cafe::17
根据RFC7239规范,有个需要注意:如果请求中只有X-Forwarded-For
,而没有其它的X-xxxxx
的扩展头,则规范是建议将其转为
Forwarded: for=192.0.2.43, for="[2001:db8:cafe::17]"
阿里云Kubernetes Ingress实践
大家或许知道,在阿里云的Kubernetes容器服务里,公有云SLB和Ingress是已经自动在安装的时候配置好的,但是如果需要透传客户端的IP,则必须针对ingress controller的service做些改动,具体如下:
kubectl edit service/nginx-ingress-lb -n kube-system
将下图对应的红色框的内容从Cluster
改为Local
原因是参见Kubernetes官网的文档说明:
该参数主要是保护客户端IP,避免loadbalancer/nodeport的服务的第二跳。这样nodeport的服务路口只会去找本节点的pod服务。如果本节点没有对应的pod(部署的deployment的实例数少于节点数),则请求将路由不到服务上。为此阿里云的Cloud Provider专门做了优化,只会把有pod的节点注!册到阿里云的SLB上,避免该问题。
这样,Ingress controller被rolling update后就可以提供原IP给容器的应用了