阿里云容器服务Ingress设置原IP透传

问题

在很多业务场景里,我们都需要知道客户端的真实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
阿里云容器服务Ingress设置原IP透传

原因是参见Kubernetes官网的文档说明:
阿里云容器服务Ingress设置原IP透传

该参数主要是保护客户端IP,避免loadbalancer/nodeport的服务的第二跳。这样nodeport的服务路口只会去找本节点的pod服务。如果本节点没有对应的pod(部署的deployment的实例数少于节点数),则请求将路由不到服务上。为此阿里云的Cloud Provider专门做了优化,只会把有pod的节点注!册到阿里云的SLB上,避免该问题。

这样,Ingress controller被rolling update后就可以提供原IP给容器的应用了