手当たり次第に書くんだ

飽きっぽいのは本能

Kubernetes で送信元アドレスが変わる問題 – externalTrafficPolicy、hostNetwork、BGP 経路広告で考える

Kubernetes 外部公開の関連記事

Kubernetes では、Service やロードバランサーを経由した通信で、アプリケーションから見える送信元アドレスが変わることがあります。

これは単純な設定ミスとは限りません。Kubernetes は、Pod がどの Node にいても Service 経由で到達できるように抽象化します。その過程で、Node 間転送、SNAT、Service のデータプレーン処理が入り、アプリケーションから見える送信元 IP が実クライアントではなくなることがあります。

ログ、アクセス制御、監査、WAF、ミドルウェアの接続元制御を送信元 IP に依存している場合、この挙動は問題になります。

この記事では、Kubernetes で送信元アドレスが変わる問題を、externalTrafficPolicyhostNetwork、BGP 経路広告、Service のデータプレーンに分けて確認します。

この記事は Kubernetes の外部公開設計を扱います。実際の挙動は kube-proxy、eBPF データプレーン、CNI、MetalLB、クラウドロードバランサー、Kubernetes バージョンによって変わるため、導入時点の公式ドキュメントと実環境で確認してください。

送信元アドレスが問題になる場面

送信元 IP が変わっても、単に Web ページを表示するだけなら問題にならないこともあります。しかし、システム運用では送信元 IP に意味を持たせている場面が少なくありません。

アクセスログ実クライアント IP を記録したい。Node IP やロードバランサー IP だけでは追跡しにくい。
アクセス制御送信元 IP によって許可、拒否、制限を行っている。
監査誰がどこからアクセスしたかを後から説明する必要がある。
WAF / セキュリティ製品送信元 IP をもとにレート制限、ブロック、検知を行う。
ミドルウェアLDAP、DNS、プロキシ、DB などで接続元に応じた制御をしている。

Kubernetes の Service は便利ですが、その便利さは通信経路を抽象化することで成立しています。抽象化によって、アプリケーションから見える通信元が、実際のクライアントではなく Node やロードバランサーに変わることがあります。

なぜ送信元アドレスが変わるのか

送信元アドレスが変わる理由を一つにまとめてしまうと、切り分けが難しくなります。実際には、いくつかの層でアドレスが変わる可能性があります。

外部ロードバランサーロードバランサーがプロキシとして動作し、送信元がロードバランサーの IP に見える場合がある。
NodePort / LoadBalancer Service外部通信を Node で受け、Service 経由で Pod へ転送する過程で SNAT が入る場合がある。
Node 間転送通信を受けた Node と Pod がいる Node が違う場合、Node 間転送が発生する。
kube-proxy / eBPFService のデータプレーン実装によって、転送や NAT の挙動が変わる。
Ingress / API GatewayNginx、Kong、Ingress Controller などが HTTP ヘッダーで実クライアント IP を伝える設計になる場合がある。

つまり、送信元 IP の問題は「Kubernetes の設定」だけではありません。外部ロードバランサー、Service、Node、CNI、Ingress、アプリケーションがまたがる問題です。

どの層で送信元が変わっているのかを分ける

アプリケーションログだけを見るのではなく、ロードバランサー、Node、Service、Pod、Ingress のどこで送信元が変わるのかを確認する必要があります。

externalTrafficPolicy: Local で何が変わるのか

externalTrafficPolicy: Local は、LoadBalancer Service や NodePort Service で外部通信を受けるときに、送信元 IP を保持したい場合の代表的な設定です。

apiVersion: v1
kind: Service
metadata:
  name: example
spec:
  type: LoadBalancer
  externalTrafficPolicy: Local
  selector:
    app: example
  ports:
    - name: http
      port: 80
      targetPort: 80

externalTrafficPolicy: Cluster の場合、Service はクラスタ内のどの Node にいる Pod へも転送できます。柔軟ですが、Node 間転送や SNAT によって送信元 IP が変わることがあります。

externalTrafficPolicy: Local の場合、外部通信を受けた Node 上に存在する Pod だけへ転送します。そのため、Node 間転送を避けやすく、送信元 IP を保持しやすくなります。

Clusterクラスタ全体の Pod へ転送できる。柔軟だが、SNAT により送信元 IP が変わることがある。
Local通信を受けた Node 上の Pod にだけ転送する。送信元 IP を保持しやすいが、Pod 配置とヘルスチェックが重要になる。

ただし、Local は万能ではありません。トラフィックを受けた Node 上に対象 Pod が存在しない場合、その Node では転送できません。つまり、送信元 IP を保持する代わりに、Pod 配置、Node 選択、ロードバランサーのヘルスチェックが重要になります。

hostNetwork は Kubernetes の抽象を一部外す設計である

hostNetwork を使うと、Pod は Node のネットワーク名前空間を使います。

通常の Pod は CNI によって Pod 用のネットワークを持ちますが、hostNetwork の Pod は Node の IP とポートを直接使います。そのため、通信経路は分かりやすくなります。

利点CNI や Service の抽象を一部避けられるため、通信経路が単純になりやすい。
利点外部ロードバランサーから Node IP とポートへ直接振り分ける設計にしやすい。
制約Node 上のポート競合を考える必要がある。
制約Pod の配置自由度が下がり、Kubernetes の Service 抽象から少し外れる。
制約NetworkPolicy や CNI 側の制御が期待どおり効くかを確認する必要がある。

hostNetwork は、Kubernetes らしさを一部捨てる代わりに、ネットワークとしての素直さを取る設計です。性能やトラブルシュートの観点では魅力がありますが、柔軟性は下がります。

そのため、単に送信元 IP を残したいから hostNetwork にする、というより、外部ロードバランサー、Node 配置、ポート管理、セキュリティ境界まで含めて判断する必要があります。

BGP 経路広告は到達性を作る手段である

Calico や MetalLB の BGP を使うと、Kubernetes 内の Pod や Service への経路を外部ネットワークへ広告できます。

これは、Kubernetes を既存ネットワークにより自然に接続するための設計です。ただし、BGP は送信元 IP を保持する魔法ではありません。

BGP が担当するのは、外部ネットワークに対して「この宛先にはこの Node 経由で到達できる」と知らせることです。実際に Pod へどう転送するか、SNAT が入るか、Service のデータプレーンがどう処理するかは、別の層で決まります。

MetalLB BGPLoadBalancer IP への経路を外部ルーターへ広告する。
CNI BGPPod CIDR や Service 関連の経路を外部ネットワークへ広告する構成がある。
外部ルーター受け取った経路をもとに、どの Node へ転送するかを決める。
Service データプレーンNode に入った通信を、どの Pod へ転送するかを処理する。

BGP を使う場合、Kubernetes は閉じた内部ネットワークではなく、外部ルーティングと接続されたネットワークになります。これは強力ですが、Pod CIDR、Service CIDR、LoadBalancer IP、経路集約、経路フィルタ、障害時の withdraw まで含めて設計する必要があります。

IPv4 と IPv6 では設計のしやすさが違う

送信元 IP や経路広告の問題は、IPv4 と IPv6 でも見え方が変わります。

IPv4 ではアドレス空間が狭く、既存ネットワークとの重複や NAT の利用が問題になりやすいです。Pod CIDR や Service CIDR を外部へ出す設計は、アドレス計画と経路集約を慎重に考える必要があります。

一方で IPv6 では、十分なアドレス空間を前提に設計しやすくなります。Pod や Service を外部ルーティングとどう接続するかという意味では、IPv6 の方が Kubernetes と相性が良い面があります。

ただし、IPv6 だから自動的に簡単になるわけではありません。ルーター、Firewall、CNI、Service、アプリケーションが IPv6 を前提に正しく設計されている必要があります。

どの案を選ぶべきか

送信元アドレスをどう扱うかは、何を守りたいかで変わります。

externalTrafficPolicy: LocalKubernetes Service の枠内で送信元 IP を保持したい場合に使う。Pod 配置とヘルスチェックが重要。
hostNetworkネットワークを単純にし、外部ロードバランサーや Node 単位の制御を重視する場合に使う。
BGP 経路広告Kubernetes ネットワークを外部ルーティングへ統合したい場合に使う。
SNAT を許容するアプリケーション側で送信元 IP を重要視しない場合は、抽象化を優先できる。
HTTP ヘッダーで渡すIngress や API Gateway が X-Forwarded-For などで実クライアント IP を伝える設計にする。

どれが常に正解という話ではありません。送信元 IP を保持するほど、Kubernetes の柔軟な抽象化は一部制約されます。一方で、抽象化を優先するほど、ネットワークとしての見通しや監査性が下がることがあります。

送信元 IP を保持する設計は、自由度と引き換えになる

実クライアント IP を残したいなら、Node 選択、Pod 配置、外部ロードバランサー、ヘルスチェック、経路広告を合わせて設計する必要があります。

確認するポイント

送信元アドレスの問題を確認するときは、Kubernetes リソースだけでなく、実際にアプリケーションが見ている送信元 IP まで確認します。

kubectl get svc -A -o wide
kubectl get endpoints -A
kubectl get pods -A -o wide
kubectl get nodes -o wide
ServicetypeexternalTrafficPolicy、外部 IP、NodePort を確認する。
Endpoint対象 Pod がどの Node にいるかを確認する。
PodPod の Node 配置、hostNetwork の有無、アプリケーションログを確認する。
Node外部通信を受けている Node と Pod がいる Node が一致しているかを見る。
外部 LB / ルーターどの Node に転送しているか、ヘルスチェックや BGP 経路がどう見えているかを確認する。

最終的には、テスト用の HTTP サーバーやアプリケーションログで、実際に見えている送信元 IP を確認します。Kubernetes のリソース状態が正しそうに見えても、アプリケーションが見ている送信元が期待どおりとは限りません。

参考
書籍
参考書籍

Kubernetes 完全ガイド 第 2 版

Kubernetes の Service、ネットワーク、CNI、運用観点を体系的に確認したい場合の参考書籍です。価格や在庫はリンク先で確認してください。

Amazon で見る

このリンクは Amazon アソシエイトリンクです。

まとめ

Kubernetes の送信元アドレス問題は、単なる設定ミスではなく、Kubernetes の抽象化とネットワーク設計の衝突です。

Service による柔軟な転送を取るのか、送信元 IP の保持を優先するのか、外部ロードバランサーや BGP と統合するのかを設計として決める必要があります。

externalTrafficPolicy: LocalhostNetwork、BGP 経路広告はいずれも有効な選択肢ですが、得るものと失うものが違います。

ログ、アクセス制御、監査、アドレス設計を含めて、Kubernetes の外側にあるネットワークとどう接続するかを考えるべきです。

参考情報

関連する記事
Kubernetes で送信元アドレスが変わる問題 – externalTrafficPolicy、hostNetwork、BGP 経路広告で考える

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)

トップへ戻る