kubernetesのデフォルト挙動では、podが取得するリモートIPがクラスターネットワークのIPになっています。
そのため、httpサーバーのログにも、10.0.0.1などのプライベートIPアクセスが記録されます。IPv6トラフィックの場合も同様です。
多くの場合、クラウドプロバイダが提供するプロキシサービスの機能を活用してIPを抽出することになるでしょう。
HTTPの場合には、X-FORWARDED-FOR
にプロキシがホップごとのIPを記録する規約があります。その他、TCP/IPのレベルでは
Proxy Protocolを使う必要があるでしょう。
NodePortやLoadBalancerの場合
k8sのServiceを素朴に実装するとクライアントのグローバルIPがNATで変換されてしまいますが、クラスタ機能の制約と引き換えに
クライアントIPを取得できるオプションもあります。
(
Loss of client source IP for external traffic)
クラスタ実装に依存する面がありますが、GKEのリージョナルLoadBalancerでは動作します。
Serviceの設定例は以下のとおりです。specセクションにexternalTrafficPolicy: Local
という設定を追加します。
apiVersion: v1
kind: Service
metadata:
labels:
name: web
name: web
spec:
externalTrafficPolicy: Local
ports:
- name: http
port: 80
targetPort: web-http
protocol: TCP
selector:
name: web
type: LoadBalancer
loadBalancerIP: 192.168.0.1
なおexternalTrafficPolicy: Local
はリクエストを受ける全ノードにPodが存在する前提であるため、素朴な想定ではDaemonSet
と組み合わせる必要があるでしょう。
※ k8s v1.6あたりで、 リクエスト消失のネットワーク障害が起きていました。古いクラスタの場合、利用バージョンに注意が必要です。
ネットワークアクセスの継続性に注意が必要
Serviceの再起動は、Pod類と同様、
$ kubectl apply -f service_config.yml
で実行できます。リソース競合でエラーになる場合はdelete -> applyを実行します。
この処理は、Webサービスの玄関にあたる設定を変えるため、注意が必要です。
まず、ロードバランサーの再作成が走るため、しばらくネットワークアクセスが中断します。
また、再起動前にIPを固定する設定を追加しておかないと、再起動にあたり外部IPを新たに取得する挙動になる可能性があります。IPが変わると、DNS設定も変更しないと現行のホスト名でアクセスできなくなります。
外部IPの固定については、
Google Container Engine の外部IPを指定するで解説しています。
PodのhostPortはクライアントIPを保持している
もはやクラスタとは言えない構成を含めるなら、podSpecのports.[].hostPort
を指定するとPodが起動したホストのポートを直接listenします。一般的にはクローズドなネットワークにサービス公開する構成になるでしょう。
ファイアーウォール設定を通じてこのポートにアクセスできたとすると、このトラフィックはk8sのルーティングを介していないため、クライアントのグローバルIPを直接取得できます。
Chuma Takahiro