はじめに
本記事では、KubernetesのGateway API実装の一つであるEnvoy Gatewayについて、Envoy Proxy関連リソースの作成先namespaceを決める2つのモードを紹介します。あわせて、ControllerNamespace モードから GatewayNamespace モードへ移行する際の注意点について説明します。
Envoy Gateway とは?
Envoy Gatewayは、高機能プロキシ「Envoy Proxy」をKubernetes上で簡単に使うための仕組みです。Gateway APIというKubernetesの標準的なAPIを使って、外部からのアクセスをどのServiceに流すか、といったルーティング設定を管理できます。
Envoy Gatewayを使うと、外部からのリクエストをどのServiceに転送するかを制御したり、TLS終端やHTTPヘッダーの操作など、トラフィックに関するさまざまな設定をGateway APIのリソースとして管理できます。
Gateway APIやEnvoy Gatewayのカスタムリソースを使うため、マニフェスト上は少し複雑に見えます。ただしPodの配置だけを見ると、Webアプリケーションの前段にEnvoy Proxyが置かれる構成であり、イメージとしては次のようになります。
ユーザーはGateway、HTTPRoute、EnvoyProxyなどのリソースを使って、必要な通信設定を宣言します。Envoy Gatewayのコントローラーはそれらのリソースを監視し、宣言された内容に合わせてEnvoy ProxyのService、Deployment、Podなどを作成・更新します。
例えばWebアプリが app-a というnamespaceにある場合、その前段にEnvoy Proxyを配置するには、GatewayやHTTPRouteなどのリソースを同じ app-a namespaceに作成します。すると、Envoy Gatewayのコントローラーによって、Webアプリの前段にEnvoy Proxyが作成されます。
Envoy Gatewayのモードについて
Envoy Gatewayには、Envoy Proxy関連リソースをどのnamespaceに作成するかを決めるモードがあります。
2026/06/11現在の最新バージョンv1.8.1では、ControllerNamespace と GatewayNamespace の2つのモードを選択できます。ControllerNamespace は、Envoy Gatewayのコントローラーが動いているnamespaceにEnvoy ProxyのServiceやDeployment、Podを作成するモードです。一方、GatewayNamespace は、Gatewayリソースが作成されたnamespaceにEnvoy Proxy関連リソースを作成するモードです。
デフォルトでは ControllerNamespace が使われます。そのため、Envoy Gatewayのコントローラーを envoy-gateway-system namespaceにデプロイしている場合、app-a のnamespaceにGatewayやHTTPRouteを作成しても、Envoy ProxyのServiceやDeployment、Podは envoy-gateway-system に作成されます。
ここからは、各モードで実際にどのnamespaceにリソースが作成されるのかを、簡単な例で見ていきます。
まず、app-a namespaceに app-a-backend というWebサービスが作成されているとします。
$ kubectl get deployment,svc -n app-a -l app=backend
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/app-a-backend 1/1 1 1 8m42s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/app-a-backend ClusterIP 10.111.14.0 <none> 80/TCP 8m42s
このサービスをEnvoy Proxy経由で公開するために、同じ app-a namespaceにEnvoyProxy、Gateway、HTTPRouteを作成します。
$ kubectl get envoyproxy,gateway,httproute -n app-a
NAME AGE
envoyproxy.gateway.envoyproxy.io/app-a-proxy-config 32s
NAME CLASS ADDRESS PROGRAMMED AGE
gateway.gateway.networking.k8s.io/app-a-gateway eg-controller-namespace-demo xx.yy.zz.aa True 4m14s
NAME HOSTNAMES AGE
httproute.gateway.networking.k8s.io/app-a-route 4m14s
続いて、上記のようにGatewayやHTTPRouteを使った場合に、Envoy Proxy関連リソースの作成先がモードによってどう変わるのかを見ていきます。この例では、Envoy Gatewayのコントローラーは envoy-gateway-system のnamespaceで動いているものとします。
ControllerNamespaceモード(デフォルト)の場合
GatewayやHTTPRouteは app-a namespaceに作成していますが、ControllerNamespace モードでは、実際にトラフィックを処理するEnvoy ProxyのDeploymentやServiceは app-a ではなく、コントローラーが動いている envoy-gateway-system namespaceに作成されます。
$ kubectl get deployment,svc -n envoy-gateway-system -l app.kubernetes.io/name=envoy
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/envoy-app-a-app-a-gateway-3bc2d0d6 1/1 1 1 16m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/envoy-app-a-app-a-gateway-3bc2d0d6 NodePort 10.107.240.218 <none> 80:31408/TCP 16m
このように、ControllerNamespace モードではEnvoy Proxy関連リソースがコントローラー側のnamespaceに集約されます。また、リソース名にはGatewayのnamespace名やGateway名が含まれるため、名前も長くなります。
なお、上記のEnvoyProxyのServiceが開けているNodePort 31408 にcurlでアクセスすると、以下のようにバックエンドのWebアプリに接続可能なことがわかります。
$ curl http://localhost:31408
app-a backend
GatewayNamespaceモードの場合
一方、GatewayNamespace モードでは、Envoy ProxyのDeploymentやServiceがGatewayと同じ app-a namespaceに作成されます。
$ kubectl get deployment,svc -n app-a -l app.kubernetes.io/name=envoy
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/app-a-gateway 1/1 1 1 90s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/app-a-gateway NodePort 10.108.95.49 <none> 80:32182/TCP 90s
ControllerNamespace モードと比べると、Envoy Proxy関連リソースの作成先がアプリケーション側のnamespaceになり、リソース名も短くなっていることが分かります。
2つのモードの比較
以下は、2つのモードの比較表です。
| 項目 | ControllerNamespace | GatewayNamespace |
|---|---|---|
| Envoy Proxy関連リソースの作成先 | Envoy Gatewayのコントローラーが動いているnamespace | Gatewayリソースが作成されたnamespace |
| デフォルト設定 | デフォルトで使用される | 明示的に有効化する必要がある |
| リソース名 | Gatewayのnamespace名などが含まれ、長くなりやすい | Gateway名と同じで、短い名前になりやすい |
| 複数namespaceでGatewayを使う場合 | Envoy Proxy関連リソースがコントローラー側namespaceに集約される | 各GatewayのnamespaceにEnvoy Proxy関連リソースが分散される |
モードを変更する方法
Envoy Gatewayは、設定を変更することで別のnamespaceモードに切り替えることができます。ここでは、ControllerNamespace モードから GatewayNamespace モードへ移行する ケースを見ていきます。
前提として、Install with Helm(公式ドキュメント)にしたがってEnvoy GatewayのHelmチャートでデプロイされているとします。
GatewayNamespace モードに切り替えるには、Helmのvaluesに以下の設定を追加します。
config:
envoyGateway:
provider:
type: Kubernetes
kubernetes:
deploy:
type: GatewayNamespace
作成したvaluesファイルを指定して、例えば以下のように helm upgrade を実行します。
helm upgrade <release-name> oci://docker.io/envoyproxy/gateway-helm \
--namespace envoy-gateway-system \
--version <envoy-gateway-version> \
--reuse-values \
-f values-gateway-namespace.yaml
helm upgrade が完了すると、Envoy ProxyのServiceやDeploymentがGatewayのnamespaceに作成されます。例えばGatewayが app-a のnamespaceにある場合、Envoy Proxy関連のリソースも同様に app-a のnamespaceに作成されます。
$ kubectl get deployment,svc -n app-a -l app.kubernetes.io/name=envoy
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/app-a-gateway 1/1 1 1 90s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/app-a-gateway NodePort 10.108.95.49 <none> 80:32182/TCP 90s
上の例では、GatewayNamespaceモードで作成したEnvoy ProxyのServiceがNodePort 32182 で公開されています。試しにcurlを実行すると、以下のようにバックエンドへ到達できることを確認できます。
$ curl http://localhost:32182
app-a backend
古いEnvoy Proxyも使えるが、危険!
GatewayNamespace モードに移行した後も、ControllerNamespace モードで作成した古いEnvoy Proxyは消えずに残ります。kubectl get でServiceとPodを確認すると、以下のように旧モードのリソースが残っているのがわかります。(各々で2番目に出力されているリソースが該当します)
$ kubectl get svc,po -A -l app.kubernetes.io/name=envoy
NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
app-a service/app-a-gateway NodePort 10.108.95.49 <none> 80:32182/TCP 2d23h
envoy-gateway-system service/envoy-app-a-app-a-gateway-3bc2d0d6 NodePort 10.107.240.218 <none> 80:31408/TCP 2d23h # 古いEnvoy Proxy
NAMESPACE NAME READY STATUS RESTARTS AGE
app-a pod/app-a-gateway-595698c9c5-jzt4h 2/2 Running 0 44h
envoy-gateway-system pod/envoy-app-a-app-a-gateway-3bc2d0d6-c749cb494-zpsxd 2/2 Running 0 44h # 古いEnvoy Proxy
そして古いEnvoy Proxyも、ServiceのNodePort 31408 を使って、以下のようにバックエンドに到達可能です。
$ curl http://localhost:31408
app-a backend
「モード移行作業が全て完了するまでの間はしばらく、古いEnvoy Proxyを使うこともできそう」と思うかもしれませんが、結論から言うとその運用は危険です!
試しに、古いEnvoy Proxyのレプリカ数を2に変更してみます。
$ kubectl scale -n envoy-gateway-system --replicas=2 deploy/envoy-app-a-app-a-gateway-3bc2d0d6
deployment.apps/envoy-app-a-app-a-gateway-3bc2d0d6 scaled
すると、2つ目のPodはいつまで経ってもReadyになりません。
$ kubectl get pod -n envoy-gateway-system -l app.kubernetes.io/name=envoy
NAME READY STATUS RESTARTS AGE
envoy-app-a-app-a-gateway-3bc2d0d6-c749cb494-kqx8c 1/2 Running 4 (4m23s ago) 48m
envoy-app-a-app-a-gateway-3bc2d0d6-c749cb494-zpsxd 2/2 Running 0 45h
この状態では、もともと起動していた 2/2 のPodが残っているため、古いEnvoy Proxy経由の通信はまだ成功します。しかし、そのPodが削除されたり再作成されたりすると、新しく起動したPodはReadyにならないため、古いEnvoy Proxy経由では通信できなくなります。
readyにならない理由
readyにならないPodのeventを確認すると、ready用に使われる 19003 のポートに接続できないことがわかります。
$ kubectl describe po -n envoy-gateway-system envoy-app-a-app-a-gateway-3bc2d0d6-c749cb494-kqx8c | tail -n 1
Warning Unhealthy 3s (x14 over 2m13s) kubelet Startup probe failed: Get "http://10.0.0.129:19003/ready": dial tcp 10.0.0.129:19003: connect: connection refused
Envoy Proxyが起動する際、Envoy GatewayのcontrollerからxDSに関する設定を受け取ります。xDSというのは、主にマイクロサービスやサービスメッシュにおいて、プロキシがルーティング情報などの設定を動的に取得するためのAPI群の総称です。DSはDiscovery Serviceの略で、先頭のxは様々なサービス(ClusterやEndpoint等)が対象となることを意味します。
古いEnvoy Proxy Podがreadyにならない理由は、controllerからxDSの設定を受け取れないことが原因です。なぜ受け取れないかというと、公式ドキュメントに書かれているように、両者で通信する際の認証方法が ControllerNamespace モードではmTLSだったのが、GatewayNamespace モードではJWTを使った検証に変わったためです。
古いEnvoy ProxyではmTLSで認証を試みているのにcontrollerはJWT検証を求めているので、いつまで経っても通信できずにreadyにならなかったわけです。
おわりに
ここまで見てきたように、モード移行前から起動していた古いEnvoy Proxy Podが動き続けている間は、バックエンドへの通信ができる場合があります。しかし、そのPodが何らかの理由で削除されて再作成されると、新しいPodはxDSの設定を取得できず、Readyになりません。そのため、「古いEnvoy Proxy経由でも通信できているから大丈夫」と考えて、そのまま使い続けるのは避けるべきです。
また、本記事では詳細には触れませんが、業務環境のGerritでは、古いEnvoy Proxy Podが起動している状態でもログインできなくなる事象を確認しました。原因を完全に切り分けたわけではありませんが、少なくとも「PodがRunningだからアプリケーションも正常に動く」とは判断できない点には注意が必要です。