ingress获取客户端真实IP配置记录

一,背景
需要日志展示真实客户端地址

二,功能实现步骤
1,配置ingress-nginx的configmap

compute-full-forwarded-for: 'true'
forwarded-for-header: X-Forwarded-For
2,修改ingress-nginx的service的配置

externalTrafficPolicy: Local (由Cluster改为Local)
三,相关技术点
1,ingress-nginx实现获取真实IP相关配置
  通常,用户ip的传递依靠的是X-Forwarded-*参数。但是默认情况下,ingress是没有开启的。

  ingress的文档还比较详细,这里介绍一下可能用到的这3个参数:

  ingress官方文档: https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/configmap/#use-forwarded-headers

1.1 use-forwarded-headers

如果Nginx在其他7层代理或负载均衡后面,当期望Nginx将X-Forwarded-的头信息传递给后端服务时,则需要将此参数设为true
如果设为false(默认为false),Nginx会忽略掉X-Forwarded-
的头信息。false设置适用于Nginx直接对外或前面只有3层负载均衡的场景,
  由于ingress的主配置是从configmap中获取的,更新参数则需要修改名为nginx-configuration的configmap的配置:在data配置块下添加use-forwarded-headers: "true"

  修改后,ingress nginx会自动加载更新nginx.conf主配置文件。下图为更新前后配置文件变化对比:

    左边为开启use-forwarded-headers后ingress nginx主配置文件,右边为开启前

1.2 forwarded-for-header

  用来设置识别客户端来源真实ip的字段,默认是X-Forwarded-For。如果想修改为自定义的字段名,则可以在configmap的data配置块下添加:forwarded-for-header: "THE_NAME_YOU_WANT"。通常情况下,我们使用默认的字段名就满足需求,所以不用对这个字段进行额外配置。。

1.3 compute-full-forwarded-for

  如果只是开启了use-forwarded-headers: "true"的话,会发现还是没能获取到客户端来源的真实ip,原因是当前X-Forwarded-For变量是从remote_addr获取的值,每次取到的都是最近一层代理的ip。为了解决这个问题,就要配置compute-full-forwarded-for字段了,即在configmap的data配置块添加:compute-full-forwarded-for: "true"。其作用就是,将客户端用户访问所经过的代理ip按逗号连接的列表形式记录下来。

  待ingress nginx加载configmap并更新主配置文件后,对比更新前后变化如下:

左边是未开启compute-full-forwarded-for配置的ingress nginx主配置文件,右边是开启了配置的

1.4 举例说明

  如果从客户端ip0发起一个HTTP请求到达服务器之前,经过了三个代理proxy1、proxy2、proxy3,对应的ip分别为ip1、ip2、ip3,那么服务端最后得到的X-Forwarded-For值为:ip0,ip1,ip2。列表中并没有ip3,ip3可以在服务端通过remote_addr来获得。这样应用程序通过获取X-Forwarded-For字段的第一个ip,就可以得到客户端用户真实ip了。

1.5 注意项

  值得注意的是,并不是所有的场景都能通过X-Forwarded-For来获取用户正式ip。

  比如,当服务器前端使用了CDN的时候,X-Forwarded-For方式获取到的可能就是CDN的来源ip了,

  这种情况,可以根CDN厂商约定一个字段名来记录用户真实ip,然后代理将这个字段逐层传递,最后到服务端。

2,关于ingress service的修改
2.1 排查思路

     Service 的 externalTrafficPolicy 表示外部流量策略,主要的作用是设置是否保留源 IP。
     如果是 NodePort 类型的 Service 默认不会保留源 IP,可以通过 externalTrafficPolicy 来设置。

2.2 什么是external-traffic-policy
在k8s的Service对象(申明一条访问通道)中,有一个“externalTrafficPolicy”字段可以设置。有2个值可以设置:Cluster或者Local。

  1)Cluster表示:流量可以转发到其他节点上的Pod。

  2)Local表示:流量只发给本机的Pod。

  图示一下:

2.3 这2种模式有什么区别

   存在这2种模式的原因就是,当前节点的Kube-proxy在转发报文的时候,会不会保留原始访问者的IP。

   2.3.1 选择(1)Cluster

   注:这个是默认模式,Kube-proxy不管容器实例在哪,公平转发。

   Kube-proxy转发时会替换掉报文的源IP。即:容器收的报文,源IP地址,已经被替换为上一个转发节点的了。

    原因是Kube-proxy在做转发的时候,会做一次SNAT (source network address translation),所以源IP变成了节点1的IP地址。

    ps:snat确保回去的报文可以原路返回,不然回去的路径不一样,客户会认为非法报文的。(我发给张三的,怎么李四给我回应?丢弃!)

    这种模式好处是负载均衡会比较好,因为无论容器实例怎么分布在多个节点上,它都会转发过去。当然,由于多了一次转发,性能会损失一丢丢。

    2.3.2  选择(2)Local

     这种情况下,只转发给本机的容器,绝不跨节点转发。

     Kube-proxy转发时会保留源IP。即:容器收到的报文,看到源IP地址还是用户的。

       缺点是负载均衡可能不是很好,因为一旦容器实例分布在多个节点上,它只转发给本机,不跨节点转发流量。当然,少了一次转发,性能会相对好一丢丢。

       注:这种模式下的Service类型只能为外部流量,即:LoadBalancer 或者 NodePort 两种,否则会报错。

       同时,由于本机不会跨节点转发报文,所以要想所有节点上的容器有负载均衡,就需要依赖上一级的Loadbalancer来实现。

2.4 两种模式该怎么选
要想性能(时延)好,当然应该选 Local 模式喽,毕竟流量转发少一次SNAT嘛。

     不过注意,选了这个就得考虑好怎么处理好负载均衡问题(ps:通常我们使用Pod间反亲和来达成)。

     如果你是从外部LB接收流量的,那么使用:Local模式 + Pod反亲和,一般是足够的
posted @ 2023-01-12 11:35  戴红领巾的少年  阅读(2954)  评论(0编辑  收藏  举报
感谢您的阅读,如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮。本文欢迎各位转载,但是转载文章之后必须在文章页面中给出作者和原文连接