Aller au contenu
Background Image

La Gateway API, le nouveau standard d'exposition de vos services Kubernetes

Romain Boulanger
Auteur
Romain Boulanger
Architecte Infra/Cloud avec une touche de DevSecOps
Sommaire

Exposer ses services dans Kubernetes, facile !
#

Exposer des services dans Kubernetes peut sembler relativement simple au premier regard… Après tout, il suffit d’utiliser un objet Ingress et le tour est joué, non ?

En pratique, c’est effectivement ce que l’on fait au quotidien. On déploie un Ingress Controller comme NGINX, Traefik, Contour ou encore Istio (la liste est évidemment non-exhaustive), puis on déploie ces quelques lignes :

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: filador
spec:
  rules:
  - http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: blog
            port:
              number: 8080

Et notre blog est exposé à la vue de tous ! (sans oublier l’entrée DNS qui va bien…)

L’Ingress Controller fait donc partie des composants presque impératifs à déployer au sein de Kubernetes. En effet, ce composant qui agit comme un reverse proxy HTTP et/ou HTTPS n’est pas fourni dans le Kubernetes de base appelé vanilla.

Ces différentes solutions ont fait leurs preuves mais derrière cette apparente simplicité se cache une réalité plus complexe…

Les quelques problématiques de l’Ingress
#

L’objet Ingress de Kubernetes, bien qu’indispensable, montre rapidement ses limites quand on cherche à aller au-delà des fonctionnalités de base.

Le problème des annotations
#

Chaque Ingress Controller implémente ses propres annotations pour étendre les capacités de l’objet Ingress standard. Résultat ? Votre configuration devient intimement liée à l’outil choisi :

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    # Annotations spécifiques à NGINX
    nginx.ingress.kubernetes.io/rewrite-target: /
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    # Ou bien spécifiques à Traefik
    traefik.ingress.kubernetes.io/router.middlewares: auth@file
[...]

Cette approche rend les choses compliquées si vous souhaitez changer de produit. En réalité, l’Ingress est un reverse proxy basique qui est drastiquement enrichi par ces fameuses annotations sans modifier l’API de l’objet Kubernetes en lui-même. On a donc plutôt tendance à choisir son Ingress Controller pour les fonctionnalités fournies que pour une autre raison.

Certains sont allés encore plus loin dans cette démarche, quitte à créer un objet à part entière, c’est le cas de Traefik (dont je vous ai déjà parlé dans un précédent article) qui permet d’utiliser l’IngressRoute pour éviter de se baser sur ce genre de mécanisme et rendre la configuration plus lisible à la sauce Traefik bien sûr !

Gestion du TLS : qui fait quoi ?
#

En voilà un autre point important…

La gestion du certificat TLS qui est lui même porté par l’Ingress :

tls:
- hosts:
    - blog.filador.ch
  secretName: mon-super-certificat-tls

Seulement, cet objet est souvent à la main des équipes qui vont déployer leurs applications au sein du cluster, alors que le certificat est plutôt un composant critique qui est plutôt déployé par les équipes plus “infra”.

Alors oui, les Ingress Controller permettent souvent de définir un certificat par défaut mais ce n’est pas forcément suffisant et la génération automatique de certificat avec des outils comme cert-manager ne répond pas à toutes les exigences non plus.

Au-delà du HTTP et HTTPS
#

L’objet Ingress a été conçu initialement pour les protocoles HTTP et HTTPS. Mais que faire quand vos applications utilisent d’autres protocoles ? Par exemple TCP, UDP, gRPC, etc.

Alors oui, traditionnellement on utilise des Services de type LoadBalancer mais ce n’est clairement pas pratique surtout sur le Cloud où les coûts peuvent grimper très vite…

Observabilité limitée
#

Chaque Ingress Controller expose ses propres métriques dans des formats différents. NGINX aura ses métriques spécifiques, Traefik les siennes, Istio encore d’autres… Difficile d’avoir une vision unifiée du comportement de votre trafic quand vous jonglez entre plusieurs outils !

Là aussi, OpenTelemetry devient le standard côté observabilité et il n’est pas toujours facile de venir brancher l’URL d’un collecteur pour venir récupérer métriques, traces et logs à travers la configuration de l’*Ingress Controller.

La limite d’un routage basique
#

L’Ingress se contente d’un routage relativement simple à base de hostname et path. Mais que faire si vous voulez router en fonction d’un header HTTP spécifique ? Implémenter du canary deployment avec une répartition de trafic 90/10 ? Là encore, retour aux annotations spécifiques…

C’est pourquoi, ces quelques limites ont poussé la communauté Kubernetes à repenser l’exposition des services. C’est ainsi qu’est née la Gateway API.

La Gateway API, kesako ?
#

Origine du projet
#

L’histoire de la Gateway API commence en 2019 avec le SIG Network de Kubernetes.

Le projet, initialement baptisé Service APIs, avait un objectif clair : créer une API moderne, extensible et standardisée pour l’exposition des services mais aussi avec le besoin d’aller plus loin en intégrant aussi le Load Balancing et le Service Mesh. Ce projet se devait de dépasser la fragmentation imposée par l’Ingress du côté des fonctionnalités.

C’est en 2021 que le projet prend le nom de Gateway API et commence à gagner en maturité avec les premières implémentations au sein de quelques outils comme Istio.

Le début de la fin de l’Ingress ?
#

Pas du tout ! Il n’est pas prévu pour autant de déprécier les objets Ingress et les Ingress Controllers. Comme l’indique ce lien, la Gateway API se veut comme une évolution, pas un remplacement.

De mon point de vue, il faudra attendre une certaine maturité du projet pour répondre à 100% des besoins qu’occupe la méthode classique d’exposition des services dans Kubernetes. À ce moment-là, les deux n’auront plus de raison d’exister côte à côte.

À noter que certains projets ont déjà pris les devants, c’est le cas de l’ingress-nginx qui ne proposera plus d’évolution pour ce dernier. Il sera néanmoins toujours maintenu sur une période de deux ans pour faciliter la migration vers InGate qui implémentera la Gateway API.

Concepts de base
#

Différents objets pour différentes responsabilités
#

La Gateway API introduit une approche complètement différente de l’Ingress avec plusieurs objets spécialisés qui sont complémentaires les uns des autres.

L’une des innovations majeures de la Gateway API est sa séparation claire des responsabilités à travers ces derniers :

  • GatewayClass : Déployée et configurée par le Cloud provider ou l’outil qui va implémenter la Gateway API. Cet objet définit le type de gateway disponible avec ses capacités et limitations.

Sur Google Cloud par exemple, il existe différentes GatewayClass qui permet de sélectionner le Load Balancer qui sera utilisé de manière sous-jacente.

  • Gateway : Instanciée lors de la configuration du cluster par une équipe plutôt Ops, en se basant sur une GatewayClass existante. Elle définit les listeners (ports, protocoles, certificats TLS) et la configuration pour attacher les routes à celle-ci.

  • HTTPRoute, GRPCRoute, TCPRoute, etc. : Créées par les équipes de développement pour exposer leurs services vers l’extérieur en fonction du type de protocole. Elles définissent les règles de routage, les backends et d’autres fonctionnalités sans se soucier de l’infrastructure définie plus haut.

Cette séparation offre la possibilité à chaque équipe de se concentrer sur son domaine d’expertise sans altérer la sécurité d’exposition des applications vers l’extérieur.

Un besoin ? Un objet Kubernetes !
#

Contrairement à l’Ingress qui permet d’exposer uniquement du HTTP et HTTPS, c’est-à-dire la couche 7 du modèle OSI, la Gateway API propose un objet spécialisé pour chaque protocole en fonction des besoins :

  • HTTPRoute : Routage HTTP classique avec support avancé des headers, query parameters, méthodes, etc. ;
  • GRPCRoute : Routage spécialement conçu pour gRPC avec support des services et méthodes ;
  • TCPRoute : Routage de niveau TCP sur la couche 4 pour les applications non-HTTP ;
  • UDPRoute : Support des applications UDP ;
  • TLSRoute : Il permet de router du trafic basé sur des métadonnées TLS, notamment le SNI (Server Name Indication).

Multi-gateways me voilà !
#

L’un des atouts de la Gateway API c’est sa flexibilité pour gérer plusieurs points d’entrée. Une même HTTPRoute peut très bien être attachée à plusieurs Gateways :

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: multi-gateways
spec:
  parentRefs:
  - kind: Gateway
    name: internal-gateway # Gateway interne
  - kind: Gateway
    name: external-gateway # Gateway externe
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /api
    backendRefs:
    - name: api-service
      port: 8080

Cette approche met en avant qu’il est possible de déployer la même application sur plusieurs environnements ou réseaux sans dupliquer la configuration. Pratique pour les architectures Kubernetes hybrides ou multi-cloud.

La sécurité au cœur du projet
#

La Gateway API a été conçue sans oublier d’ajouter une couche de sécurité pour configurer les routes que l’on peut associer ou non à la Gateway.

Par défaut, une HTTPRoute ne peut se lier qu’à une Gateway du même namespace. Pour autoriser des liaisons cross-namespace, il est nécessaire de l’autoriser explicitement dans la Gateway :

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: shared-gateway
spec:
  listeners:
  - name: http
    port: 80
    protocol: HTTP
    allowedRoutes:
      namespaces:
        from: Selector
        selector:
          matchLabels:
            gateway-access: "allowed"

Dans ce matchLabels, il est possible d’ajouter plusieurs labels. Ces labels doivent être présents sur les namespaces où se trouvent les différentes routes pour que ces dernières soient rattachées à la Gateway.

Pour esquiver ce comportement, il est possible d’autoriser tous les namespaces par défaut :

[...]
allowedRoutes:
  namespaces:
    from: All

Autre point, quand une HTTPRoute veut pointer vers un service dans un autre namespace, elle doit obtenir l’autorisation via un objet appelé ReferenceGrant :

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: blog
  namespace: external-exposure
spec:
  rules:
  - matches:
    - path: /
    backendRefs:
      - name: blog
        namespace: blog
---
apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
  name: blog-access
  namespace: blog
spec:
  from:
  - group: gateway.networking.k8s.io
    kind: HTTPRoute
    namespace: external-exposure
  to:
  - group: ""
    kind: Service

Cela peut être pratique si on souhaite centraliser les HTTPRoute plutôt que de les faire vivre dans le namespace de l’application.

Même logique pour les certificats TLS ! Si votre Gateway veut utiliser un Secret stocké dans un autre namespace (par exemple géré par cert-manager), il faut là aussi un ReferenceGrant :

apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
  name: allow-tls-secret-access
  namespace: cert-manager
spec:
  from:
  - group: gateway.networking.k8s.io
    kind: Gateway
    namespace: gateway-system
  to:
  - group: ""
    kind: Secret
    name: wildcard-tls

Cette approche évite les accès non autorisés tout en conservant la flexibilité nécessaire pour les organisations plus complexes. Cela permet un contrôle plus fin entre les objets sans devoir tout gérer au sein d’un même namespace.

Du Load Balancing intégré
#

L’une des fonctionnalités proposées par la Gateway API est sa gestion native du Load Balancing en proposant notamment la répartition de trafic entre plusieurs backends. Car oui ! il est possible de spécifier plusieurs backends pour une règle définie.

La Gateway API permet de distribuer le trafic entre plusieurs services avec des poids différents, idéal pour les déploiements canary ou les tests A/B :

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: canary-deployment
spec:
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /api
    backendRefs:
    - name: api-v1
      port: 8080
      weight: 90  # 90% du trafic vers la v1
    - name: api-v2
      port: 8080
      weight: 10  # 10% du trafic vers la v2

Et ce n’est pas tout !
#

Vous l’aurez compris, cette API est riche, très riche. Il serait donc difficile de lister l’ensemble des fonctionnalités offertes.

Cependant, les filtres pour réaliser des redirections sont pris en charge, tout comme la possibilité de modifier des headers, sans oublier la possibilité de faire du mirroring de trafic

Les forces de la Gateway API
#

Pour résumer ce qui précède, voici quelques arguments en faveur de l’utilisation de la Gateway API :

La portabilité avant tout
#

Finie l’époque des annotations ! La Gateway API définit un standard commun. Vos HTTPRoutes fonctionnent aussi bien avec Istio qu’avec NGINX ou Traefik.

Une multitude de possibilités
#

Routage par headers, canary deployment avec répartition de trafic, support de TCP/UDP/gRPC… L’ensemble des fonctionnalités plébiscitées par la communauté sont maintenant couvertes ou le seront dans des versions futures.

Sécurité by design
#

Séparation des rôles claire, ReferenceGrant pour les accès cross-namespace, contrôle fin des permissions. Tout est fait pour autoriser au compte-gouttes les accès entre les composants de la Gateway API.

Adoption massive
#

Istio, NGINX, Traefik, Kong, mais aussi Google Cloud, AWS et Azure en proposant leurs propres GatewayClass. La Gateway API se veut de plus en plus courante, il manque encore aux charts Helm communautaires de l’adopter massivement au profit du traditionnel Ingress.

Ce que la version 1.3 permet de faire…
#

La version 1.3 publiée en avril 2024 consolide la Gateway API avec un écosystème de plus en plus mature. Certains concepts sont toujours en mode Experimental, et d’autres progressent en Standard.

Pour faire bref, le Standard Channel regroupe les fonctionnalités stables, prêtes pour de la production tandis que l’Experimental Channel inclut les nouvelles fonctionnalités en cours de développement qui peuvent encore évoluer. Par exemple certaines APIs peuvent avoir des changements cassants ce qui pousse à utiliser ces fonctionnalités avec précaution.

Cette version 1.3 introduit quelques fonctionnalités intéressantes : le mirroring de requêtes par pourcentage qui permet de dupliquer une fraction du trafic vers un autre backend, les filtres CORS pour configurer le partage de ressources inter-origines, et les XListenerSets pour décorréler les listeners et la Gateway. À noter aussi les retry budgets pour limiter intelligemment les tentatives de requêtes et éviter la surcharge.

Pour suivre l’implémentation de chaque version, le projet a mis en place une page pour suivre l’adoption des fonctionnalités. On remarque que Istio et Cilium sont les plus avancés avec un support assez complet. Les autres progressent dans l’adoption de ce standard de jour en jour.

Pour finir, la liste des propositions d’amélioration peut être retrouvée ici au sein du Gateway Enhancement Proposal. Cela permet de tracer les propositions avortées, à l’étude ou encore en cours d’implémentation si vous vous sentez l’âme d’un contributeur !

Quelques mots pour conclure
#

La Gateway API représente l’évolution naturelle de l’exposition de services dans Kubernetes. Au revoir les annotations, place à un standard unifié et extensible !

De plus, le projet progresse rapidement avec l’investissement des grands acteurs de l’écosystème Kubernetes et de la Cloud Native Computing Foundation.

Prenez donc le temps d’étudier et de déployer ce concept qui offre de plus en plus de possibilités à travers les versions, pour au final l’adopter un jour dans des environnements allant jusqu’à la production !

Articles connexes

Un peu de Cilium dans votre cluster Kubernetes ?