Skip to content
Published on

Kubernetes Gateway API 実践ガイド: Ingress移行からHTTPRoute·GRPCRoute·Envoy Gatewayプロダクション配備まで

Authors
  • Name
    Twitter
Kubernetes Gateway API 実践ガイド

はじめに

Kubernetes Ingressは長い間、外部トラフィックをクラスター内部にルーティングする標準的な方法でした。しかし、実装ごとに異なるアノテーションへの依存、単一リソースへの設定集中、gRPCやTCPなどの非HTTPプロトコル未対応など、限界は明確でした。Kubernetesコミュニティはこれらの問題を解決するためにGateway APIを設計し、現在v1.2でHTTPRouteとGRPCRouteが共にGA(Generally Available)状態となり、プロダクション環境で安定的に使用できます。

特にIngress NGINXの公式リタイアメントが発表され(2026年3月以降はベストエフォート維持のみ提供)、Gateway APIへの移行は選択ではなく必須となっています。本記事ではEnvoy Gatewayを実装として使用し、HTTPRoute、GRPCRoute、TLS終端、トラフィック分割、ヘッダーベースルーティング、レート制限まで、プロダクションに必要な全設定を実践コードと共に解説します。

Gateway API vs Ingress 比較

アーキテクチャの違い

Gateway APIはロールベースのリソースモデルで設計されており、インフラ運用者とアプリケーション開発者の関心事を明確に分離します。

比較項目IngressGateway API
リソースモデル単一IngressリソースGatewayClass / Gateway / Route分離
ロール分離不可インフラチーム / プラットフォームチーム / 開発チーム
プロトコル対応HTTP/HTTPSのみHTTP, gRPC, TCP, UDP, TLS
設定方式実装別アノテーション標準化されたスペック
クロスネームスペース未対応ReferenceGrantで対応
トラフィック分割アノテーション依存ネイティブweight基盤
ヘッダーマッチング実装により異なる標準スペックに含まれる
GA状態安定だがリタイア予定v1.2 GA(HTTPRoute、GRPCRoute)

リソース階層構造

Gateway APIの3層リソースモデルを見ていきましょう。

# 第1層: GatewayClass - インフラプロバイダーが管理(クラスタースコープ)
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: envoy-gateway
spec:
  controllerName: gateway.envoyproxy.io/gatewayclass-controller
---
# 第2層: Gateway - プラットフォームチームが管理(ネームスペーススコープ)
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: production-gateway
  namespace: infra-gateway
spec:
  gatewayClassName: envoy-gateway
  listeners:
    - name: https
      protocol: HTTPS
      port: 443
      tls:
        mode: Terminate
        certificateRefs:
          - name: wildcard-tls-cert
    - name: http
      protocol: HTTP
      port: 80
---
# 第3層: HTTPRoute - 開発チームが管理(各アプリケーションネームスペース)
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: api-route
  namespace: app-team-a
spec:
  parentRefs:
    - name: production-gateway
      namespace: infra-gateway
      sectionName: https
  hostnames:
    - 'api.example.com'
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /v1/users
      backendRefs:
        - name: user-service
          port: 8080

Envoy Gateway インストールと構成

Helmによるインストール

# Envoy Gateway Helmチャートインストール
helm install eg oci://docker.io/envoyproxy/gateway-helm \
  --version v1.3.0 \
  -n envoy-gateway-system \
  --create-namespace \
  --set config.envoyGateway.logging.level.default=info

# インストール確認
kubectl get pods -n envoy-gateway-system
kubectl get gatewayclass

# CRD確認
kubectl get crd | grep gateway.networking.k8s.io

GatewayClassおよびGateway作成

apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: envoy-production
spec:
  controllerName: gateway.envoyproxy.io/gatewayclass-controller
  parametersRef:
    group: gateway.envoyproxy.io
    kind: EnvoyProxy
    name: production-config
    namespace: envoy-gateway-system
---
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyProxy
metadata:
  name: production-config
  namespace: envoy-gateway-system
spec:
  provider:
    type: Kubernetes
    kubernetes:
      envoyDeployment:
        replicas: 3
        pod:
          annotations:
            prometheus.io/scrape: 'true'
            prometheus.io/port: '19001'
        container:
          resources:
            requests:
              cpu: '500m'
              memory: '512Mi'
            limits:
              cpu: '2000m'
              memory: '2Gi'
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: production-gateway
  namespace: infra-gateway
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
  gatewayClassName: envoy-production
  listeners:
    - name: https-wildcard
      protocol: HTTPS
      port: 443
      hostname: '*.example.com'
      tls:
        mode: Terminate
        certificateRefs:
          - name: wildcard-example-tls
      allowedRoutes:
        namespaces:
          from: Selector
          selector:
            matchLabels:
              gateway-access: 'true'
    - name: http-redirect
      protocol: HTTP
      port: 80
      allowedRoutes:
        namespaces:
          from: Same

HTTPRoute 詳細活用

パスベースルーティング

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: app-routes
  namespace: production
spec:
  parentRefs:
    - name: production-gateway
      namespace: infra-gateway
      sectionName: https-wildcard
  hostnames:
    - 'app.example.com'
  rules:
    # Exactマッチ - 最高優先度
    - matches:
        - path:
            type: Exact
            value: /healthz
      backendRefs:
        - name: health-check-service
          port: 8080
    # PathPrefixマッチ - APIバージョン別ルーティング
    - matches:
        - path:
            type: PathPrefix
            value: /api/v2
      backendRefs:
        - name: api-v2-service
          port: 8080
    - matches:
        - path:
            type: PathPrefix
            value: /api/v1
      backendRefs:
        - name: api-v1-service
          port: 8080
    # RegularExpressionマッチ
    - matches:
        - path:
            type: RegularExpression
            value: '/users/[0-9]+/profile'
      backendRefs:
        - name: user-profile-service
          port: 8080

ヘッダーベースルーティング

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: header-based-routing
  namespace: production
spec:
  parentRefs:
    - name: production-gateway
      namespace: infra-gateway
  hostnames:
    - 'api.example.com'
  rules:
    # 特定ヘッダー値に基づいて異なるバックエンドにルーティング
    - matches:
        - headers:
            - name: x-api-version
              value: 'beta'
            - name: x-user-tier
              value: 'premium'
      backendRefs:
        - name: api-beta-premium
          port: 8080
    # A/Bテスト用のヘッダーベースルーティング
    - matches:
        - headers:
            - name: x-experiment-group
              value: 'treatment'
      backendRefs:
        - name: api-experiment
          port: 8080
    # デフォルトルーティング(マッチ条件なし)
    - backendRefs:
        - name: api-stable
          port: 8080

トラフィック分割(カナリアデプロイ)

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: canary-route
  namespace: production
spec:
  parentRefs:
    - name: production-gateway
      namespace: infra-gateway
  hostnames:
    - 'app.example.com'
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /
      backendRefs:
        # 安定版: 90%トラフィック
        - name: app-stable
          port: 8080
          weight: 90
        # カナリア版: 10%トラフィック
        - name: app-canary
          port: 8080
          weight: 10

HTTPリダイレクトとURLリライト

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: redirect-and-rewrite
  namespace: production
spec:
  parentRefs:
    - name: production-gateway
      namespace: infra-gateway
  hostnames:
    - 'app.example.com'
  rules:
    # HTTP -> HTTPSリダイレクト
    - matches:
        - path:
            type: PathPrefix
            value: /
      filters:
        - type: RequestRedirect
          requestRedirect:
            scheme: https
            statusCode: 301
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: url-rewrite
  namespace: production
spec:
  parentRefs:
    - name: production-gateway
      namespace: infra-gateway
  hostnames:
    - 'api.example.com'
  rules:
    # /old-api/* -> /new-api/* パスリライト
    - matches:
        - path:
            type: PathPrefix
            value: /old-api
      filters:
        - type: URLRewrite
          urlRewrite:
            path:
              type: ReplacePrefixMatch
              replacePrefixMatch: /new-api
      backendRefs:
        - name: api-service
          port: 8080

GRPCRoute プロダクション構成

基本GRPCRoute設定

GRPCRouteはGateway API v1.1でGAに昇格し、v1.2では旧v1alpha2バージョンが完全に削除されました。

apiVersion: gateway.networking.k8s.io/v1
kind: GRPCRoute
metadata:
  name: order-service-route
  namespace: production
spec:
  parentRefs:
    - name: production-gateway
      namespace: infra-gateway
      sectionName: https-wildcard
  hostnames:
    - 'grpc.example.com'
  rules:
    # サービスベースマッチング
    - matches:
        - method:
            service: 'order.v1.OrderService'
      backendRefs:
        - name: order-service-grpc
          port: 50051
    # メソッドベースマッチング
    - matches:
        - method:
            service: 'order.v1.OrderService'
            method: 'CreateOrder'
      backendRefs:
        - name: order-write-service
          port: 50051
    # ヘッダーベースマッチング
    - matches:
        - headers:
            - name: x-region
              value: 'asia'
          method:
            service: 'order.v1.OrderService'
      backendRefs:
        - name: order-service-asia
          port: 50051

GRPCRoute トラフィック分割

apiVersion: gateway.networking.k8s.io/v1
kind: GRPCRoute
metadata:
  name: grpc-canary
  namespace: production
spec:
  parentRefs:
    - name: production-gateway
      namespace: infra-gateway
  hostnames:
    - 'grpc.example.com'
  rules:
    - matches:
        - method:
            service: 'payment.v1.PaymentService'
      backendRefs:
        - name: payment-service-stable
          port: 50051
          weight: 95
        - name: payment-service-canary
          port: 50051
          weight: 5

TLS終端と証明書管理

cert-manager連携

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: admin@example.com
    privateKeySecretRef:
      name: letsencrypt-prod-key
    solvers:
      - http01:
          gatewayHTTPRoute:
            parentRefs:
              - name: production-gateway
                namespace: infra-gateway
---
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: wildcard-example-tls
  namespace: infra-gateway
spec:
  secretName: wildcard-example-tls
  issuerRef:
    name: letsencrypt-prod
    kind: ClusterIssuer
  dnsNames:
    - '*.example.com'
    - 'example.com'

mTLS設定(Envoy Gateway BackendTLSPolicy)

apiVersion: gateway.networking.k8s.io/v1alpha3
kind: BackendTLSPolicy
metadata:
  name: backend-mtls
  namespace: production
spec:
  targetRefs:
    - group: ''
      kind: Service
      name: secure-backend
  validation:
    caCertificateRefs:
      - name: backend-ca-cert
        group: ''
        kind: ConfigMap
    hostname: secure-backend.production.svc.cluster.local

Envoy Gateway レート制限

グローバルレート制限

apiVersion: gateway.envoyproxy.io/v1alpha1
kind: BackendTrafficPolicy
metadata:
  name: global-rate-limit
  namespace: infra-gateway
spec:
  targetRefs:
    - group: gateway.networking.k8s.io
      kind: Gateway
      name: production-gateway
  rateLimit:
    type: Global
    global:
      rules:
        - clientSelectors:
            - headers:
                - name: x-api-key
                  type: Distinct
          limit:
            requests: 100
            unit: Minute
        - limit:
            requests: 1000
            unit: Minute

ルート別レート制限

apiVersion: gateway.envoyproxy.io/v1alpha1
kind: BackendTrafficPolicy
metadata:
  name: api-rate-limit
  namespace: production
spec:
  targetRefs:
    - group: gateway.networking.k8s.io
      kind: HTTPRoute
      name: api-route
  rateLimit:
    type: Global
    global:
      rules:
        - clientSelectors:
            - headers:
                - name: x-user-tier
                  value: 'free'
          limit:
            requests: 10
            unit: Minute
        - clientSelectors:
            - headers:
                - name: x-user-tier
                  value: 'premium'
          limit:
            requests: 1000
            unit: Minute

ReferenceGrantによるクロスネームスペースルーティング

# app-team-aネームスペースのHTTPRouteが
# shared-servicesネームスペースのServiceを参照できるように許可
apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
  name: allow-cross-ns-routing
  namespace: shared-services
spec:
  from:
    - group: gateway.networking.k8s.io
      kind: HTTPRoute
      namespace: app-team-a
    - group: gateway.networking.k8s.io
      kind: HTTPRoute
      namespace: app-team-b
  to:
    - group: ''
      kind: Service

IngressからGateway APIへの移行

ingress2gatewayツールの活用

# ingress2gatewayインストール
go install github.com/kubernetes-sigs/ingress2gateway@latest

# 既存IngressリソースをGateway APIに変換
ingress2gateway print \
  --input-file existing-ingress.yaml \
  --providers ingress-nginx \
  --all-resources

# クラスターから直接変換
ingress2gateway print \
  --providers ingress-nginx \
  --all-resources \
  --namespace production

移行戦略: 並行運用

# 既存Ingress(維持)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: legacy-app-ingress
  namespace: production
  annotations:
    nginx.ingress.kubernetes.io/ssl-redirect: 'true'
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - app.example.com
      secretName: app-tls
  rules:
    - host: app.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: app-service
                port:
                  number: 8080
---
# 新しいGateway API HTTPRoute(並行デプロイ)
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: app-route
  namespace: production
spec:
  parentRefs:
    - name: production-gateway
      namespace: infra-gateway
  hostnames:
    - 'app.example.com'
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /
      backendRefs:
        - name: app-service
          port: 8080

段階的移行手順

# ステップ1: Gateway API CRDインストール確認
kubectl get crd gateways.gateway.networking.k8s.io

# ステップ2: Envoy Gatewayインストール
helm install eg oci://docker.io/envoyproxy/gateway-helm \
  --version v1.3.0 \
  -n envoy-gateway-system --create-namespace

# ステップ3: GatewayClass/Gateway作成
kubectl apply -f gateway-class.yaml
kubectl apply -f gateway.yaml

# ステップ4: 既存IngressをHTTPRouteに変換
ingress2gateway print --providers ingress-nginx --all-resources > routes.yaml

# ステップ5: 変換されたHTTPRouteをデプロイ(並行運用)
kubectl apply -f routes.yaml

# ステップ6: DNSを新しいGateway LoadBalancerに切り替え
GATEWAY_IP=$(kubectl get gateway production-gateway -n infra-gateway \
  -o jsonpath='{.status.addresses[0].value}')
echo "Update DNS A record to: $GATEWAY_IP"

# ステップ7: トラフィック監視後、既存Ingressを削除
kubectl delete ingress legacy-app-ingress -n production

モニタリング構成

Prometheusメトリクス収集

apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: envoy-gateway-metrics
  namespace: envoy-gateway-system
spec:
  selector:
    matchLabels:
      app.kubernetes.io/name: envoy
  endpoints:
    - port: metrics
      interval: 15s
      path: /stats/prometheus
---
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: gateway-api-alerts
  namespace: monitoring
spec:
  groups:
    - name: gateway-api
      rules:
        - alert: GatewayHighErrorRate
          expr: |
            sum(rate(envoy_http_downstream_rq_xx{envoy_response_code_class="5"}[5m])) by (envoy_http_conn_manager_prefix)
            /
            sum(rate(envoy_http_downstream_rq_total[5m])) by (envoy_http_conn_manager_prefix)
            > 0.05
          for: 5m
          labels:
            severity: critical
          annotations:
            summary: 'Gateway error rate exceeds 5%'
        - alert: GatewayHighLatency
          expr: |
            histogram_quantile(0.99,
              sum(rate(envoy_http_downstream_rq_time_bucket[5m])) by (le, envoy_http_conn_manager_prefix)
            ) > 1000
          for: 5m
          labels:
            severity: warning
          annotations:
            summary: 'Gateway p99 latency exceeds 1s'

Grafanaダッシュボードクエリ

# 主要Envoyメトリクス
# 秒間リクエスト数
sum(rate(envoy_http_downstream_rq_total[5m])) by (envoy_http_conn_manager_prefix)

# エラー率(5xx)
sum(rate(envoy_http_downstream_rq_xx{envoy_response_code_class="5"}[5m]))

# 平均応答時間
sum(rate(envoy_http_downstream_rq_time_sum[5m])) / sum(rate(envoy_http_downstream_rq_time_count[5m]))

# アクティブ接続数
envoy_http_downstream_cx_active

運用時の注意事項

1. リソース制限設定

プロダクション環境では必ずEnvoy Proxyのリソース制限を設定してください。デフォルト値で運用するとトラフィック急増時にOOM(Out of Memory)が発生する可能性があります。

2. Gatewayリスナー制限

一つのGatewayに多数のリスナーを追加するとEnvoy設定が肥大化し、リロード時間が長くなります。ドメインまたはチーム単位でGatewayを分離することを推奨します。

3. ReferenceGrant最小権限

クロスネームスペース参照は必要な場合のみ許可し、対象ネームスペースとリソースをできるだけ具体的に指定してください。

4. HTTPRoute優先順位

複数のHTTPRouteが同一パスにマッチする場合、優先順位ルールを必ず理解しておく必要があります:

  1. 最も長いホスト名が優先
  2. 最も長いパスが優先
  3. ExactマッチがPathPrefixより優先
  4. 同一条件の場合、作成時刻が早いリソースが優先

5. v1alpha2削除への対応

Gateway API v1.2ではGRPCRouteとReferenceGrantのv1alpha2バージョンが削除されました。既存にv1alpha2を使用している場合、必ずv1へ移行してください。

障害事例と復旧手順

事例1: TLS証明書期限切れ

# 症状: 503エラー、TLSハンドシェイク失敗
# 診断
kubectl get certificate -n infra-gateway
kubectl describe certificate wildcard-example-tls -n infra-gateway

# 復旧: cert-manager強制更新
kubectl delete certificaterequest -n infra-gateway --all
kubectl annotate certificate wildcard-example-tls \
  -n infra-gateway \
  cert-manager.io/renew-before="720h" --overwrite

# 緊急時: 手動証明書交換
kubectl create secret tls wildcard-example-tls \
  --cert=fullchain.pem --key=privkey.pem \
  -n infra-gateway --dry-run=client -o yaml | kubectl apply -f -

事例2: Gatewayコントローラー障害

# 症状: 新しいHTTPRouteが適用されない
# 診断
kubectl get pods -n envoy-gateway-system
kubectl logs -n envoy-gateway-system deploy/envoy-gateway -f

# Envoy Proxy状態確認
kubectl get pods -l app.kubernetes.io/name=envoy -A

# 復旧: コントローラー再起動
kubectl rollout restart deployment/envoy-gateway -n envoy-gateway-system

# Gateway状態確認
kubectl get gateway production-gateway -n infra-gateway -o yaml

事例3: 不正なHTTPRouteによるトラフィックブラックホール

# 症状: 特定パスへのリクエストが全て404
# 診断: HTTPRoute状態確認
kubectl get httproute -A
kubectl describe httproute app-route -n production

# 状態のAccepted/ResolvedRefs条件を確認
# 原因: backendRefが存在しないサービスを参照

# 復旧: 正しいサービス名に修正して再適用
kubectl apply -f corrected-httproute.yaml

# Envoy設定同期確認
kubectl exec -n envoy-gateway-system deploy/envoy-gateway -- \
  curl -s localhost:19000/config_dump | python3 -m json.tool | head -100

事例4: レート制限の誤動作

# 症状: 正常なユーザーも429 Too Many Requestsが発生
# 診断
kubectl get backendtrafficpolicy -A
kubectl describe backendtrafficpolicy global-rate-limit -n infra-gateway

# Redisベースのグローバルレートリミッター状態確認
kubectl logs -n envoy-gateway-system deploy/envoy-ratelimit

# 復旧: レート制限ポリシー一時削除
kubectl delete backendtrafficpolicy global-rate-limit -n infra-gateway

# 正常化後、修正したポリシーを再適用
kubectl apply -f corrected-rate-limit.yaml

まとめ

Kubernetes Gateway APIはIngressの限界を克服し、ロールベースの体系的なトラフィック管理を実現します。v1.2でHTTPRouteとGRPCRouteが共にGAで安定化し、Ingress NGINXのリタイアメントが迫っている今が移行の適期です。

Envoy Gatewayを実装として選択すれば、レート制限、認証、グローバルロードバランシングなどEnvoyの強力な機能をGateway APIの標準インターフェースで活用できます。並行運用戦略で安全に移行し、モニタリングとアラートを必ず併せて構成してプロダクションの安定性を確保してください。

参考資料