【问题标题】:How to read client IP addresses from HTTP requests behind Kubernetes services?如何从 Kubernetes 服务后面的 HTTP 请求中读取客户端 IP 地址?
【发布时间】:2015-11-13 18:45:10
【问题描述】:

我的 Web 应用程序在 SSL 的 nginx 反向代理后面作为 Kubernetes pod 运行。代理和我的应用程序都使用 Kubernetes 服务进行负载平衡(如 here 所述)。

问题是我所有的 HTTP 请求日志只显示内部集群 IP 地址,而不是实际 HTTP 客户端的地址。有没有办法让 Kubernetes 服务将此信息传递给我的应用服务器?

【问题讨论】:

    标签: http kubernetes


    【解决方案1】:

    从 1.5 开始,如果您在 GCE(通过扩展 GKE)或 AWS 中运行,您只需向您的服务添加一个注释即可使 HTTP 源保存工作。

    ...
    kind: Service
    metadata:
      annotations:
        service.beta.kubernetes.io/external-traffic: OnlyLocal
    ...
    

    它基本上通过节点端口直接公开服务,而不是提供代理——通过在每个节点上公开健康探测,负载均衡器可以确定将流量路由到哪些节点。

    在 1.7 中,此配置已成为 GA,因此您可以在服务规范中设置 "externalTrafficPolicy": "Local"

    Click here to learn more

    【讨论】:

    • 如果服务类型是ClusterIP怎么办?
    • externalTrafficPolicy:本地,仅适用于负载均衡器和 NodePort 类型的 Kubernetes 服务。
    • 另外,AWS 不支持这个功能,我发布了一个关于它的答案。
    【解决方案2】:

    externalTrafficPolicy:本地

    是您可以在类型为负载均衡器或类型为 NodePort 的 Kubernetes Services 的 yaml 中指定的设置。 (入口控制器通常包含 yaml 来提供 LB 服务。)

    externalTrafficPolicy:本地

    做三件事:
    1. 禁用 SNAT,以便 而不是入口控制器 pod 将源 IP 视为 Kubernetes 节点的 IP,而是应该看到真正的源 IP
    2。通过添加 2 条规则来消除额外的网络跃点
    - 如果流量落在没有入口 pod 的节点的 nodeport 上,它就会被丢弃。
    -如果流量到达带有入口 pod 的节点的节点端口,它会被转发到同一节点上的 pod。
    3。使用 /healthz 端点 更新 Cloud Load Balancer 的 HealthCheck,这样 LB 就不会转发到本应被丢弃的节点,而只会转发到具有入口 pod 的节点。
    (为了澄清起见,重新措辞:默认情况下,又名“externalTrafficPolicy:Cluster”,流量在每个工作节点的 NodePort 之间进行负载平衡。“externalTrafficPolicy:Local”允许流量仅发送到具有 Ingress Controller Pod 的节点子集因此,如果您有一个 100 个节点的集群,而不是云负载均衡器将流量发送到 97 个节点,它只会将其发送到运行 Ingress Controller Pod 的约 3-5 个节点)


    重要提示!
    AWS 不支持“externalTrafficPolicy: Local”。
    (据说它在 GCP 和 Azure 上运行良好,话虽如此,我还记得读过在 Kubernetes 1.14 的次要版本中存在回归破坏它 + 有一些 Cilium CNI 版本也破坏了,所以请注意默认的 externalTrafficPolicy: Cluster 是坚如磐石的稳定,如果您不需要该功能,通常应该是首选。还请注意,如果您前面有 WAF 作为服务,那么您可以利用以查看客户端流量的来源。)

    (它会导致 kops 和 EKS 出现问题,在 AWS 上运行的其他发行版实际上可能不受影响,下面会详细介绍。)

    AWS 不支持“externalTrafficPolicy:Local”是 Kubernetes 维护人员已知的一个问题,但没有详细记录。此外,令人讨厌的事情是,如果您尝试它,您将获得一些运气/它似乎正在工作,这会诱使足够多的人认为它有效。

    externalTrafficPolicy:本地在 AWS 上以 2 种方式中断,并且两种中断都有解决方法来强制它工作:
    第一次中断 + 解决方法: /healthz 端点初始创建不稳定 + 协调循环逻辑已损坏。
    初次应用时,它将适用于某些节点而不适用于其他节点,然后永远不会更新。
    https://github.com/kubernetes/kubernetes/issues/80579
    ^更详细地描述了这个问题。
    https://github.com/kubernetes/kubernetes/issues/61486
    ^描述了一种解决方法,以使用 kops 钩子强制它工作
    (当您解决 /healthz 端点协调循环逻辑时,您将解锁好处 #2 和 #3 更少的跃点 + LB 仅将流量发送到工作节点的子集。但是,好处 #1 源 IP 仍然不正确。)

    第二次休息 + 2 个解决方法:
    理想的最终结果是入口 pod 可以看到真实客户端的 IP。
    但真正发生的是 ingress pod 从查看 k8s 节点的源 IP 转变为查看 Classic ELB 的源 IP。

    解决方法选项 1.)

    切换到更像 Azure LB 的网络 LB (L4 LB)。这样做的代价是无法使用 ACM(AWS 证书管理器)在 AWS LB 终止 TLS/为您处理 TLS 证书预置和轮换。

    解决方法选项 2。)
    继续使用 AWS 经典 ELB,(并且您可以继续使用 ACM),您只需将配置添加到经典 ELB(以 LB 服务的注释形式)+ 将配置添加到入口控制器.所以两者都使用代理协议或 x 转发标头,我记得另一个 Stack Overflow 帖子涵盖了这个,所以我不会在这里重复。

    【讨论】:

      【解决方案3】:

      您可以通过两种方式完全摆脱 kube-proxy:

      1. 使用 Ingress 将您的 nginx 配置为基于源 ip 进行平衡并将流量直接发送到您的端点 (https://github.com/kubernetes/contrib/tree/master/ingress/controllers#ingress-controllers)

      2. 部署 haproxy serviceloadbalancer(https://github.com/kubernetes/contrib/blob/master/service-loadbalancer/service_loadbalancer.go#L51) 并在服务上设置平衡注释,使其使用“源”。

      【讨论】:

      • 听起来像是用 Kubernetes 方式解决这个问题的最佳选择 :)
      • @Prashanth B 我们的应用程序中有一个类似的用例,但我们的 nginx conf 文件使用 nginx 入口控制器非常复杂(包括复杂的身份验证机制和所有)。自发布以来有任何发展吗?一种将原始客户端 IP 获取到应用服务的更简单方法(用于会话亲和性)。
      • 这些选项都不会让 kube-proxy 退出循环吗? kube-proxy 仍然负责从 NodePort --> Inner Cluster Network 转移流量。 LoadBalancer 服务仍然转发到 NodePorts。并且 Ingress Controller 存在于集群中。
      【解决方案4】:

      现在,没有。

      服务使用 kube_proxy 将流量分配到其后端。 Kube-proxy 使用 iptables 将服务 IP 路由到它正在侦听的本地端口,然后打开与其中一个后端的新连接。您看到的内部 IP 是在您的一个节点上运行的 kube-proxy 的 IP:port。

      仅 iptables 的 kube-proxy 是 in the works。这将保留原始源 IP。

      【讨论】:

      • 有谁知道“iptables only kube-proxy”现在是否可用?
      【解决方案5】:

      从 Kubernetes 1.1 开始,有一个基于 iptables 的 kube-proxy 可以在某些情况下解决此问题。默认禁用;有关如何启用它的说明,请参阅this post。总之,做:

      for node in $(kubectl get nodes -o name); do kubectl annotate $node net.beta.kubernetes.io/proxy-mode=iptables; done
      

      在 Pod 到 Pod 流量的情况下,使用 iptables kube-proxy,您现在将在目标 pod 上看到真正的源 IP。

      但是,如果您的服务正在转发来自集群外部的流量(例如 NodePort、LoadBalancer 服务),那么我们仍然需要替换 (SNAT) 源 IP。这是因为我们正在对传入流量进行 DNAT 以将其路由到服务 Pod(可能在另一个节点上),因此 DNATing 节点需要将自己插入返回路径中才能取消对响应的 DNAT。

      【讨论】:

        【解决方案6】:

        对于非 HTTP 请求(HTTPS、gRPC 等),这计划在 Kubernetes 1.4 中得到支持。见:https://github.com/kubernetes/features/issues/27

        【讨论】:

          【解决方案7】:

          对于 kubernetes 1.7+,将 service.spec.externalTrafficPolicy 设置为 Local 将解决它。 更多信息在这里:Kubernetes Docs

          【讨论】:

            猜你喜欢
            • 2021-04-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2018-03-18
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多