Skip to content
Published on

Envoy Gateway ディープダイブ: Gateway API リファレンス実装を読み解く

Authors

はじめに

Kubernetes で外部トラフィックをクラスタの中へ取り込むことは、ほぼすべてのサービス運用の出発点です。長らくその役割は Ingress リソースが担ってきました。ところが現場で Ingress を少し深く使ってみると、すぐに限界にぶつかります。パスベースのルーティングとホストベースのルーティングまでは標準ですが、重み付けトラフィック分割、ヘッダー操作、タイムアウト、リトライ、mTLS、gRPC ルーティングといった実践的な機能は、すべてアノテーションで押し込まなければなりません。

問題は、このアノテーションがコントローラごとにバラバラだという点です。ingress-nginx で使っていたアノテーションは Traefik では動かず、AWS ALB Ingress Controller はまた別の文法を要求します。結局 Ingress マニフェストは特定のコントローラに強く依存し、コントローラを変えた瞬間にルーティングルール全体を書き直す羽目になります。

2026 年現在、この流れは明確に整理されました。Ingress API は事実上凍結された状態です。Kubernetes プロジェクトは Ingress にこれ以上新機能を追加しないと決め、すべての新機能は Gateway API に入っていきます。さらに最も広く使われていた ingress-nginx はメンテナンスモードに近い状態へ移行し、セキュリティ問題への対応負担が大きくなりました。新しく始めるチームであれば、もはや Ingress を選ぶ理由はなくなったのです。

本稿では Gateway API のリファレンス級実装である Envoy Gateway を深く掘り下げます。Envoy Gateway がどのような位置にあるのか、内部アーキテクチャはどうなっているのか、実際の YAML はどう書くのか、ポリシー拡張 CRD で何ができるのか、そして Ingress からどう移行するのかまで、実践的な観点で整理します。

Gateway API と Envoy Gateway の位置づけ

まず用語を整理します。Gateway API は Kubernetes SIG-Network が標準化した次世代のトラフィックルーティング API 仕様です。仕様そのものは実装ではなく CRD の集合であり、複数のベンダーがこの仕様をそれぞれ実装します。Istio、Cilium、Kong、Traefik、NGINX Gateway Fabric などはすべて Gateway API の実装です。

Envoy Gateway はその中でも Envoy プロキシコミュニティが直接手がけた公式実装です。CNCF 配下の Envoy プロジェクトの一部として出発し、データプレーンとして Envoy プロキシを使い、その上に Gateway API をそのままマッピングします。つまり Envoy の強力な機能を、Gateway API という標準インターフェースで公開することが中心的な目標です。

下の表は、従来の Ingress と Gateway API の違いを整理したものです。

項目Ingress APIGateway API
標準化の状態凍結、新機能なし活発に発展、標準の後継
役割分離単一リソースに責務が集中インフラとアプリの役割を明確に分離
ルーティング表現力パス/ホスト中心、残りはアノテーションヘッダー、重み、メソッドが一級表現
プロトコル主に HTTP/HTTPSHTTP, gRPC, TCP, UDP, TLS
移植性コントローラ別アノテーションに依存仕様ベースで移植性を確保
ポリシー拡張標準なしポリシーアタッチメントモデルを提供

Gateway API の最大の設計思想は役割ベースの分離です。クラスタ運用者(インフラ提供者)が GatewayClass を定義し、ネームスペース運用者が Gateway を立ち上げ、アプリケーション開発者は HTTPRoute で自分のサービスのルーティングだけを定義します。各役割が自分の責任範囲の中だけでリソースを触るため、権限分離とマルチテナンシーが自然と付いてきます。

アーキテクチャを覗いてみる

Envoy Gateway は大きくコントロールプレーンとデータプレーンに分かれます。コントロールプレーンは Gateway API リソースを監視し、それを Envoy 設定に変換してデータプレーンへ配信します。データプレーンは実際にトラフィックを処理する Envoy プロキシの Pod 群です。

                  [ ユーザー / クライアント ]
                           |
                           v
        +----------------------------------+
        |   Envoy Proxy (データプレーン)     |
        |   - リスナー / ルート / クラスタ     |
        |   - フィルタチェーン / TLS 終端      |
        +----------------------------------+
                           ^
                           | xDS (gRPC)
                           |
        +----------------------------------+
        |  Envoy Gateway コントローラ        |
        |  (コントロールプレーン)            |
        |  - Gateway API リソースを watch    |
        |  - IR(中間表現)へ変換             |
        |  - Envoy xDS 設定を生成            |
        +----------------------------------+
                           ^
                           | watch
                           |
        +----------------------------------+
        |  Kubernetes API Server            |
        |  GatewayClass / Gateway           |
        |  HTTPRoute / GRPCRoute / TLSRoute  |
        |  ポリシー CRD                      |
        +----------------------------------+

コントローラ内部の処理フローをもう少しほぐすと、次のような段階で進みます。

  1. プロバイダ層が Kubernetes API から Gateway API リソースと関連リソース(Service、Secret、EndpointSlice など)を監視します。
  2. 変換層がこれらのリソースを内部の中間表現(Intermediate Representation, IR)へ変換します。IR は特定のデータプレーンに依存しない抽象モデルです。
  3. IR を再び Envoy の xDS リソース(Listener、Route、Cluster、Endpoint、Secret)へ翻訳します。
  4. xDS サーバーがこの設定を Envoy プロキシの Pod に gRPC ストリームで配信します。

この構造のおかげで、Envoy Gateway は Gateway API の宣言的モデルと Envoy の動的設定モデルの間を途切れなくつなぎます。ユーザーは Envoy の設定文法を直接書く必要はなく、Gateway API リソースだけを書けばよいのです。

インストール

Helm でインストールするのが最も一般的です。以下は基本的なインストール例です。

helm install eg oci://docker.io/envoyproxy/gateway-helm \
  --version v1.3.0 \
  -n envoy-gateway-system \
  --create-namespace

# コントローラが準備できるまで待機
kubectl wait --timeout=5m \
  -n envoy-gateway-system \
  deployment/envoy-gateway \
  --for=condition=Available

インストールが終わると GatewayClass が自動的に作成される場合もあれば、自分で作成する必要がある場合もあります。インストール後、次のコマンドで CRD が正しく入ったか確認します。

kubectl get crd | grep -E 'gateway|envoyproxy'
kubectl get gatewayclass
kubectl get pods -n envoy-gateway-system

ハンズオン: GatewayClass と Gateway

まず GatewayClass を定義します。GatewayClass は、どのコントローラがこのクラスを処理するかを指定するクラスタスコープのリソースです。

apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: eg
spec:
  controllerName: gateway.envoyproxy.io/gatewayclass-controller

次に、実際の入口となる Gateway を作ります。Gateway はリスナーを定義し、リスナーはポート、プロトコル、ホスト名、そしてどのルートを付けられるかを指定します。

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: eg
  namespace: default
spec:
  gatewayClassName: eg
  listeners:
    - name: http
      protocol: HTTP
      port: 80
      allowedRoutes:
        namespaces:
          from: Same
    - name: https
      protocol: HTTPS
      port: 443
      hostname: "*.example.com"
      tls:
        mode: Terminate
        certificateRefs:
          - kind: Secret
            name: example-com-tls
      allowedRoutes:
        namespaces:
          from: All

ここで allowedRoutes の namespaces 設定がマルチテナンシーの核心です。from を Same にすると同じネームスペースのルートだけが付けられ、All にするとすべてのネームスペースが付けられ、Selector にするとラベルセレクタで許可範囲を絞れます。

Gateway が正常にプログラムされたかは、次のように status で確認します。

kubectl get gateway eg -o yaml | yq '.status.conditions'
kubectl get gateway eg \
  -o jsonpath='{.status.addresses[0].value}'

ハンズオン: HTTPRoute

HTTPRoute はアプリケーション開発者が最もよく扱うリソースです。どの Gateway に付けるか、どのホスト名とパスにマッチさせるか、どのバックエンドへ送るかを宣言します。

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: backend
  namespace: default
spec:
  parentRefs:
    - name: eg
  hostnames:
    - "www.example.com"
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /api
      backendRefs:
        - name: backend-svc
          port: 8080

重み付けトラフィック分割(カナリアデプロイ)は Ingress ではアノテーションでしかできませんでしたが、Gateway API では一級の機能です。2 つのバックエンドに重みを与えるだけです。

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: canary
  namespace: default
spec:
  parentRefs:
    - name: eg
  hostnames:
    - "www.example.com"
  rules:
    - backendRefs:
        - name: backend-v1
          port: 8080
          weight: 90
        - name: backend-v2
          port: 8080
          weight: 10

ヘッダーマッチングとリクエストヘッダー操作も標準として表現されます。次の例は特定のヘッダーがあるときだけマッチし、転送前にリクエストヘッダーを追加するフィルタを適用します。

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: header-route
  namespace: default
spec:
  parentRefs:
    - name: eg
  rules:
    - matches:
        - headers:
            - name: x-canary
              value: "true"
      filters:
        - type: RequestHeaderModifier
          requestHeaderModifier:
            add:
              - name: x-routed-by
                value: envoy-gateway
      backendRefs:
        - name: backend-v2
          port: 8080

URL リライトとリダイレクトのフィルタもよく使われます。次はパスの接頭辞を入れ替えるリライトの例です。

filters:
  - type: URLRewrite
    urlRewrite:
      path:
        type: ReplacePrefixMatch
        replacePrefixMatch: /v2

ハンズオン: GRPCRoute

gRPC は Ingress で扱いづらい代表的なプロトコルでした。Gateway API は GRPCRoute という専用リソースを提供し、サービスとメソッド単位でルーティングできるようにします。

apiVersion: gateway.networking.k8s.io/v1
kind: GRPCRoute
metadata:
  name: grpc-route
  namespace: default
spec:
  parentRefs:
    - name: eg
  hostnames:
    - "grpc.example.com"
  rules:
    - matches:
        - method:
            service: com.example.UserService
            method: GetUser
      backendRefs:
        - name: user-grpc-svc
          port: 9000

HTTP/2 ベースの gRPC トラフィックは Envoy が非常にうまく処理する領域です。Envoy Gateway は GRPCRoute を Envoy のルート設定に変換しながら、gRPC 特有のトレーラー処理やステータスコードのマッピングまで自動で面倒を見てくれます。

ハンズオン: TLSRoute

TLSRoute は TLS を終端せず、SNI ベースでパススルーさせるときに使います。バックエンドまで暗号化をそのまま維持したい場合に有用です。

apiVersion: gateway.networking.k8s.io/v1alpha2
kind: TLSRoute
metadata:
  name: tls-passthrough
  namespace: default
spec:
  parentRefs:
    - name: eg
      sectionName: tls-passthrough
  hostnames:
    - "secure.example.com"
  rules:
    - backendRefs:
        - name: secure-backend
          port: 8443

このとき Gateway リスナーは TLS モードを Passthrough にしておく必要があります。

listeners:
  - name: tls-passthrough
    protocol: TLS
    port: 8443
    hostname: "secure.example.com"
    tls:
      mode: Passthrough
    allowedRoutes:
      kinds:
        - kind: TLSRoute

ポリシー拡張: Envoy Gateway の本当の強み

Gateway API の中心的なルーティングリソースだけでは、タイムアウト、リトライ、レートリミット、認証といった運用機能をすべて表現できません。Gateway API はそのためにポリシーアタッチメント(policy attachment)モデルを定義し、Envoy Gateway はこのモデルに従う強力なポリシー CRD 群を提供します。この部分こそ、Envoy Gateway を単なる Ingress の代替以上のものにする点です。

ポリシー CRDアタッチ先主な機能
ClientTrafficPolicyGatewayクライアント側 TLS、HTTP/2、接続バッファ、ヘッダー処理
BackendTrafficPolicyGateway/Routeリトライ、タイムアウト、サーキットブレーカー、レートリミット、ロードバランシング
SecurityPolicyGateway/RouteJWT 認証、OIDC、CORS、基本認証、外部認可
EnvoyProxyGatewayClass/Gatewayプロキシのデプロイ形態、リソース、ロギング、テレメトリ

ClientTrafficPolicy

クライアントとゲートウェイの間の動作を制御します。たとえばクライアント IP を正確に保持したり、TLS パラメータを調整したりできます。

apiVersion: gateway.envoyproxy.io/v1alpha1
kind: ClientTrafficPolicy
metadata:
  name: client-policy
  namespace: default
spec:
  targetRefs:
    - group: gateway.networking.k8s.io
      kind: Gateway
      name: eg
  clientIPDetection:
    xForwardedFor:
      numTrustedHops: 1
  tls:
    minVersion: "1.3"

BackendTrafficPolicy

ゲートウェイとバックエンドの間の回復性の動作を扱います。リトライ、タイムアウト、サーキットブレーカーを一か所で宣言できます。

apiVersion: gateway.envoyproxy.io/v1alpha1
kind: BackendTrafficPolicy
metadata:
  name: backend-policy
  namespace: default
spec:
  targetRefs:
    - group: gateway.networking.k8s.io
      kind: HTTPRoute
      name: backend
  retry:
    numRetries: 3
    perRetry:
      timeout: 2s
    retryOn:
      httpStatusCodes:
        - 503
        - 502
  timeout:
    http:
      requestTimeout: 10s
  circuitBreaker:
    maxConnections: 1024
    maxPendingRequests: 256

レートリミットも同じ CRD で宣言します。次はクライアント IP ごとに 1 分あたり 100 件に制限する例です。正確な上限の表現に数字だけを使うので安全です。

apiVersion: gateway.envoyproxy.io/v1alpha1
kind: BackendTrafficPolicy
metadata:
  name: ratelimit-policy
  namespace: default
spec:
  targetRefs:
    - group: gateway.networking.k8s.io
      kind: HTTPRoute
      name: backend
  rateLimit:
    type: Global
    global:
      rules:
        - clientSelectors:
            - sourceCIDR:
                value: 0.0.0.0/0
                type: Distinct
          limit:
            requests: 100
            unit: Minute

SecurityPolicy

認証と認可をゲートウェイ層で処理します。JWT 検証を例に取ると次のようになります。

apiVersion: gateway.envoyproxy.io/v1alpha1
kind: SecurityPolicy
metadata:
  name: jwt-policy
  namespace: default
spec:
  targetRefs:
    - group: gateway.networking.k8s.io
      kind: HTTPRoute
      name: backend
  jwt:
    providers:
      - name: example-issuer
        remoteJWKS:
          uri: https://issuer.example.com/.well-known/jwks.json
        issuer: https://issuer.example.com

CORS と外部認可(external authorization)も SecurityPolicy で宣言できるため、アプリケーションコードから共通の認証ロジックを取り除くのに大いに役立ちます。

EnvoyProxy

データプレーンそのものを調整します。プロキシ Pod のレプリカ数、リソースのリクエストと制限、サービスタイプ、アクセスログのフォーマット、トレーシングなどをここで設定します。

apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyProxy
metadata:
  name: custom-proxy
  namespace: envoy-gateway-system
spec:
  provider:
    type: Kubernetes
    kubernetes:
      envoyDeployment:
        replicas: 3
        container:
          resources:
            requests:
              cpu: 200m
              memory: 256Mi
            limits:
              memory: 512Mi
  telemetry:
    accessLog:
      settings:
        - format:
            type: JSON

この EnvoyProxy リソースを GatewayClass の parametersRef でつなぐと、そのクラスで立ち上がるすべてのゲートウェイのデータプレーンの形態を一度に制御できます。

Envoy プロキシと xDS の関係

Envoy Gateway をきちんと運用するには、データプレーンである Envoy プロキシがどう動くかを知っておく必要があります。Envoy の設定モデルは大きく 4 つの中心概念から成ります。

  • リスナー(Listener): 特定のポートで接続を受け付ける入口です。Gateway のリスナーがこれに対応します。
  • ルート(Route): 入ってきたリクエストをどのクラスタへ送るかを決める規則です。HTTPRoute がこれに対応します。
  • クラスタ(Cluster): 同一のアップストリームサービスのエンドポイントの束です。バックエンドの Service がこれに対応します。
  • エンドポイント(Endpoint): 実際の Pod の IP とポートです。EndpointSlice から取得します。

これらの設定はすべて xDS というプロトコル群を通じて動的に配信されます。xDS は LDS(リスナー)、RDS(ルート)、CDS(クラスタ)、EDS(エンドポイント)、SDS(シークレット)などのディスカバリサービスで構成されます。Envoy Gateway コントローラが xDS サーバーの役割を果たし、Envoy プロキシがクライアントとして gRPC ストリームで設定を受け取ります。

  Gateway API リソース      Envoy 概念          xDS サービス
  -----------------        ---------          ----------
  Gateway listener    ->   Listener      ->   LDS
  HTTPRoute           ->   Route         ->   RDS
  Service             ->   Cluster       ->   CDS
  EndpointSlice       ->   Endpoint      ->   EDS
  TLS Secret          ->   Secret        ->   SDS

この動的設定モデルのおかげで、ルーティングルールが変わっても Envoy プロキシを再起動せずに設定だけを入れ替えられます。無停止での設定反映は Envoy の最大の強みの一つであり、Ingress コントローラが設定変更のたびにリロードを起こしていた時代と比べると大きな進歩です。

設定が実際に Envoy にどう反映されたかをデバッグしたいときは、次のコマンドで Envoy の設定ダンプを直接見られます。

# Envoy プロキシ Pod の名前を探す
kubectl get pods -n envoy-gateway-system \
  -l gateway.envoyproxy.io/owning-gateway-name=eg

# 管理ポート経由で設定ダンプを取得
kubectl exec -n envoy-gateway-system <proxy-pod> -- \
  curl -s localhost:19000/config_dump | head -c 2000

Ingress から Gateway API への移行

これまで Ingress で運用していたクラスタを Gateway API へ移す作業は、段階的に進めるのが安全です。一度にすべてを変えるよりも、両システムを並行運用しながらトラフィックを徐々に移す戦略を推奨します。

まず、既存の Ingress ルールが Gateway API のどのリソースに対応するかのマッピングを整理します。

Ingress 概念Gateway API の対応
Ingress リソースGateway と HTTPRoute
ingressClassNamegatewayClassName
host ルールHTTPRoute の hostnames
path ルールHTTPRoute の matches
backend serviceHTTPRoute の backendRefs
TLS secretGateway リスナーの certificateRefs
カナリアアノテーションbackendRefs の weight
リライトアノテーションURLRewrite フィルタ

移行の順序は次のように組むと無理がありません。

  1. Envoy Gateway を別のネームスペースにインストールし、新しい Gateway を立ち上げます。このとき既存の Ingress コントローラはそのままにします。
  2. 新しい Gateway に別の LoadBalancer アドレスを割り当てます。既存トラフィックには影響がありません。
  3. サービスごとに HTTPRoute を書き、新しい Gateway へのルーティングを複製します。
  4. DNS または上位のロードバランサで重みを調整し、トラフィックを少しずつ新しい Gateway へ移します。
  5. すべてのトラフィックが安定して移れば、既存の Ingress リソースとコントローラを削除します。

Kubernetes プロジェクトは、この移行を助けるために ingress2gateway という変換ツールも提供しています。既存の Ingress マニフェストを入力すると、それと同等の Gateway API リソースの下書きを作ってくれます。

ingress2gateway print \
  --input-file=legacy-ingress.yaml \
  --providers=ingress-nginx > gateway-resources.yaml

ただし自動変換はあくまで出発点です。コントローラ別のアノテーションの一部はポリシー CRD へ手作業で移す必要があるため、変換結果は必ず人間が確認しなければなりません。

運用とチューニング

運用段階でまず気を配るべきは可観測性です。Envoy Gateway はアクセスログ、メトリクス、分散トレーシングをすべて EnvoyProxy リソースの telemetry セクションで設定します。アクセスログを JSON で構造化すると、ログ収集パイプラインで扱いやすくなります。

メトリクスは Envoy が公開する Prometheus フォーマットをそのまま活用します。次のようにスクレイプ対象を設定します。

apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyProxy
metadata:
  name: observable-proxy
  namespace: envoy-gateway-system
spec:
  telemetry:
    metrics:
      prometheus:
        disable: false
    tracing:
      provider:
        type: OpenTelemetry
        host: otel-collector.monitoring.svc
        port: 4317

可用性の面では、データプレーンのプロキシを最低 2 つ以上のレプリカで立ち上げ、Pod アンチアフィニティでノードに分散させるのが基本です。さらに PodDisruptionBudget を設定しておけば、ノードのドレイン時でも最低 1 つ以上のプロキシが常に生きていることを保証できます。

性能チューニングはトラフィックの特性によって変わりますが、いくつかの一般原則があります。

  • 接続の再利用: バックエンドへの keep-alive と HTTP/2 マルチプレキシングを活用し、接続確立のコストを減らします。
  • 適切なタイムアウト: 長すぎるタイムアウトは障害伝播を招き、短すぎるタイムアウトは正常なリクエストを切ります。バックエンドの実際の応答分布を見て定めます。
  • サーキットブレーカー: バックエンドが過負荷のときに追加リクエストを素早く遮断し、連鎖障害を防ぎます。
  • リソースの適正化: Envoy は効率的ですが、高スループットでは CPU がボトルネックになります。負荷テストで適切なレプリカ数を定めます。

cert-manager と連携すると、TLS 証明書の発行と更新を自動化できます。Gateway リソースに cert-manager のアノテーションを付けると、証明書が自動的にシークレットとして発行されます。これは運用負担を大きく減らす組み合わせです。

よくある落とし穴とトラブルシューティング

実践でよく出会う問題と診断法を整理します。

第一に、ルートが Gateway に付かない場合です。最もよくある原因は allowedRoutes のネームスペース設定の不一致か、parentRefs が誤った Gateway やリスナーを指している場合です。HTTPRoute の status 条件を確認すると、Accepted と ResolvedRefs の状態がはっきり分かります。

kubectl get httproute backend -o yaml | yq '.status.parents'

ここで Accepted が False なら親の Gateway がルートを拒否したということであり、ResolvedRefs が False ならバックエンドの Service や Secret を見つけられなかったということです。

第二に、別のネームスペースの Service や Secret を参照するときです。Gateway API はネームスペース境界を越える参照をデフォルトで遮断し、ReferenceGrant という別リソースで明示的な許可を受けるようにします。クロスネームスペース参照が動かないときは、まず ReferenceGrant が抜けていないか確認します。

apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
  name: allow-route-to-svc
  namespace: backend-ns
spec:
  from:
    - group: gateway.networking.k8s.io
      kind: HTTPRoute
      namespace: route-ns
  to:
    - group: ""
      kind: Service

第三に、ポリシーが適用されない場合です。ポリシー CRD の targetRefs が実在するリソースを正確に指しているか、そして同一の対象に複数のポリシーが衝突していないかを確認します。ポリシー CRD にも status 条件があり、適用の可否を知らせてくれます。

第四に、TLS 証明書の問題です。certificateRefs が指すシークレットが Gateway と同じネームスペースにあるか、シークレットのタイプが kubernetes.io/tls かを確認します。SDS を通じて証明書が配信されないと、リスナーは Programmed 状態に移りません。

最後に、設定が反映されたように見えるのに動作がおかしいときは、先に見た config_dump で Envoy に実際にどんな設定が降りたかを直接確認するのが最も早い道です。コントローラのログも合わせて見ます。

kubectl logs -n envoy-gateway-system \
  deployment/envoy-gateway --tail=100

おわりに

Ingress が凍結され、Gateway API が標準の後継として定着した今、新しいトラフィックの入口層を設計するなら、Gateway API はもはや選択肢ではなくデフォルトに近いものです。その中でも Envoy Gateway は Envoy コミュニティが直接手がけたリファレンス級の実装であり、Envoy の実績あるデータプレーンの上に標準インターフェースをきれいに重ねてくれます。

核心は役割分離とポリシー拡張です。ルーティングは Gateway API の標準リソースで宣言し、回復性やセキュリティといった運用機能は ClientTrafficPolicy、BackendTrafficPolicy、SecurityPolicy といったポリシー CRD できれいに分離します。データプレーンの形態は EnvoyProxy で統制します。この構造を理解すると、アノテーションの山に依存していた過去の Ingress 運用がいかに脆弱だったかを改めて実感します。

新しく始めるプロジェクトなら最初から Gateway API で設計し、既存の Ingress 環境なら ingress2gateway で下書きを作り、段階的に移していってください。標準の上に立つということは、コントローラを変えても自分のルーティングルールがそのまま生き残るということを意味します。

参考資料