- Published on
Kubernetes Gateway API 実践ガイド: Ingress移行からHTTPRoute·GRPCRoute·Envoy Gatewayプロダクション配備まで
- Authors
- Name
- はじめに
- Gateway API vs Ingress 比較
- Envoy Gateway インストールと構成
- HTTPRoute 詳細活用
- GRPCRoute プロダクション構成
- TLS終端と証明書管理
- Envoy Gateway レート制限
- ReferenceGrantによるクロスネームスペースルーティング
- Ingressから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はロールベースのリソースモデルで設計されており、インフラ運用者とアプリケーション開発者の関心事を明確に分離します。
| 比較項目 | Ingress | Gateway 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が同一パスにマッチする場合、優先順位ルールを必ず理解しておく必要があります:
- 最も長いホスト名が優先
- 最も長いパスが優先
- ExactマッチがPathPrefixより優先
- 同一条件の場合、作成時刻が早いリソースが優先
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の標準インターフェースで活用できます。並行運用戦略で安全に移行し、モニタリングとアラートを必ず併せて構成してプロダクションの安定性を確保してください。