Skip to content
Published on

Emissary-ingress(Ambassador)完全ガイド: APIゲートウェイ型Ingressのすべて

Authors

はじめに

Kubernetes でサービスを外部に公開するとき、まず思い浮かぶのが Ingress です。ところが実際に運用してみると、単純なパスベースのルーティング以上のものが必要になる瞬間が訪れます。認証を付け、レートリミットをかけ、カナリアリリースでトラフィックを 5 パーセントだけ新バージョンへ流し、ヘッダーベースでルーティングを分岐し、gRPC と WebSocket を同時に処理しなければならない状況がそれです。

こうした要件が積み重なると、結局「Ingress ではなく API ゲートウェイが必要なのではないか」という問いにたどり着きます。Emissary-ingress(以前の名称は Ambassador)は、まさにその地点から出発したプロジェクトです。CNCF のインキュベーティングプロジェクトであり、Envoy Proxy をデータプレーンとして採用し、Kubernetes ネイティブな方法で API ゲートウェイ機能を提供します。

2026 年現在、Ingress のエコシステムは大きな転換点を通過しています。ネットワーキング Ingress API は事実上凍結(frozen)状態であり、もはや新しい機能は追加されません。その後継標準として Gateway API が定着し、最も広く使われていた ingress-nginx は、セキュリティ問題とメンテナー不足によりメンテナンスモードに近い流れを見せています。こうした状況の中で、Envoy ベースのモダンなコントローラーを理解しておくことは、ますます重要になっています。

本記事では、Emissary-ingress のアーキテクチャを分解し、中核リソースである Mapping CRD を深く扱い、認証とレートリミット、トラフィック管理、カナリアリリース、Gateway API サポート、実践的なデプロイと運用チューニング、そしてよく陥る落とし穴まで、一度に整理します。

Emissary-ingress とは何か

Emissary-ingress は、Kubernetes のためのオープンソース API ゲートウェイであり Ingress コントローラーです。まず主な特徴を挙げると以下のとおりです。

  • Envoy Proxy をデータプレーンとして使用します。つまり実際のトラフィック処理は実績のある Envoy が担当します。
  • Kubernetes CRD(カスタムリソース)ですべての設定を宣言します。Mapping、Host、Listener、AuthService などが代表例です。
  • アノテーションベースの設定ではなく独立したリソースでルーティングを定義するため、マイクロサービスのチームがそれぞれ自分の Mapping を所有する分散設定(decentralized configuration)が自然に成り立ちます。
  • gRPC、WebSocket、HTTP/2、HTTP/3、TLS 終端、認証、レートリミットといった API ゲートウェイ機能を標準で提供します。

歴史的に、このプロジェクトは Datawire(現 Ambassador Labs)が作った Ambassador から出発しました。その後、オープンソースのコアが CNCF に寄贈されたことで Emissary-ingress に名称が変わり、商用版は Ambassador Edge Stack(AES)という名前で、開発者ポータル、高度なレートリミット、OAuth/OIDC フィルター、Web アプリケーションファイアウォールなどを追加で提供します。

ひと目で分かるポジショニング

区分単純な Ingress コントローラーEmissary-ingress
主な用途L7 パスルーティング、TLS 終端API ゲートウェイ、パスルーティング、トラフィック管理
設定方式Ingress リソース + アノテーション専用 CRD(Mapping など)
データプレーンコントローラーごとに異なるEnvoy Proxy
認証別途構成が必要AuthService で内蔵
レートリミット限定的RateLimitService で内蔵
カナリア困難weight ベースで内蔵
Gateway APIコントローラーにより異なるサポート

アーキテクチャ: コントロールプレーンとデータプレーンの分離

Emissary-ingress を正しく理解するには、まずコントロールプレーンとデータプレーンの分離を知る必要があります。多くの単純な Ingress コントローラーは設定ファイルを直接レンダリングしてプロキシを再起動する方式を使いますが、Emissary は Envoy の xDS(動的ディスカバリ)プロトコルを活用します。

                    +-----------------------------+
                    |   Kubernetes API Server      |
                    |  (Mapping, Host, Listener...) |
                    +--------------+--------------+
                                   | watch (CRD)
                                   v
              +----------------------------------------+
              |        Emissary Pod                     |
              |  +----------------+   +--------------+   |
              |  |  controller    |   |   Envoy      |   |
              |  | (CRD -> snapshot|-->| (data plane) |  |
              |  |  -> Envoy conf) |xDS|              |  |
              |  +----------------+   +------+-------+   |
              +--------------------------------|--------+
                                               | L7 traffic
                  client ---- :8080/:8443 ------+----> upstream services

流れを段階で解くと次のようになります。

  1. ユーザーが Mapping や Host といった CRD を kubectl apply で作成します。
  2. Emissary コントローラーが Kubernetes API サーバーを watch しながら変更を検知します。
  3. コントローラーはすべてのリソースを集めて 1 つの一貫したスナップショットに構成し、それを Envoy 設定に変換します。
  4. 変換された設定は xDS(または V3 ADS)プロトコルで Envoy データプレーンに渡されます。
  5. Envoy は無停止で新しい設定を適用し、実際のクライアントトラフィックを処理します。

この構造のおかげで、設定変更時にプロキシプロセスを丸ごと再起動せずにルーティングルールを更新できます。データプレーン(Envoy)はトラフィック処理だけに集中し、コントロールプレーンは宣言された意図を Envoy が理解できる形に翻訳する役割に集中します。

中核リソースの階層

Emissary 3.x を基準にすると、トラフィックが入ってくる経路は次のリソースが組み合わさって構成します。

  • Listener: Envoy がどのポートとプロトコルでリクエストを受けるかを定義します(例: 8080 HTTP、8443 HTTPS)。
  • Host: ドメイン(hostname)と TLS 設定、証明書を束ねます。ACME の自動発行もここで扱います。
  • Mapping: 実際のルーティングルールです。パスの prefix をどのサービスへ送るかを定義します。
  • AuthService / RateLimitService: 外部フィルターの動作を定義します。

この分離のおかげで、「どのポートで listen するか(Listener)」「どのドメインを処理するか(Host)」「どのパスをどこへ送るか(Mapping)」がきれいに分かれます。

Mapping CRD を深く覗く

Mapping は Emissary の心臓です。最も基本的な形から見てみましょう。

apiVersion: getambassador.io/v3alpha1
kind: Mapping
metadata:
  name: quote-backend
  namespace: default
spec:
  hostname: "*"
  prefix: /backend/
  service: quote

この Mapping は /backend/ で始まるすべてのリクエストを quote サービスへ送ります。hostname: "*" はすべてのドメインに適用するという意味です。サービス名は同じ namespace の Kubernetes Service を指し、quote.namespace 形式で他の namespace も指定できます。

prefix と正規表現ルーティング

apiVersion: getambassador.io/v3alpha1
kind: Mapping
metadata:
  name: regex-route
spec:
  hostname: "*"
  prefix: "/user/[0-9]+/profile"
  prefix_regex: true
  service: user-service

prefix_regex: true を指定すると prefix を正規表現として解釈します。ただし、正規表現ルーティングには性能コストがあるため、本当に必要なときだけ使うのが望ましいです。

ヘッダーとクエリパラメータベースのルーティング

apiVersion: getambassador.io/v3alpha1
kind: Mapping
metadata:
  name: canary-by-header
spec:
  hostname: "*"
  prefix: /api/
  service: api-v2
  headers:
    x-api-version: "v2"

上の Mapping は x-api-version: v2 ヘッダーを持つリクエストだけを api-v2 へ送ります。ヘッダーがないリクエストは同じ prefix を処理する別の Mapping へ流れます。このようなヘッダーベースの分岐は、段階的な移行や内部テストトラフィックの分離に便利です。

パスの書き換えとホストの書き換え

apiVersion: getambassador.io/v3alpha1
kind: Mapping
metadata:
  name: rewrite-example
spec:
  hostname: "*"
  prefix: /legacy/
  rewrite: /v3/
  host_rewrite: internal.example.com
  service: modern-service

/legacy/foo リクエストはアップストリームには /v3/foo として渡され、Host ヘッダーは internal.example.com に変わります。レガシーのパスを維持しながらバックエンドだけを差し替えるときによく使うパターンです。

タイムアウト、リトライ、サーキットブレーカー

apiVersion: getambassador.io/v3alpha1
kind: Mapping
metadata:
  name: resilient-route
spec:
  hostname: "*"
  prefix: /orders/
  service: orders
  timeout_ms: 4000
  connect_timeout_ms: 1500
  retry_policy:
    retry_on: "5xx"
    num_retries: 3
  circuit_breakers:
    - max_connections: 2048
      max_pending_requests: 1024
      max_requests: 2048

Envoy の強力なレジリエンス(resilience)機能が Mapping フィールドとしてそのまま公開されます。retry_on はどの条件でリトライするか(5xx、gateway-error、connect-failure など)、circuit_breakers はアップストリームの過負荷を防ぐ上限値を定めます。

認証: AuthService

Emissary は Envoy の ext_authz(外部認可)フィルターを通じて認証を処理します。AuthService リソースを作ると、すべての(または特定の)リクエストがアップストリームに到達する前に認証サービスへ先に転送されます。

apiVersion: getambassador.io/v3alpha1
kind: AuthService
metadata:
  name: authentication
  namespace: default
spec:
  auth_service: "auth-service:3000"
  proto: http
  path_prefix: "/extauth"
  allowed_request_headers:
    - "x-request-id"
    - "authorization"
  allowed_authorization_headers:
    - "x-user-id"
    - "x-user-role"

動作は次のとおりです。

  1. リクエストが来ると、Emissary がボディ処理の前に auth-service:3000 へ認証リクエストを送ります。
  2. 認証サービスが 200 OK を返すとリクエストは通過し、レスポンスヘッダー(x-user-id など)がアップストリームへ転送されます。
  3. 認証サービスが 401/403 を返すと、リクエストはそこで遮断されます。

特定の Mapping だけ認証をスキップさせるには、その Mapping に bypass_auth: true を追加します。商用の Edge Stack では OAuth2/OIDC を Filter リソースとして宣言的に付けられるため、別途の認証サービスを自分で実装しなくても済みます。

レートリミット: RateLimitService

レートリミットも Envoy の ext フィルター機構を活用します。まず RateLimitService で外部レートリミットサービスを登録します。

apiVersion: getambassador.io/v3alpha1
kind: RateLimitService
metadata:
  name: ratelimit
  namespace: default
spec:
  service: "ratelimit-service:8081"
  protocol_version: v3
  domain: emissary

そして Mapping で、どの次元(descriptor)でレートリミットを適用するかをラベルで指定します。

apiVersion: getambassador.io/v3alpha1
kind: Mapping
metadata:
  name: rate-limited-api
spec:
  hostname: "*"
  prefix: /api/
  service: api
  labels:
    emissary:
      - request_label_group:
          - remote_address:
              key: remote_address

ここで labels の下のグループが Envoy の rate limit descriptor に変換され、外部レートリミットサービスへ転送されます。レートリミットサービスは descriptor ごとにカウンターを管理し、上限を超えると 429 を返すように構成します。オープンソースの Emissary は Envoy ratelimit サービス(Redis バックエンド)をそのまま使い、Edge Stack は上限を CRD で宣言する統合レートリミットを提供します。

トラフィック管理とカナリアリリース

API ゲートウェイ型 Ingress の真価はトラフィック管理で発揮されます。同じ prefix を処理する 2 つの Mapping に weight を与えると、ウェイトベースのトラフィック分割になります。

apiVersion: getambassador.io/v3alpha1
kind: Mapping
metadata:
  name: app-stable
spec:
  hostname: "*"
  prefix: /app/
  service: app-v1
  weight: 90
---
apiVersion: getambassador.io/v3alpha1
kind: Mapping
metadata:
  name: app-canary
spec:
  hostname: "*"
  prefix: /app/
  service: app-v2
  weight: 10

この設定は /app/ トラフィックの 90 パーセントを v1 へ、10 パーセントを v2 へ送ります。カナリアリリースの最も単純な形です。監視指標が正常であれば、weight を 10 から 25、50、100 へ段階的に上げていきます。

   incoming /app/ requests
            |
            v
   +-----------------+
   | Emissary route  |
   | weight split    |
   +--+-----------+--+
      | 90%       | 10%
      v           v
  app-v1       app-v2  (canary)

GitOps と組み合わせると、この weight 値を単に PR で調整するだけで段階的なロールアウトを制御できます。Argo Rollouts のようなツールと連携すれば、指標ベースの自動昇格も可能です。ヘッダーベースのカナリア(特定の社内ユーザーだけ新バージョン)とウェイトベースのカナリア(ランダムな割合)を組み合わせるのもよくあるパターンです。

トラフィックシャドーイング

apiVersion: getambassador.io/v3alpha1
kind: Mapping
metadata:
  name: shadow-traffic
spec:
  hostname: "*"
  prefix: /app/
  service: app-v2-shadow
  shadow: true

shadow: true は実トラフィックのコピーを新バージョンへ送りつつ、レスポンスは捨てます。つまりユーザーには影響を与えずに、新バージョンが実トラフィックをどう処理するかを観察できます。ダークローンチ(dark launch)に便利です。

API ゲートウェイ vs 単純な Ingress: いつ何を使うか

ここで一度整理しておきます。すべてのサービスに API ゲートウェイが必要なわけではありません。

単純な Ingress で十分な場合は次のとおりです。

  • 内部ツールや単一チームが所有する小規模なサービスを公開するとき
  • パスベースのルーティングと TLS 終端だけが必要なとき
  • 認証をアプリケーションやサービスメッシュレベルですでに処理しているとき

一方、Emissary のような API ゲートウェイ型 Ingress が輝くのは次の場合です。

  • 複数のチームがそれぞれマイクロサービスを所有し、ルーティング設定を分散管理したいとき
  • ゲートウェイレベルで認証、レートリミット、変換を一括適用したいとき
  • カナリア、トラフィックシャドーイング、ヘッダーベースのルーティングといった段階的なデプロイが必要なとき
  • gRPC、WebSocket、HTTP/3 など多様なプロトコルを単一の入口で処理するとき
要件おすすめ
単純なパスルーティング + TLS基本的な Ingress コントローラー
分散ルーティング所有権Emissary Mapping
ゲートウェイレベルの認証Emissary AuthService
段階的なカナリアEmissary weight または Argo Rollouts
標準化された未来志向の APIGateway API(Emissary サポート)

開発者ポータル

商用の Ambassador Edge Stack には開発者ポータル(Dev Portal)が含まれます。これは Mapping に OpenAPI 仕様を結び付けると、自動的に API カタログページを生成してくれる機能です。

apiVersion: getambassador.io/v3alpha1
kind: Mapping
metadata:
  name: catalog-api
  labels:
    docs.getambassador.io/source: "true"
spec:
  hostname: "*"
  prefix: /catalog/
  service: catalog
  docs:
    path: "/openapi.json"

docs.path に OpenAPI(Swagger)ドキュメントのパスを指定すると、ポータルがそれを収集し、外部・内部の開発者が探索できるドキュメントを自動生成します。マイクロサービスが増えるほど、「どの API がどこにあり、どう呼び出すか」を自動で集めてくれることは大きな運用上の利点です。オープンソースの Emissary 単独ではポータル UI は提供されないため、この機能が必要なら Edge Stack を検討する必要があります。

Gateway API: 未来の標準への流れ

先に述べたとおり、2026 年現在、ネットワーキング Ingress API は凍結され、Gateway API が後継標準として定着しました。Gateway API の核心はロール指向(role-oriented)の設計です。

  • GatewayClass: インフラ提供者が定義するゲートウェイ実装の種類(例: Emissary)
  • Gateway: クラスタ運用者が作る実際の入口(リスナー、ポート)
  • HTTPRoute: アプリケーション開発者が作るルーティングルール

この分離は、Emissary がもともと追求していた Listener/Host/Mapping の分離と哲学的に非常に似ています。だからこそ Emissary は独自の CRD とともに Gateway API もサポートします。

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: emissary-gateway
spec:
  gatewayClassName: emissary
  listeners:
    - name: http
      protocol: HTTP
      port: 8080
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: app-route
spec:
  parentRefs:
    - name: emissary-gateway
  hostnames:
    - "app.example.com"
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /app/
      backendRefs:
        - name: app
          port: 80

流れを見ると、GatewayClass が Emissary を指し、Gateway がポート 8080 のリスナーを開き、HTTPRoute が /app/ パスを app サービスへ送ります。Gateway API の backendRefs は複数のバックエンドと weight をサポートするため、カナリアのようなシナリオも標準的な方法で表現できます。

  GatewayClass(emissary)
        |
        v
     Gateway  ---- listener :8080
        ^
        | parentRefs
   HTTPRoute  ---- /app/ -> Service(app)

新規プロジェクトであれば、Mapping のようなベンダー CRD ではなく Gateway API で始めることを積極的に検討する価値があります。標準であるため、後でコントローラーを変更してもルーティング定義をそのまま再利用できる可能性が高いです。ただし、Gateway API がまだカバーできていない高度な機能(一部の Envoy の細かい設定)は、ベンダー CRD や拡張フィールドで補う必要がある場合があります。

実践的なデプロイのハンズオン

Helm で Emissary をインストールする最も一般的な流れをたどってみましょう。

# まず CRD をインストール
kubectl apply -f https://app.getambassador.io/yaml/emissary/3.10.0/emissary-crds.yaml
kubectl wait --timeout=90s --for=condition=available deployment emissary-apiext -n emissary-system

# Helm リポを追加してインストール
helm repo add datawire https://app.getambassador.io
helm repo update
helm install emissary-ingress datawire/emissary-ingress \
  --namespace emissary \
  --create-namespace
kubectl rollout status deployment/emissary-ingress -n emissary

インストールが終わったら、基本的な Listener を定義します。

apiVersion: getambassador.io/v3alpha1
kind: Listener
metadata:
  name: http-listener
  namespace: emissary
spec:
  port: 8080
  protocol: HTTP
  securityModel: XFP
  hostBinding:
    namespace:
      from: ALL
---
apiVersion: getambassador.io/v3alpha1
kind: Listener
metadata:
  name: https-listener
  namespace: emissary
spec:
  port: 8443
  protocol: HTTPS
  securityModel: XFP
  hostBinding:
    namespace:
      from: ALL

次に、ドメインと TLS を束ねる Host を作ります。cert-manager または Emissary 内蔵の ACME で証明書を自動発行できます。

apiVersion: getambassador.io/v3alpha1
kind: Host
metadata:
  name: example-host
  namespace: emissary
spec:
  hostname: app.example.com
  acmeProvider:
    authority: https://acme-v02.api.letsencrypt.org/directory
    email: ops@example.com
  tlsSecret:
    name: app-example-com-tls

最後に、ルーティング Mapping を適用して動作を確認します。

kubectl apply -f mapping.yaml

# 外部 IP を確認
kubectl get service emissary-ingress -n emissary

# ルーティングを確認
EMISSARY_IP=$(kubectl get svc emissary-ingress -n emissary -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
curl -i http://$EMISSARY_IP/backend/

# 診断情報を確認 (ポートフォワーディング)
kubectl port-forward -n emissary deploy/emissary-ingress 8877
curl http://localhost:8877/ambassador/v0/diag/?json=true | jq .

/ambassador/v0/diag/ 診断エンドポイントは、現在 Emissary が認識しているすべての Mapping とクラスタの状態を表示するため、設定が正しく反映されているかを確認するときに最初に見る場所です。

他の Envoy ベースコントローラーとの比較: Contour

Emissary 以外にも、Envoy をデータプレーンとして使う代表的なコントローラーに Contour があります。どちらも CNCF プロジェクトであり Envoy ベースですが、目指す方向が異なります。

項目Emissary-ingressContour
主な主体Ambassador Labs(コアは CNCF)VMware/CNCF
中核 CRDMapping, Host, ListenerHTTPProxy
目指す方向API ゲートウェイのフルセット軽量 Ingress + L7 ルーティング
認証/レートリミット内蔵(AuthService など)外部 ext_authz 連携
委譲モデルnamespace/リソース単位HTTPProxy include 委譲
Gateway APIサポート積極的にサポート
商用版Edge Stackなし(純粋なオープンソース)

おおまかな選択基準は次のとおりです。ゲートウェイレベルで認証、レートリミット、開発者ポータルといったフルセットの機能を望むなら Emissary が有利です。逆に、軽量で単純な Envoy ベースの L7 ルーターが必要で、認証は外部で処理するなら Contour がすっきりしています。Contour の HTTPProxy は include による委譲モデルが明確なので、マルチテナント環境でパスの衝突を防ぐのに強みがあります。

運用とチューニング

本番環境で Emissary を安定して運用するには、次の点を押さえるとよいでしょう。

リソースとスケーリング

Envoy はメモリ使用量が設定規模(ルートやクラスタの数)に比例して大きくなります。Mapping が数千個に増えると、コントローラーがスナップショットを再計算するコストも大きくなるため、適切な CPU/メモリのリクエストと上限を設定し、HPA で水平スケールを構成します。

# Helm values の例 (一部)
replicaCount: 3
resources:
  requests:
    cpu: 500m
    memory: 512Mi
  limits:
    cpu: "1"
    memory: 1Gi
autoscaling:
  enabled: true
  minReplicas: 3
  maxReplicas: 10
  targetCPUUtilizationPercentage: 70

可観測性

Emissary は Envoy の統計を Prometheus フォーマットで公開します。リクエスト数、レイテンシ(p50/p90/p99)、アップストリーム 5xx の比率、アクティブコネクションなどをダッシュボードで監視すると、カナリア昇格の判断根拠になります。分散トレーシングは TracingService リソースで Zipkin/Jaeger/OpenTelemetry コレクターに連携できます。

apiVersion: getambassador.io/v3alpha1
kind: TracingService
metadata:
  name: tracing
  namespace: emissary
spec:
  service: "otel-collector:9411"
  driver: zipkin
  sampling:
    overall: 10

無停止の再デプロイ

Emissary 自体をアップグレードするときは、PodDisruptionBudget と適切な readiness/liveness プローブ、そして十分な replica を置いて、ローリングアップデート中もトラフィックが途切れないようにします。LoadBalancer サービスの externalTrafficPolicy: Local を使うとクライアント IP は保存されますが、ノード分布に注意が必要です。

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

実際の運用でよく出会う問題を整理します。

  • 404 が出るのに Mapping は存在する: 最もよくある原因は、Listener がない、または Host の hostname とリクエストの Host ヘッダーがマッチしないケースです。まず /ambassador/v0/diag/ 診断ページで該当 Mapping が実際にロードされているかを確認してください。
  • 設定が反映されない: Emissary はすべての CRD を集めて一貫したスナップショットが作られたときだけ適用します。1 つでもリソースがバリデーションに失敗すると、スナップショット全体が拒否されることがあるため、コントローラーのログでバリデーションエラーを確認します。
  • TLS 証明書が適用されない: Host の tlsSecret が同じ namespace にあるか、ACME 発行が完了しているかを Host の state で確認します。ACME は 80 ポートへの HTTP-01 チャレンジの到達が可能でなければなりません。
  • gRPC が動かない: Mapping に grpc: true を付け忘れたケースが多いです。HTTP/2 prior knowledge の設定とプロトコルマッチングを併せて点検してください。
  • カナリアの比率が正確でないように感じる: weight は統計的な分割なので、リクエスト数が少ないと比率が揺れることがあります。また、同じ prefix を処理する Mapping の weight の合計が 100 でないと、意図と異なって正規化されます。
  • 503 upstream connect error: アップストリームサービスのエンドポイントが準備できているか、ポートとプロトコル(特に TLS origination の有無)が合っているかを確認します。cluster の状態を診断ページで見ることができます。

問題診断の黄金律は「診断エンドポイントが先、コントローラーのログが次、Envoy の統計が最後」です。この順番で見れば、ほとんどのルーティング問題は素早く絞り込めます。

おわりに

Emissary-ingress は単なる Ingress コントローラーではなく、Envoy の強力さを Kubernetes ネイティブな CRD で解きほぐした API ゲートウェイです。コントロールプレーンとデータプレーンの明確な分離、Mapping による分散ルーティング所有権、ゲートウェイレベルの認証とレートリミット、weight ベースのカナリアとトラフィックシャドーイング、そして Gateway API サポートまで、モダンなトラフィック管理に必要なほぼすべての要素を備えています。

2026 年の文脈で見ると、方向性は明確です。Ingress API は凍結され、Gateway API が標準の座を引き継ぎつつあり、既存のコントローラーはメンテナンスとセキュリティの負担を抱えています。こうした流れの中で、Envoy ベースのコントローラーを理解し、新規のルーティングは Gateway API で標準化しておく戦略は、将来の移行コストを大きく削減してくれます。

単純な公開だけが必要なら基本的な Ingress で十分です。しかし、複数のチーム、複数のマイクロサービス、段階的なデプロイ、ゲートウェイレベルのポリシーが必要な瞬間が訪れたら、Emissary のような API ゲートウェイ型 Ingress が答えになります。小さく始め、診断エンドポイントと可観測性を整え、weight と Gateway API で段階的に拡張していくことをお勧めします。

参考資料