Help us understand the problem. What is going on with this article?

GKE(ingress)配下のNginxでClinet IPアドレスを取得する方法

概要

GKE上にNginxのpodを起動し、起動したNginxでIPアドレスによるアクセス制御するという要件があり、
その時にいろいろハマったのでご紹介。

何にハマったのか?

構成は、

Client -> Ingress -> (NodePort) -> Nginx(pod) -> GAE(アプケーション)

という構成です。

Nginxのログはデフォルトだとremote_addrを使ってクライアントIPを出力するようなのですが、
アクセスログを見ると、remote_addrがClientのIPアドレスではなく、何やらPrivateなIPアドレスが表示されています。
この時点では、なんとなくX-Forwarded-Forの設定を入れればいいかなー?と思っていたのですが、
それだけではなかったというお話です。

調査結果

調査を進めると、LoadBalancer配下のNginxがClientIPアドレスを取得する方法が出てきました。
GCP HTTP(S) load balancing 配下のnginxでクライアントIPを取得する方法

remote_addrx-forwarded-forについては、以下のブログが分かりやすかったです。
remote_addrとかx-forwarded-forとかx-real-ipとか

上記を参考にNginxを設定しても、NginxがクライアントIPアドレスを取得できません。
ずっとNginxの設定ばかりみていたのですが、実はIngressとNginxに間にあるNodePortの設定が原因でした。

NodePortのexternalTrafficPolicyについて

NodePortはWokerノードに到達したパケットをロードバランスする機能があるようです。
その設定は、externalTrafficPolicy で設定可能で、

  • cluster(デフォルト)
  • local

が設定できます。

externalTrafficPolicy Clusterモード

デフォルトの設定です。
こちらの文献によると、

NodePortではノード上のNodePortに到達したパケットは、さらにノードをまたいだPodへもロードバランシングされる形となっています。

とあります。
このパケット転送を行う際、各Wokerノードで起動しているkube-proxyがSNATでSrouce IP AddressDestination IP Addressが書き換えられ、ロードバラシングしてるようです。

今回、 このSrouce IP Addressの書き換えによって、ClientIPアドレスの取得できませんでした。

以下、参考文献をご紹介。

Source IP for Services with Type=NodePort

externalTrafficPolicy localモード

一方、localモードでは、パケットはバラシングされません。
Clusterモードの場合、パケットを転送する分ホップ数が増えるため、結果レイテンシーが増えます。
しかしlocalモードは、当該ノード到達後にノードをまたいだロードバランシングを行わず、当該ノードのPodにパケットを転送しようとするため、Clusterモードと比較して早いようです。
よって、当該ノードに該当するラベルのPodが存在しない場合は、レスポンスを返すことができなくなりエラーが返ります。
localモードを使用する際は、全ノードへPodがデプロイされるDaemonSetが必須 ということになります。

まとめ

externalTrafficPolicyがClusterモードで動いている場合、
Client -> Ingressとリクエストが流れきて、ingressが各Wokerノードにリクエストをロードバランスする。
さらにロードバランスした先のWokerノードで起動しているkube-proxyによって、各Pod間でロードバランスされる。
この際、負荷分散対象となるPodは、NodePortでラベル指定しているPodが対象となり、そのIPアドレスは、NodePortのEndpointsに登録してある。
と理解しました。(間違ってたら指摘してください)

NodePortと一言で言っても裏ではいろんな仕組みが動いているし、そもそもその概念を理解するのが難しい。
なんとなく動いちゃってるから普段は意識しないが、こうやっていろいろ調べてみると勉強になる。

参考文献

この記事は以下の情報を参考にして執筆しました。
KubernetesのDiscovery&LBリソース(その1)
GKE/Kubernetes でなぜ Pod と通信できるのか
ネットワークの概要
Source IP for Services with Type=NodePort

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away