Skip to content
Published on

IngressとExternalDNS・クラウドLBの統合 — ドメインからL4まで

Authors

はじめに

Kubernetesでアプリケーションを外部に公開する作業は、Ingressリソースを一つ書けば終わりというわけではありません。ユーザーがブラウザにドメインを入力した瞬間からPodにパケットが届くまでには、いくつもの層が鎖のようにつながっています。ドメインのDNSレコードがクラウドロードバランサーのアドレスを指し、そのロードバランサーがクラスタノードへトラフィックを転送し、ノード上のIngressコントローラーがホストとパスを見て正しいServiceへルーティングする必要があります。

この経路のどこか一箇所でも設定を誤ると、症状はよく似た形で現れます。「ドメインに接続できません」という一行の報告の裏には、DNS伝播の問題、LoadBalancerがpendingで止まる問題、送信元IPが消えてアクセス制御が壊れる問題など、まったく異なる原因が隠れていることがあります。

この記事では、その鎖全体をたどっていきます。Ingressコントローラーを外部に公開する方法から始め、AWS・GCP・Azureのクラウドロードバランサー統合、ExternalDNSによるDNSレコードの自動化、オンプレミス環境のMetalLB、送信元IP保存のためのProxy ProtocolとexternalTrafficPolicy、マルチリージョンDNSルーティング、コスト考慮事項、そして現場で出会うトラブルシューティングまで順番に扱います。

2026年の文脈: APIの凍結と後継標準

本題に入る前に、現在のエコシステムの流れを確認しておきます。Ingress APIは安定化以降、事実上凍結(frozen)状態です。新機能はもう追加されず、Kubernetesネットワーキングの後継標準はGateway APIとして定着しました。同時に、最も広く使われてきたingress-nginxプロジェクトはメンテナンスモードに移行し、新機能よりもセキュリティパッチ中心の運用になっています。

とはいえ、この記事の内容が無意味になるわけではありません。ドメインからロードバランサーを経てクラスタへ入るトラフィック経路の原理は、IngressであろうとGateway APIであろうと同じように適用されます。ExternalDNSもすでにGateway APIのHTTPRouteをソースとしてサポートし始めており、クラウドロードバランサー統合のアノテーションの大半はServiceリソースのレベルで動作するため、上位の抽象が何であってもそのまま使えます。したがって、ここで身につける概念はGateway APIへ移行しても有効です。

トラフィック経路全体を一目で

まず、ユーザーのリクエストがPodに届くまでの経路を図で整理します。

[ユーザーのブラウザ]
      |  (1) app.example.com を解決
      v
[DNSリゾルバ] ----> [権威DNS: Route53/Cloud DNS/Azure DNS]
      |                  ^
      |                  | ExternalDNS がレコードを自動生成・更新
      |  (2) A/CNAME 応答: LB アドレス
      v
[クラウドロードバランサー (NLB/ALB/GLB)]
      |  (3) L4 または L7 転送
      v
[クラスタノード (NodePort) または直接 Pod]
      |  (4) kube-proxy / 直接ルーティング
      v
[Ingress コントローラー Pod (nginx/envoy など)]
      |  (5) Host/Path ベースのルーティング
      v
[アプリケーション Service ----> Pod]

この五つの段階それぞれが別個の設定ポイントであり、この記事の各節はこの経路の一区間ずつに対応します。(1)と(2)はDNSとExternalDNS、(3)はクラウドLB統合、(4)から(5)はIngressコントローラーの公開方法と送信元IP保存に当たります。

Ingressコントローラーを外部に公開する方法

Ingressリソース自体は「どうルーティングするか」を宣言するだけで、実際にトラフィックを受ける入口はIngressコントローラーPodです。このコントローラーを外部に公開する方法は三つあり、それぞれトレードオフが異なります。

Service typeの比較

公開方法動作利点欠点適した環境
NodePortすべてのノードの固定ポート(30000-32767)で公開追加インフラ不要、シンプル非標準ポート、ノードIP露出、別途LB必要開発/テスト、外部LBの前段
LoadBalancerクラウドLBを自動プロビジョニング標準ポート、自動化、ヘルスチェッククラウドコスト、サービスごとにLB1つパブリッククラウド本番
hostNetworkPodがノードネットワークを直接使用最小ホップ、最高性能ノードごとのポート競合、スケジューリング制約ベアメタル、高性能要求

NodePort方式

最もシンプルな公開方法です。ServiceをNodePortタイプにすると、すべてのノードの特定ポートが開き、外部ロードバランサーがそのポートへトラフィックを送ります。

apiVersion: v1
kind: Service
metadata:
  name: ingress-nginx-controller
  namespace: ingress-nginx
spec:
  type: NodePort
  selector:
    app.kubernetes.io/name: ingress-nginx
  ports:
    - name: http
      port: 80
      targetPort: 80
      nodePort: 30080
    - name: https
      port: 443
      targetPort: 443
      nodePort: 30443

NodePortは外部に直接公開するよりも、別途管理するロードバランサー(例: 会社標準のLBやオンプレミスのアプライアンス)の背後に置くバックエンドとしてよく使われます。

LoadBalancer方式

パブリッククラウドで最も一般的な方法です。ServiceをLoadBalancerタイプにすると、クラウドプロバイダのコントローラーが実際のロードバランサーを自動でプロビジョニングし、外部IPまたはDNS名を割り当てます。

apiVersion: v1
kind: Service
metadata:
  name: ingress-nginx-controller
  namespace: ingress-nginx
spec:
  type: LoadBalancer
  externalTrafficPolicy: Local
  selector:
    app.kubernetes.io/name: ingress-nginx
  ports:
    - name: http
      port: 80
      targetPort: 80
    - name: https
      port: 443
      targetPort: 443

ここでexternalTrafficPolicyは送信元IP保存に直結する重要なフィールドであり、後の独立した節で詳しく扱います。

hostNetwork方式

性能を極限まで引き上げたい場合や、ベアメタルでLB抽象なしに直接公開したい場合に使います。Podがノードのネットワーク名前空間を直接使うため、追加のネットワークホップがありません。

spec:
  template:
    spec:
      hostNetwork: true
      dnsPolicy: ClusterFirstWithHostNet
      containers:
        - name: controller
          ports:
            - name: http
              containerPort: 80
              hostPort: 80
            - name: https
              containerPort: 443
              hostPort: 443

ただしノードごとに80/443ポートを占有するため、1ノードに1つのコントローラーPodしかスケジューリングできず、ノードIPがそのまま露出するためノード交換時にDNS更新が必要です。

クラウドロードバランサー統合

LoadBalancerタイプのServiceを作るとき、クラウドごとにどの種類のロードバランサーをどのオプションで作るかは、Serviceに付けるアノテーションで制御します。クラウドごとにアノテーションのキーが異なるため、主要な三つのクラウドを整理します。

AWS: NLB vs ALB

AWSには大きく二種類のロードバランサーがあります。NLB(Network Load Balancer)はL4で動作し、ALB(Application Load Balancer)はL7で動作します。Ingressコントローラーの前段には通常NLBを置き、TLS終端とL7ルーティングをコントローラーに任せる構成がよく使われます。

項目NLBALB
動作レイヤーL4 (TCP/UDP)L7 (HTTP/HTTPS)
送信元IP保存デフォルトで保存可能X-Forwarded-Forヘッダで伝達
TLS終端任意(通常コントローラーで)LBで終端
統合リソースService type=LoadBalancerIngress (AWS LB Controller)
コスト特性時間あたり + LCU時間あたり + LCU

NLBをServiceアノテーションでプロビジョニングする例です。AWS Load Balancer Controllerがインストールされている必要があります。

apiVersion: v1
kind: Service
metadata:
  name: ingress-nginx-controller
  namespace: ingress-nginx
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-type: external
    service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: instance
    service.beta.kubernetes.io/aws-load-balancer-scheme: internet-facing
    service.beta.kubernetes.io/aws-load-balancer-cross-zone-load-balancing-enabled: "true"
    service.beta.kubernetes.io/aws-load-balancer-proxy-protocol: "*"
spec:
  type: LoadBalancer
  externalTrafficPolicy: Local
  ports:
    - name: http
      port: 80
      targetPort: 80
    - name: https
      port: 443
      targetPort: 443

ここでaws-load-balancer-proxy-protocolアノテーションは、NLBがProxy Protocol v2を通じて元のクライアント情報を伝達するようにします。これを有効にした場合、Ingressコントローラー側でもProxy Protocolを受信するように設定する必要があり、この部分は後で扱います。

ALBを使う場合は、ServiceではなくIngressリソースにアノテーションを付け、AWS Load Balancer ControllerがそのIngressを見てALBを作ります。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app
  annotations:
    kubernetes.io/ingress.class: alb
    alb.ingress.kubernetes.io/scheme: internet-facing
    alb.ingress.kubernetes.io/target-type: ip
    alb.ingress.kubernetes.io/listen-ports: '[{"HTTP": 80}, {"HTTPS": 443}]'
    alb.ingress.kubernetes.io/ssl-redirect: '443'
spec:
  rules:
    - host: app.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: app-svc
                port:
                  number: 80

GCP

GCPでは、Service type=LoadBalancerがデフォルトで外部パススルーネットワークロードバランサーを作ります。内部LBやバックエンド設定はアノテーションとBackendConfig CRDで制御します。

apiVersion: v1
kind: Service
metadata:
  name: ingress-nginx-controller
  namespace: ingress-nginx
  annotations:
    cloud.google.com/load-balancer-type: "External"
    networking.gke.io/load-balancer-type: "External"
spec:
  type: LoadBalancer
  externalTrafficPolicy: Local
  ports:
    - name: http
      port: 80
      targetPort: 80
    - name: https
      port: 443
      targetPort: 443

内部専用LBが必要なら、cloud.google.com/load-balancer-typeの値をInternalに変えます。GKEのIngressを使うとGCE L7ロードバランサーが生成され、その場合はBackendConfigでヘルスチェック、Cloud CDN、IAPなどを細かく設定できます。

Azure

Azureでは、AKSがStandard Load Balancerをプロビジョニングします。内部LBかどうか、ヘルスプローブのパスなどをアノテーションで指定します。

apiVersion: v1
kind: Service
metadata:
  name: ingress-nginx-controller
  namespace: ingress-nginx
  annotations:
    service.beta.kubernetes.io/azure-load-balancer-internal: "false"
    service.beta.kubernetes.io/azure-load-balancer-health-probe-request-path: /healthz
spec:
  type: LoadBalancer
  externalTrafficPolicy: Local
  ports:
    - name: http
      port: 80
      targetPort: 80
    - name: https
      port: 443
      targetPort: 443

内部LBが必要なら、azure-load-balancer-internalの値をtrueに設定します。ヘルスプローブのパスを明示しないとAzureはデフォルトのプローブを使いますが、externalTrafficPolicyがLocalのときはノードのヘルスチェックポートを正確に指すよう注意が必要です。

ExternalDNSでDNSレコードを自動化

ここまででロードバランサーに外部アドレスが割り当てられます。しかしユーザーはそのアドレスを直接入力せず、ドメインを入力します。したがってドメインのレコードが常に最新のLBアドレスを指すように保つ必要がありますが、これを手動で管理するとLB交換やIP変更のたびにミスが起こります。ExternalDNSはこの作業を自動化します。

ExternalDNSの動作原理

ExternalDNSはクラスタ内のServiceとIngress(およびGateway APIのHTTPRoute)リソースを監視します。ホスト名とLBアドレスの情報を読み、設定されたDNSプロバイダ(Route53、Cloud DNS、Azure DNSなど)に該当レコードを自動で生成・更新・削除します。

[Service/Ingress リソース]
   - host: app.example.com
   - LB アドレス: a1b2.elb.amazonaws.com
        |
        v
   [ExternalDNS コントローラー]
        |  定期的な reconcile
        v
   [DNS プロバイダ API]
   - app.example.com  CNAME  a1b2.elb.amazonaws.com
   - 所有権 TXT レコードを同時に生成

デプロイ例

次はRoute53をプロバイダとして使うExternalDNS Deploymentの例です。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: external-dns
  namespace: external-dns
spec:
  replicas: 1
  selector:
    matchLabels:
      app: external-dns
  template:
    metadata:
      labels:
        app: external-dns
    spec:
      serviceAccountName: external-dns
      containers:
        - name: external-dns
          image: registry.k8s.io/external-dns/external-dns:v0.15.0
          args:
            - --source=service
            - --source=ingress
            - --provider=aws
            - --aws-zone-type=public
            - --registry=txt
            - --txt-owner-id=my-cluster-prod
            - --policy=sync
            - --domain-filter=example.com

主要な引数の説明

  • sources: ExternalDNSが監視するリソースの種類です。serviceとingressを両方指定すると、両方からレコードを作ります。Gateway APIを使うならgateway-httprouteなどを追加できます。
  • provider: DNSプロバイダです。aws(Route53)、google(Cloud DNS)、azureなどを指定します。
  • txt-owner-id: このExternalDNSインスタンスの一意の識別子です。非常に重要な値で、後で説明します。
  • policy: syncは生成・更新・削除をすべて行い、upsert-onlyは削除を行いません。運用初期はupsert-onlyで始めて安全を確認してからsyncへ切り替えるのが安全です。
  • domain-filter: 管理対象ドメインを制限し、誤って別のゾーンを触らないようにします。

txtOwnerIdの重要性

ExternalDNSは自分が作ったレコードを示すために、別途TXTレコードを併せて生成します。このTXTレコードにはtxt-owner-idが入り、ExternalDNSはこの識別子が一致するレコードのみを自分の管理対象として認識します。

複数のクラスタが同じDNSゾーンを共有するとき、すべてのインスタンスのtxt-owner-idが同じだと、あるクラスタが別のクラスタの作ったレコードを自分のものと誤認して削除することがあります。したがってクラスタごと、または環境(prod/staging)ごとに一意のtxt-owner-idを必ず付与する必要があります。この一行を抜かしたために本番ドメインがまるごと消える事故は珍しくありません。

Serviceにアノテーションでホスト名を指定する例です。

apiVersion: v1
kind: Service
metadata:
  name: app-svc
  annotations:
    external-dns.alpha.kubernetes.io/hostname: app.example.com
    external-dns.alpha.kubernetes.io/ttl: "60"
spec:
  type: LoadBalancer
  ports:
    - port: 80
      targetPort: 8080

MetalLB: オンプレミスのLoadBalancer

パブリッククラウドではなくベアメタルやオンプレミスでは、LoadBalancerタイプのServiceを作っても外部IPを自動で割り当ててくれるクラウドコントローラーがありません。その結果、Serviceは永遠にpending状態にとどまります。MetalLBはこの空白を埋め、オンプレミスでもLoadBalancerタイプを使えるようにします。

L2モードとBGPモード

MetalLBは二つのモードで動作します。

モード動作利点欠点
L2 (ARP/NDP)1ノードがVIPを所有しARPで応答ルーター設定不要、シンプル単一ノードのボトルネック、フェイルオーバー遅延
BGPルーターとBGPピアリングしてECMP分散真の負荷分散、速い収束BGP対応ルーターが必要、設定が複雑

IPプールと広告の設定

次はIPアドレスプールを定義し、L2モードで広告する例です。

apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: prod-pool
  namespace: metallb-system
spec:
  addresses:
    - 192.168.10.100-192.168.10.150
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: prod-l2
  namespace: metallb-system
spec:
  ipAddressPools:
    - prod-pool

BGPモードを使う場合は、ルーターとのピアリングを定義します。

apiVersion: metallb.io/v1beta1
kind: BGPPeer
metadata:
  name: top-of-rack
  namespace: metallb-system
spec:
  myASN: 64500
  peerASN: 64501
  peerAddress: 192.168.10.1
---
apiVersion: metallb.io/v1beta1
kind: BGPAdvertisement
metadata:
  name: prod-bgp
  namespace: metallb-system
spec:
  ipAddressPools:
    - prod-pool

L2モードは設定がシンプルですが、特定のVIPのトラフィックが常に1ノードを通過するため、そのノードがボトルネックになります。スループットが重要なら、BGPモードでECMPベースの分散を構成するほうがよいでしょう。

Proxy Protocolと送信元IPの保存

クラウドLBやMetalLBを経ると、バックエンドから見える送信元IPがクライアントの実際のIPではなく中間ノードのIPに変わることがよくあります。これはアクセス制御(IPホワイトリスト)、レート制限、監査ログ、地域ベースの処理などで問題を起こします。送信元IPを保存する方法は大きく二つです。

externalTrafficPolicy: Local vs Cluster

項目Cluster (デフォルト)Local
送信元IPSNATで失われる保存される
負荷分散すべてのノードへ再分配受けたノードのPodのみ
追加ホップノード間ホップが発生しうるなし
ヘルスチェックすべてのノードを通過Podのあるノードのみ通過

externalTrafficPolicyがClusterの場合、トラフィックを受けたノードが別のノードのPodへ再分配することがあり、この過程でSNATが起きて送信元IPがノードIPに変わります。Localに設定すると、トラフィックを受けたノードは自ノードにあるPodへのみ転送するため、SNATがなく送信元IPが保存されます。

その代わりLocalモードでは、当該ノードにバックエンドPodがないとトラフィックが破棄されることがあるため、LBがPodの実際にあるノードへのみトラフィックを送るように、ノードのヘルスチェックが正確に動作する必要があります。

Proxy ProtocolとX-Forwarded-For

L4 LB(例: AWS NLB)ではパケットのペイロードを触らないため、送信元IPをヘッダに入れられません。代わりにProxy Protocolというプロトコルで、接続開始時に元のアドレス情報を伝達します。LBとIngressコントローラーの両方でProxy Protocolを有効にする必要があります。

ingress-nginxでProxy Protocolを受信するように設定する例です。

apiVersion: v1
kind: ConfigMap
metadata:
  name: ingress-nginx-controller
  namespace: ingress-nginx
data:
  use-proxy-protocol: "true"
  real-ip-header: "proxy_protocol"

一方、L7 LB(例: AWS ALB)はHTTPヘッダを扱えるため、X-Forwarded-ForヘッダにクライアントのIPを入れて伝達します。この場合、コントローラーはそのヘッダを信頼するように信頼プロキシ帯域を設定する必要があります。

data:
  use-forwarded-headers: "true"
  proxy-real-ip-cidr: "10.0.0.0/8"

注意点として、LBでProxy Protocolを有効にしたのにコントローラーで無効にすると(またはその逆だと)、コントローラーが最初のバイトをHTTPとして解釈しようとして壊れ、すべてのリクエストが失敗します。両方の設定は必ず一致させる必要があります。

マルチクラスタ・マルチリージョンDNS

一つのドメインを複数のリージョンや複数のクラスタに分散するときは、DNSレベルのルーティングポリシーが必要です。Route53を例にとると、重み付け、レイテンシベース、地理ベースのルーティングをサポートします。

ルーティングポリシーの比較

ポリシー動作用途
重み付け比率どおりにトラフィック分配段階的ロールアウト、ブルー/グリーン
レイテンシベース最も速いリージョンへグローバル性能最適化
地理ベースユーザー位置ごとのリージョンデータ主権、規制遵守
フェイルオーバーヘルスチェック失敗時にバックアップへ災害復旧

ExternalDNSはアノテーションを通じてこれらのポリシーを表現できます。重み付けルーティングの例です。

apiVersion: v1
kind: Service
metadata:
  name: app-region-a
  annotations:
    external-dns.alpha.kubernetes.io/hostname: app.example.com
    external-dns.alpha.kubernetes.io/aws-weight: "80"
    external-dns.alpha.kubernetes.io/set-identifier: region-a
spec:
  type: LoadBalancer
  ports:
    - port: 80

別のリージョンのクラスタでは、同じホスト名に別のset-identifierと重みを付与します。両クラスタのExternalDNSが同じゾーンを管理するため、先に強調したtxt-owner-idをクラスタごとに区別し、set-identifierを一貫して付与することが衝突を防ぐ鍵です。

コスト考慮事項

ロードバランサーとDNSは運用費に直接影響します。設計段階でコストモデルを理解しておけば、後で請求書を見て驚かずに済みます。

  • Serviceごとにロードバランサーを作ると、LBの数だけ時間あたり料金が掛け算されます。Ingressコントローラー一つの背後に複数のサービスをホスト/パスでルーティングすればLBは一つで済むため、コストが大きく下がります。これがServiceごとにLoadBalancerタイプを作らずIngressを使う主な経済的理由です。
  • AWSのLCU(Load balancer Capacity Unit)のように、スループット・接続数・ルール数に応じて課金されるモデルは、トラフィックが増えると時間あたりの固定費より大きな割合を占めることがあります。
  • クロスゾーン負荷分散を有効にすると可用性は上がりますが、可用性ゾーン間のデータ転送料金が発生します。トラフィックが多ければこの料金は無視できません。
  • DNSクエリ自体も百万クエリ単位で課金されます。TTLを短くしすぎるとクエリ数が増え、コストと負荷がともに増加します。ただしフェイルオーバーや頻繁な変更が必要なレコードは短いTTLが必要なので、バランスを取る必要があります。

トラブルシューティング

現場でよく出会う三つの症状と診断フローを整理します。

LoadBalancerがpending状態

ServiceがEXTERNAL-IP欄にpendingだけを表示して止まる場合です。

kubectl get svc -n ingress-nginx
kubectl describe svc ingress-nginx-controller -n ingress-nginx
kubectl get events -n ingress-nginx --sort-by=.lastTimestamp

原因の診断フローです。

LoadBalancer pending
   |
   +-- オンプレミスか? --はい--> クラウドコントローラーなし。MetalLB 等の導入が必要
   |
   +-- クラウドか?
         |
         +-- cloud-controller-manager は動作中? --いいえ--> コンポーネント点検
         +-- LB Controller(AWS 等)は導入済み? --いいえ--> 導入/IAM 権限を確認
         +-- サブネットタグ/クォータの問題? --はい--> サブネットタグ、LB クォータを確認

DNS伝播の遅延

レコードは作られたのに、ドメインがまだ古いアドレスを指している場合です。

dig +short app.example.com
dig app.example.com @8.8.8.8
kubectl logs -n external-dns deploy/external-dns | tail -50

ExternalDNSのログでレコードが実際にupsertされたかをまず確認し、その次に権威ネームサーバへ直接問い合わせます。権威ネームサーバはすでに更新されているのにクライアントが古い値を見ているなら、それはTTL分のキャッシュ残存であり、時間が経てば解消します。権威ネームサーバ自体が古い値なら、ExternalDNSの権限やゾーン設定の問題です。

送信元IPが失われる

バックエンドのログにクライアントIPではなくノードIPやLB IPだけが記録される場合です。

送信元 IP 損失
   |
   +-- externalTrafficPolicy=Cluster か? --はい--> Local への変更を検討
   |
   +-- L4 LB(NLB)で Proxy Protocol を片側だけ有効にしたか?
   |       --はい--> LB とコントローラーの両側を一致させる
   |
   +-- L7 LB(ALB)で X-Forwarded-For を非信頼? --はい--> 信頼プロキシ CIDR を設定

最もよくある罠は、Proxy ProtocolをLBだけ、またはコントローラーだけで有効にした場合です。このときは送信元IP損失を超えて接続自体が壊れるため、上のフローでは最初に両側の一致を確認すべきです。

運用チェックリスト

  • Ingressコントローラーの公開方法(NodePort/LoadBalancer/hostNetwork)を環境に合わせて選んだか。
  • クラウドごとのLBアノテーションが意図したLBの種類(NLB/ALBなど)とスキーム(internet-facing/internal)を正確に指しているか。
  • ExternalDNSのtxt-owner-idがクラスタ・環境ごとに一意か。
  • ExternalDNSのpolicyを初期はupsert-onlyにして検証後にsyncへ切り替えたか。
  • domain-filterで管理対象ゾーンを制限したか。
  • externalTrafficPolicyの設定と送信元IP保存の要件が一致しているか。
  • Proxy Protocolを使うなら、LBとコントローラーの両側の設定が一致しているか。
  • マルチリージョンならset-identifierとルーティングポリシーが一貫しているか。
  • LBの数とクロスゾーン転送コストを検討したか。
  • DNS TTLがフェイルオーバー要件とクエリコストの間でバランスが取れているか。

おわりに

ドメインから始まり、クラウドロードバランサー、Ingressコントローラーを経てPodまで続く経路は長く、各区間ごとに別個の設定ポイントがあります。この記事では、公開方法の選択、クラウドごとのLB統合、ExternalDNS自動化、MetalLB、送信元IP保存、マルチリージョンルーティング、コスト、トラブルシューティングまで、その経路全体をたどってきました。

要点は、各区間を独立して理解しつつ、区間どうしの約束(例: Proxy Protocolの両側一致、txt-owner-idの一意性、externalTrafficPolicyとヘルスチェックの整合)を見落とさないことです。また2026年現在、Ingress APIは凍結されGateway APIが後継標準ですが、ここで扱ったトラフィック経路の原理とExternalDNS・LB統合はGateway APIへ移行しても通用します。今この経路をしっかり理解しておけば、抽象が変わっても揺らがないでしょう。

参考資料