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.
1Gateway "eg" (envoy-gateway-system) → Service LB → IP 203.0.113.10
2Gateway "app1-gw" → Service LB → IP 203.0.113.11
3Gateway "app2-gw" → Service LB → IP 203.0.113.12
4Gateway "monitoring-gw" (monitoring) → Service LB → IP 203.0.113.13
5[...]
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é :
1# envoyproxy-custom-proxy-config.yaml
2apiVersion: gateway.envoyproxy.io/v1alpha1
3kind: EnvoyProxy
4metadata:
5 name: custom-proxy-config
6 namespace: envoy-gateway-system
7spec:
8 mergeGateways: true # <-- là
9 provider:
10 type: Kubernetes
11 kubernetes:
12 envoyService:
13 type: LoadBalancer
14 envoyDeployment:
15 [...]
Le GatewayClass qui référence l'EnvoyProxy
La GatewayClass fait le lien entre les Gateway et notre configuration EnvoyProxy, via parametersRef :
1# gatewayclass-eg.yaml
2apiVersion: gateway.networking.k8s.io/v1
3kind: GatewayClass
4metadata:
5 name: eg
6spec:
7 controllerName: gateway.envoyproxy.io/gatewayclass-controller
8 parametersRef:
9 group: gateway.envoyproxy.io
10 kind: EnvoyProxy
11 name: custom-proxy-config # <-- mergeGateways: true
12 namespace: envoy-gateway-system
Toute 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 :
1# gateway-eg.yaml
2apiVersion: gateway.networking.k8s.io/v1
3kind: Gateway
4metadata:
5 name: eg
6 namespace: envoy-gateway-system
7spec:
8 gatewayClassName: eg
9 listeners:
10 - name: http
11 protocol: HTTP
12 port: 80
13 allowedRoutes:
14 namespaces:
15 from: All
16 - name: https
17 protocol: HTTPS
18 port: 443
19 tls:
20 mode: Terminate
21 certificateRefs:
22 - kind: Secret
23 name: ip-cert-tls # Voir article précédent
24 allowedRoutes:
25 namespaces:
26 from: All
Exemple : 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 :
1# app1-gateway.yaml
2apiVersion: gateway.networking.k8s.io/v1
3kind: Gateway
4metadata:
5 name: app1-gateway
6 namespace: app1
7 annotations:
8 cert-manager.io/cluster-issuer: letsencrypt-prod-eg
9spec:
10 gatewayClassName: eg
11 listeners:
12 - name: https
13 protocol: HTTPS
14 hostname: app1.gravitek.io
15 port: 443
16 tls:
17 mode: Terminate
18 certificateRefs:
19 - kind: Secret
20 name: app1.gravitek.io-tls
21 allowedRoutes:
22 namespaces:
23 from: Same
La HTTPRoute
1# app1-httproute.yaml
2apiVersion: gateway.networking.k8s.io/v1
3kind: HTTPRoute
4metadata:
5 name: app1
6 namespace: app1
7spec:
8 parentRefs:
9 - group: gateway.networking.k8s.io
10 kind: Gateway
11 name: app1-gateway
12 namespace: app1
13 sectionName: https
14 hostnames:
15 - app1.gravitek.io
16 rules:
17 - matches:
18 - path:
19 type: PathPrefix
20 value: /
21 backendRefs:
22 - kind: Service
23 name: app1
24 port: 8080
25 weight: 1
Ré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
