Envoy Gateway : 1 seul LoadBalancer avec MergeGateways
Éviter la prolifération de LoadBalancers avec Envoy Gateway : la feature mergeGateways pour partager un seul Service LB entre plusieurs Gateways d'un cluster Kubernetes.

Dernier article sur Envoy Gateway ! Aujourd'hui, comment éviter d'avoir un Service LoadBalancer par Gateway déployée. La réponse d'Envoy Gateway : mergeGateways.
Lorsque j'ai migré depuis Ingress NGINX, j'ai été un peu dérouté avec la notion de HTTPRoutes, Gateways, etc. Et j'ai remarqué que pour chaque Gateway définie dans le cluster, Envoy Gateway génère son propre déploiement Envoy et son propre Service de type LoadBalancer. Si sur le principe c'est cohérent avec la spec, j'aimerais un moyen de mutualiser les LoadBalancers entre les Gateways du cluster, pour notamment économiser des coûts de cloud (et les IPs publiques).
La problématique : un LoadBalancer par Gateway
En théorie, les équipes devs n'ont pas à gérer les Gateways, seulement les Routes, c'est aux ops de gérer cette partie. Mais parfois on peut donner de l'autonomie aux équipes, et qu'elles gèrent leurs propres Gateways avec leur policies.
Sans mergeGateways, le schéma est le suivant : chaque équipe qui déploie une Gateway pour son application se retrouve avec son propre LB provisionné côté cloud, sa propre IP.
Gateway "eg" (envoy-gateway-system) → Service LB → IP 203.0.113.10
Gateway "app1-gw" → Service LB → IP 203.0.113.11
Gateway "app2-gw" → Service LB → IP 203.0.113.12
Gateway "monitoring-gw" (monitoring) → Service LB → IP 203.0.113.13
[...]Ce comportement est différent d'avant avec les ingress, où j'avais 1 seul LB pour toutes mes ressources. On va voir comment retrouver cette façon de faire.
La solution : mergeGateways
Envoy Gateway propose via la ressource EnvoyProxy un paramètre mergeGateways. Quand il est à true, toutes les Gateway utilisant la même GatewayClass partagent un seul déploiement Envoy et un seul Service LoadBalancer.
La ressource EnvoyProxy
C'est ici qu'on active la fusion des Gateways, et qu'on configure le Service LB partagé :
# envoyproxy-custom-proxy-config.yaml
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyProxy
metadata:
name: custom-proxy-config
namespace: envoy-gateway-system
spec:
mergeGateways: true # <-- là
provider:
type: Kubernetes
kubernetes:
envoyService:
type: LoadBalancer
envoyDeployment:
[...]Le GatewayClass qui référence l'EnvoyProxy
La GatewayClass fait le lien entre les Gateway et notre configuration EnvoyProxy, via parametersRef :
# gatewayclass-eg.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: eg
spec:
controllerName: gateway.envoyproxy.io/gatewayclass-controller
parametersRef:
group: gateway.envoyproxy.io
kind: EnvoyProxy
name: custom-proxy-config # <-- mergeGateways: true
namespace: envoy-gateway-systemToute Gateway déclarée avec gatewayClassName: eg bénéficiera automatiquement du merge.
La Gateway principale
La gateway "centrale" reste dans envoy-gateway-system, avec ses listeners HTTP/HTTPS habituels :
# gateway-eg.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: eg
namespace: envoy-gateway-system
spec:
gatewayClassName: eg
listeners:
- name: http
protocol: HTTP
port: 80
allowedRoutes:
namespaces:
from: All
- name: https
protocol: HTTPS
port: 443
tls:
mode: Terminate
certificateRefs:
- kind: Secret
name: ip-cert-tls # Voir article précédent
allowedRoutes:
namespaces:
from: AllExemple : App1 avec sa propre Gateway
Avec mergeGateways, chaque application peut avoir sa propre Gateway dans son propre namespace.
La Gateway applicative
App1 déclare sa Gateway dans son namespace app1, avec son listener HTTPS et son certificat Let's Encrypt géré par cert-manager via l'annotation :
# app1-gateway.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: app1-gateway
namespace: app1
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod-eg
spec:
gatewayClassName: eg
listeners:
- name: https
protocol: HTTPS
hostname: app1.gravitek.io
port: 443
tls:
mode: Terminate
certificateRefs:
- kind: Secret
name: app1.gravitek.io-tls
allowedRoutes:
namespaces:
from: SameLa HTTPRoute
# app1-httproute.yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: app1
namespace: app1
spec:
parentRefs:
- group: gateway.networking.k8s.io
kind: Gateway
name: app1-gateway
namespace: app1
sectionName: https
hostnames:
- app1.gravitek.io
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- kind: Service
name: app1
port: 8080
weight: 1Résultat : App1 est accessible via la même IP publique que toutes les autres applications, sans LB supplémentaire, avec son propre certificat géré de façon autonome dans son namespace.
Remarques
-
Si besoin particulier, chaque application peut maintenant gérer des règles différentes sur les
ClientTrafficPolicy,BackendTrafficPolicy& co. -
⚠️ Pour la gestion TLS, dans mon cas d'usage toutes les applis ont des routes HTTPS, pas HTTP. La redirection HTTP vers HTTPS est gérée par la
Gatewaypar défaut, avec un redirect permanent. Cela m'a posé soucis avec cert-manager, et cette configuration marche bien chez moi.
Alternative : ListenerSets
Gateway API v1.3 propose une approche alternative avec les ListenerSets : une Gateway principale expose des listeners de base, et des ListenerSet permettent d'en ajouter depuis d'autres namespaces.
C'est séduisant car c'est natif Gateway API (pas une extension Envoy), avec un modèle de permissions plus granulaire sur les listeners. Mais il y a des limites importantes à date :
- Seule
HTTPRoutesupportée : si vous utilisez desGRPCRoute,TCPRoute, elles ne peuvent pas référencer des listeners issus d'unListenerSet. - Encore expérimental : la feature est en alpha dans Gateway API, le support varie selon les implémentations.
Pour l'instant, mergeGateways reste la solution que j'ai choisie, à vous de voir laquelle vous préférez.
Conclusion
Avec mergeGateways, on passe d'un modèle "un LB par Gateway" à "un LB pour tout le cluster", retrouvant à peu près la configuration que j'avais avec Ingress NGINX :
- 💰 Un seul LoadBalancer partagé entre toutes les Gateways
- 🌐 Une seule IP publique → gestion DNS simplifiée
- 🧩 Autonomie conservée : chaque application gère sa propre
Gatewaydans son namespace, avec ses propres certificats et listeners
Bien sûr, rien ne vous empêche d'avoir plusieurs Gateways quand même, avec ou sans mergeGateways, cela dépend de votre besoin et architecture !
Ressources
- Envoy Gateway - mergeGateways : https://gateway.envoyproxy.io/docs/tasks/operations/deployment-mode/#merged-gateways-onto-a-single-envoyproxy-fleet
- Gateway API - ListenerSets (GEP-1713) : gateway-api.sigs.k8s.io/geps/gep-1713
- Envoy Gateway - EnvoyProxy API : gateway.envoyproxy.io/docs/api/extension_types/#envoyproxy