Aller au contenu

Gateway API en production

300 Concevoir ⏱ 1 h 30 kubernetesgateway-apitraefik

À la fin, vous saurez

  • Répartir la configuration d'entrée entre fournisseur d'infrastructure, opérateur du cluster et équipe applicative
  • Écrire une Gateway à plusieurs listeners dont les namespaces autorisés sont restreints par sélecteur
  • Autoriser une référence entre namespaces avec une ReferenceGrant, et expliquer pourquoi elle appartient à la cible
  • Composer des correspondances, des filtres et une répartition pondérée dans une HTTPRoute
  • Distinguer ce qui est dans le canal standard de Gateway API de ce qui reste expérimental
  • Lire les conditions Accepted, Programmed et ResolvedRefs pour localiser une panne de routage
  • Planifier la migration d'Ingress vers Gateway API avec ingress2gateway

Prérequis

Testé avec gateway-api 1.6.2 ingress2gateway 1.2.0 kubernetes 1.36 traefik 3.7 traefik-helm-chart 41.6.1 , vérifié le 5 octobre 2026

Pourquoi

Le cours précédent a exposé Signalements avec une Gateway et une HTTPRoute dans le namespace de l'application (Déployer Signalements). Pour un seul service, cela suffit. Pour un cluster qui héberge les applications de plusieurs équipes ou de plusieurs clients, trois questions se posent aussitôt.

Qui possède quoi ? Qui décide qu'un port 443 est ouvert, qu'un certificat est servi, qu'un domaine est réservé à telle équipe ? Et qui, au contraire, décide qu'/api/v2 part vers la nouvelle version ? Avec Ingress, ces deux niveaux de décision vivent dans le même objet, et les droits RBAC ne savent pas les séparer.

Comment éviter qu'une équipe en dérange une autre ? Si deux équipes déclarent la même route sur le même nom d'hôte, laquelle l'emporte ? Si une équipe envoie du trafic vers le Service d'une autre, qui l'a autorisé ?

Comment déployer progressivement ? Envoyer 10 % du trafic vers une nouvelle version, réécrire un chemin, rediriger une ancienne adresse : Ingress ne sait pas le dire, chaque contrôleur invente donc ses annotations, et la configuration cesse d'être portable.

Gateway API répond à ces trois questions par construction : des ressources séparées par rôle, des règles d'attachement explicites entre elles, et un vocabulaire standard pour le routage. Cette leçon la fait fonctionner comme un opérateur de cluster la fait fonctionner, avec Traefik comme implémentation, sur le modèle de ce que Lyneko exploite sur Kapsule.

Les concepts

Trois rôles, trois ressources

La documentation de Gateway API décrit trois profils. Ils n'ont pas besoin d'être trois équipes : sur un petit cluster, ce sont trois casquettes d'une même personne. Mais le modèle permet de les séparer sans changer d'objets.

RôleRessourcePossède
Fournisseur d'infrastructureGatewayClassle contrôleur et ses capacités (Traefik, Cilium, Envoy Gateway, un répartiteur de cloud)
Opérateur du clusterGatewayl'entrée : adresse, ports, protocoles, noms d'hôte, certificats, namespaces autorisés
Développeur d'applicationHTTPRoute, GRPCRoute...les règles de routage vers ses Services

Le lien entre les niveaux est déclaré des deux côtés. La Gateway désigne sa classe (gatewayClassName) ; la route désigne sa Gateway (parentRefs) ; et la Gateway dit quelles routes elle accepte (allowedRoutes). Tant que ces trois déclarations ne concordent pas, rien n'est programmé. Les droits RBAC se calquent sur ces objets : l'équipe applicative reçoit create sur les HTTPRoute de son namespace, l'équipe plateforme seule sur les Gateway.

Note

Une GatewayClass a pour contrôleur un nom d'identifiant (controllerName). Celui de Traefik est traefik.io/gateway-controller. Le contrôleur écrit dans le statut de la classe qu'il l'a acceptée ; sans cette condition Accepted, aucune Gateway de cette classe ne sera servie.

La Gateway et ses listeners

Une Gateway décrit un ou plusieurs listeners, des points d'écoute. Chaque listener a un nom, un port, un protocole (HTTP, HTTPS, TLS, TCP, UDP), un nom d'hôte facultatif et des règles sur les routes qui peuvent s'y attacher. Les champs que présente le schéma 1.6.2 d'un listener sont : name, port, protocol, hostname, allowedRoutes (avec namespaces et kinds) et tls (avec mode, certificateRefs, options).

Pourquoi plusieurs listeners dans une même Gateway ? Parce qu'ils correspondent à des publics différents. Un listener pour les applications des équipes, un autre pour les outils de la plateforme (tableau de bord, métriques), chacun avec son nom d'hôte et ses propres namespaces autorisés. Deux listeners sur le même port sont possibles s'ils se distinguent par leur nom d'hôte : le contrôleur choisit celui qui correspond.

allowedRoutes : qui peut s'attacher

C'est le contrôle d'accès de l'entrée. allowedRoutes.namespaces.from prend trois valeurs :

  • Same (la valeur par défaut) : seules les routes du namespace de la Gateway ;
  • All : les routes de tous les namespaces (rarement une bonne idée) ;
  • Selector : les routes des namespaces dont les étiquettes correspondent à un sélecteur.

allowedRoutes.kinds restreint de surcroît les types de routes acceptés (par exemple HTTPRoute seulement). Une route qui désigne une Gateway sans y être autorisée reçoit une condition Accepted: False avec la raison NotAllowedByListeners, et n'est pas servie.

ReferenceGrant : autoriser ce qui franchit un namespace

Une HTTPRoute envoie normalement vers des Services de son namespace. Qu'elle désigne un Service d'un autre namespace est une opération sensible : l'équipe du namespace A ferait recevoir du trafic à l'équipe B, éventuellement un trafic qu'elle n'a pas demandé. Gateway API refuse donc les références croisées par défaut, et exige une ressource ReferenceGrant créée dans le namespace de la cible.

apiVersion: gateway.networking.k8s.io/v1
kind: ReferenceGrant
metadata:
  name: routes-de-la-plateforme
  namespace: signalements        # le namespace de la cible
spec:
  from:
    - group: gateway.networking.k8s.io
      kind: HTTPRoute
      namespace: passerelle      # qui a le droit de référencer
  to:
    - group: ""
      kind: Service

Lisez-la comme une phrase : « les HTTPRoute du namespace passerelle peuvent référencer les Services de ce namespace ». Le principe est celui d'une permission donnée par le propriétaire de la ressource, pas prise par le demandeur : sans quoi n'importe qui pourrait s'attribuer l'accès. Les mêmes grants servent aux Gateways qui référencent un Secret de certificat dans un autre namespace (leçon 7). Notez que le champ spec est obligatoire dans le schéma depuis la version 1.6.

Les correspondances

Une règle d'HTTPRoute contient des matches (quand elle s'applique) et des backendRefs (où envoyer). Quatre critères sont possibles dans une correspondance : path, headers, queryParams et method. À l'intérieur d'une correspondance, tous les critères se combinent en ET ; entre plusieurs correspondances d'une même règle, c'est un OU.

Pour le chemin, type vaut Exact, PathPrefix (le défaut), ou RegularExpression : ce dernier est de support spécifique à l'implémentation, donc moins portable. Un PathPrefix: /api correspond à /api et /api/signalements, mais pas à /apiary : la comparaison se fait par éléments de chemin.

Quand plusieurs règles correspondent, la spécification fixe un ordre de priorité : la correspondance exacte du chemin d'abord, puis le préfixe le plus long, puis la méthode, puis le plus grand nombre d'en-têtes, puis le plus grand nombre de paramètres de requête. Si deux règles de routes différentes restent à égalité, la plus ancienne (date de création) gagne. Ce dernier critère mérite qu'on s'en souvienne : deux équipes qui déclarent la même route ne produisent pas une erreur, l'une des deux est simplement ignorée.

Les filtres

Un filtre modifie la requête ou la réponse. Les types que contient le schéma 1.6.2 sont : RequestHeaderModifier, ResponseHeaderModifier, RequestMirror, RequestRedirect, URLRewrite, CORS, ExternalAuth et ExtensionRef (le point d'extension propre à une implémentation). Les filtres se posent sur une règle, ou sur une backendRef pour ne toucher qu'un des destinataires.

Les modificateurs d'en-têtes sont Core ; redirections, réécritures et miroir sont Extended (portables mais facultatifs). Le miroir copie les requêtes vers un second destinataire dont la réponse est ignorée : on éprouve une nouvelle version avec du trafic réel sans impact visible.

La répartition pondérée

Chaque backendRef porte un weight (1 par défaut). La part de trafic d'un destinataire est son poids divisé par la somme des poids de la règle. Avec 90 et 10, 10 % des requêtes vont au second ; avec 9 et 1, c'est la même chose. Un poids de 0 désactive le destinataire sans le retirer : c'est utile pour préparer un déploiement. La répartition porte sur des requêtes, pas sur des clients : le même navigateur peut tomber alternativement sur les deux versions, à moins d'activer la persistance de session (expérimentale).

Timeouts et retries

La règle d'une HTTPRoute accepte un champ timeouts avec deux durées : request (temps total depuis la requête reçue jusqu'à la fin de la réponse) et backendRequest (temps d'une requête individuelle vers le destinataire). Ce champ est dans le canal standard depuis la version 1.2. Une valeur 0s désactive le délai ; backendRequest ne doit pas dépasser request.

Le champ retry (attempts, backoff, codes) n'est lui pas dans le canal standard : une comparaison des schémas de la version 1.6.2 le montre, il n'existe que dans le canal expérimental, avec sessionPersistence. La conséquence est pratique : pour l'utiliser, il faut installer le fichier experimental-install.yaml, avec le risque de rupture d'une API expérimentale, et vérifier que votre contrôleur le prend en charge. Règle de prudence : ne construisez aucune dépendance critique sur un champ expérimental.

Les autres routes

L'API ne se limite pas à HTTP. GRPCRoute route le gRPC par service et méthode. TLSRoute et ListenerSet sont passés dans le canal standard à la version 1.5, TCPRoute et UDPRoute à la version 1.6 (notes de version). Pour Traefik, la documentation fait pourtant passer TCPRoute et TLSRoute par l'option experimentalChannel : vérifiez la page de votre contrôleur avant de vous appuyer sur l'une d'elles.

Le statut, ou comment l'API parle

Chaque objet reçoit du contrôleur un statut, sous forme de conditions (type, état, raison, message). Trois sont à connaître :

  • Accepted : la configuration est valide et acceptée par le contrôleur. Sur une route, elle est par parent (chaque Gateway désignée) ;
  • Programmed : la configuration est traduite en fonctionnement réel, une adresse existe. Sur une Gateway seulement ;
  • ResolvedRefs : toutes les références ont été trouvées et sont permises (Services, Secrets). Sur les listeners et les routes.

Un False s'accompagne d'une raison normalisée : NotAllowedByListeners, NoMatchingListenerHostname, BackendNotFound, RefNotPermitted, InvalidCertificateRef... C'est la grande amélioration par rapport à Ingress : la cause d'un refus est dans l'API, avec un vocabulaire commun à tous les contrôleurs.

En pratique

Les manifestes ci-dessous ont été validés comme du YAML et vérifiés champ par champ contre les schémas de la version 1.6.2. Les commandes qui demandent un cluster sont expliquées, et leur résultat est décrit sans être reproduit.

1. Installer les CRD et Traefik

Les définitions de ressources, canal standard, version prise en charge par Traefik 3.7 :

$ kubectl apply --server-side \
    -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.6.2/standard-install.yaml

Puis Traefik, avec un fichier de valeurs plutôt que des --set, qui se relit et se versionne :

# traefik-values.yaml (chart 41.6.1)
providers:
  kubernetesGateway:
    enabled: true        # active le fournisseur Gateway API et la GatewayClass « traefik »
gateway:
  enabled: false         # pas de Gateway par défaut : c'est l'opérateur qui écrit la sienne
$ helm install traefik traefik/traefik --version 41.6.1 \
    --namespace traefik --create-namespace -f traefik-values.yaml
$ kubectl get gatewayclass

Dans le chart, ports.web écoute sur 8000 dans le pod (exposé sur 80 par le Service), et ports.websecure sur 8443 (exposé sur 443). Une Gateway ne peut utiliser que des ports déclarés dans la section ports du chart : le commentaire du fichier de valeurs le dit pour chaque listener. Si votre listener est sur un port inconnu, la Gateway est refusée.

2. La Gateway de la plateforme

L'opérateur du cluster crée, dans un namespace dédié, la Gateway partagée.

apiVersion: v1
kind: Namespace
metadata:
  name: passerelle
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: entree
  namespace: passerelle
spec:
  gatewayClassName: traefik
  listeners:
    - name: apps
      protocol: HTTP
      port: 8000                       # point d'entrée « web » de Traefik
      hostname: "*.apps.example.com"
      allowedRoutes:
        kinds:
          - kind: HTTPRoute
        namespaces:
          from: Selector
          selector:
            matchLabels:
              acces-passerelle: apps
    - name: plateforme
      protocol: HTTP
      port: 8000
      hostname: "plateforme.example.com"
      allowedRoutes:
        namespaces:
          from: Same               # seules les routes de l'équipe plateforme

Les deux listeners partagent le port 8000 et se distinguent par leur nom d'hôte. Le premier est ouvert aux namespaces qui portent l'étiquette acces-passerelle: apps ; le second est réservé au namespace de la Gateway. On donne alors l'étiquette au namespace de l'application :

$ kubectl label namespace signalements acces-passerelle=apps
$ kubectl get gateway entree -n passerelle

La seconde commande doit afficher la classe traefik, une adresse (celle du répartiteur de charge Scaleway sur Kapsule) et PROGRAMMED à True.

3. La route de l'application

L'équipe de Signalements écrit sa route, dans son namespace :

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: signalements
  namespace: signalements
spec:
  parentRefs:
    - name: entree
      namespace: passerelle
      sectionName: apps
  hostnames:
    - signalements.apps.example.com
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /
      backendRefs:
        - name: signalements
          port: 80

parentRefs désigne la Gateway et son namespace (par défaut, c'est celui de la route), et sectionName le listener visé. Le nom d'hôte de la route doit être compatible avec celui du listener : signalements.apps.example.com correspond à *.apps.example.com. Un nom qui ne correspondrait à aucun listener donne Accepted: False, raison NoMatchingListenerHostname.

4. Un déploiement canari

On a déployé une version 1.3.0 de l'API sous un second Deployment, derrière un second Service signalements-canari (même port, sélecteur sur la nouvelle version). Les deux Services reçoivent le trafic dans les proportions de la règle :

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: signalements
  namespace: signalements
spec:
  parentRefs:
    - name: entree
      namespace: passerelle
      sectionName: apps
  hostnames:
    - signalements.apps.example.com
  rules:
    # 1. Les équipes internes forcent la nouvelle version avec un en-tête.
    - matches:
        - headers:
            - name: X-Version
              value: canari
      backendRefs:
        - name: signalements-canari
          port: 80
    # 2. Tous les autres : 95 % / 5 %.
    - matches:
        - path:
            type: PathPrefix
            value: /
      backendRefs:
        - name: signalements
          port: 80
          weight: 95
        - name: signalements-canari
          port: 80
          weight: 5

La première règle utilise une correspondance d'en-tête : une requête portant X-Version: canari va toujours au canari, ce qui permet de l'éprouver à la main. La seconde envoie 5 % du reste. Pour augmenter la part, on change les poids (95/5, puis 75/25, 50/50, 0/100) en les commitant, avec Argo CD comme pour le reste. Un poids à 0 pour l'ancienne version la garde prête pour un retour arrière en un commit.

Warning

La répartition est par requête et sans affinité. Si la nouvelle version change le format d'une réponse que le client enchaîne avec une autre requête, un même utilisateur verra les deux versions en alternance. Le canari suppose des versions compatibles entre elles, ce que la leçon sur la mise à jour du cours précédent exigeait déjà.

5. Filtres : redirection, réécriture, en-têtes

Trois cas typiques, dans une même route :

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: signalements-compat
  namespace: signalements
spec:
  parentRefs:
    - name: entree
      namespace: passerelle
      sectionName: apps
  hostnames:
    - signalements.apps.example.com
  rules:
    # L'ancienne adresse /v1/... est redirigée définitivement vers /api/...
    - matches:
        - path:
            type: PathPrefix
            value: /v1
      filters:
        - type: RequestRedirect
          requestRedirect:
            path:
              type: ReplacePrefixMatch
              replacePrefixMatch: /api
            statusCode: 301
    # Le préfixe public /api est retiré avant d'atteindre l'application.
    - matches:
        - path:
            type: PathPrefix
            value: /api
      filters:
        - type: URLRewrite
          urlRewrite:
            path:
              type: ReplacePrefixMatch
              replacePrefixMatch: /
        - type: RequestHeaderModifier
          requestHeaderModifier:
            add:
              - name: X-Entree
                value: passerelle
        - type: ResponseHeaderModifier
          responseHeaderModifier:
            set:
              - name: Cache-Control
                value: no-store
      backendRefs:
        - name: signalements
          port: 80

La redirection répond au client (code 301, nouvelle adresse) : il refait la requête. La réécriture, elle, est invisible : l'application reçoit /sante alors que le client a appelé /api/sante. Le filtre d'en-têtes de requête propose add, set et remove ; celui de réponse en fait de même sur ce que reçoit le client.

6. Une route de la plateforme vers un Service de l'application

Admettons que l'équipe plateforme veuille exposer, depuis son namespace, une page de supervision de Signalements. Sa route référence un Service d'un autre namespace :

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: supervision-signalements
  namespace: passerelle
spec:
  parentRefs:
    - name: entree
      sectionName: plateforme
  hostnames:
    - plateforme.example.com
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /signalements
      backendRefs:
        - name: signalements
          namespace: signalements    # référence entre namespaces
          port: 80

Tant que la ReferenceGrant de la section « Les concepts » n'existe pas dans le namespace signalements, la route est acceptée (Accepted: True) mais sa condition ResolvedRefs est à False, raison RefNotPermitted. Créez-la, et la condition passe à True sans autre action : le contrôleur réagit à la ReferenceGrant.

7. Timeouts

Pour la route d'export de Signalements, longue par nature, et le reste :

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: signalements-delais
  namespace: signalements
spec:
  parentRefs:
    - name: entree
      namespace: passerelle
      sectionName: apps
  hostnames:
    - signalements.apps.example.com
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /api/export
      timeouts:
        request: 60s
        backendRequest: 60s
      backendRefs:
        - name: signalements
          port: 80
    - matches:
        - path:
            type: PathPrefix
            value: /api
      timeouts:
        request: 10s
        backendRequest: 5s
      backendRefs:
        - name: signalements
          port: 80

Le délai fait partie du canal standard, mais comme toute fonction Extended il reste facultatif pour un contrôleur : sa documentation indique ce qu'il en fait. Le retry, lui, s'écrit ainsi dans le canal expérimental (CRD expérimentales requises) :

# Canal expérimental seulement
      retry:
        attempts: 2
        codes: [502, 503]
        backoff: 100ms

8. Lire le statut

$ kubectl get gateway,httproute -A
$ kubectl describe httproute signalements -n signalements
$ kubectl get gateway entree -n passerelle -o jsonpath='{range .status.listeners[*]}{.name}{"\t"}{range .conditions[*]}{.type}={.status}({.reason}) {end}{"\n"}{end}'

La première vue d'ensemble. La deuxième affiche, dans la partie Status > Parents, une entrée par Gateway visée avec ses conditions Accepted et ResolvedRefs. La troisième extrait, listener par listener, les conditions de la Gateway : chacun doit afficher Accepted=True, Programmed=True, ResolvedRefs=True. Un listener qui échoue le dit lui-même, sans qu'une autre le masque.

9. Migrer depuis Ingress

L'outil ingress2gateway du projet Kubernetes lit des objets Ingress (et, selon le fournisseur, les ressources propres au contrôleur) et émet des ressources Gateway API. Sa dernière version (1.2.0) prend en charge, d'après son README, plusieurs fournisseurs, dont ingress-nginx et traefik.

$ ingress2gateway print --providers=ingress-nginx -n signalements > gateway-api.yaml

La commande lit le namespace indiqué dans le cluster courant et écrit les manifestes sur la sortie standard, avec des avertissements pour les annotations qu'elle n'a pas pu traduire. Relisez la sortie comme un brouillon, jamais comme un résultat : les annotations sans équivalent portable (certaines règles de limitation de débit, des extraits de configuration) sont à reprendre à la main, avec un filtre ExtensionRef ou un objet propre au contrôleur. Une migration sans surprise se fait en parallèle : on déploie les routes Gateway API sur un nom d'hôte de test, on compare, puis on bascule le DNS.

Sous le capot

Le contrôleur est un client de l'API. Traefik, comme les autres implémentations, ne lit pas des fichiers : il ouvre des watches sur les GatewayClass, Gateways, routes, ReferenceGrants, Services, EndpointSlices et Secrets, et recalcule sa configuration à chaque événement. L'état « programmé » veut dire : la configuration est chargée dans le proxy. La table de routage vit en mémoire de chaque pod Traefik, ce qui explique qu'un redémarrage de Traefik la reconstruise en quelques secondes depuis l'API.

Pourquoi la ReferenceGrant est dans la cible. Une route est créée par un acteur du namespace A ; un Service est la propriété d'un acteur du namespace B. Si l'autorisation vivait côté route, A s'autoriserait lui-même. Dans la cible, la permission suppose un droit d'écriture dans le namespace de B, que A n'a pas. C'est la même logique que le RBAC : on accorde de l'accès à d'autres, on ne se l'attribue pas.

Trafic direct aux pods. Comme le rappelait la leçon 11 du cours précédent, Traefik envoie le trafic à l'adresse des pods, tirée des EndpointSlices, sans passer par l'adresse virtuelle du Service (kube-proxy et ses alternatives). Votre répartition pondérée s'applique donc requête par requête, dans Traefik. Le Service n'est ici qu'une liste de pods et un nom stable.

Pièges courants

Les ports de la Gateway ne sont pas ceux du Service Traefik. Le listener s'écrit en 8000 ou 8443 (les ports du pod Traefik), alors que les clients se connectent aux ports 80 et 443 du Service. Écrire port: 80 donne une Gateway refusée par Traefik : les ports d'un listener doivent correspondre aux points d'entrée déclarés dans le chart.

Accepted: True ne veut pas dire « ça marche ». Une route peut être acceptée et pourtant ne rien servir, parce que ResolvedRefs est à False (Service absent, référence non permise). Lisez toujours les deux.

Une CRD absente ou trop ancienne. Installer Traefik avant les CRD ou avec des CRD d'une version que le contrôleur ne gère pas produit des erreurs de watch dans ses journaux, pas dans le statut des objets. Le contrôle est : kubectl get crd | grep gateway.networking.k8s.io après chaque mise à jour de Gateway API ou du contrôleur.

Mélanger les canaux. Les notes de version 1.5 signalent qu'une règle d'admission (la safe-upgrades ValidatingAdmissionPolicy) empêche d'installer les CRD du canal expérimental par-dessus celles du canal standard. Choisissez un canal par cluster, et gardez la même version dans les environnements.

Sécurité

  • Les droits RBAC reflètent les rôles. Donnez aux équipes applicatives create, update et delete sur les HTTPRoute de leur namespace seulement. Les Gateways, GatewayClasses et ReferenceGrants entre namespaces restent à l'équipe plateforme. Une équipe qui peut créer une ReferenceGrant dans son propre namespace n'ouvre que ses Services, ce qui est voulu.
  • Les étiquettes de namespace sont un contrôle d'accès. Avec from: Selector, la personne qui peut poser l'étiquette acces-passerelle: apps sur un namespace lui donne l'entrée. Réservez cette permission à l'équipe plateforme, ou utilisez une politique d'admission qui l'interdit.
  • Réduisez allowedRoutes. from: All sur un listener de production est l'équivalent de « n'importe qui peut publier sur ce domaine ». Préférez Selector, avec un nom d'hôte précis par listener.
  • Limitez les types de route. Une Gateway HTTP n'a pas à accepter les routes TCP ou TLS d'équipes applicatives.
  • Les filtres modifient des en-têtes de confiance. Si l'application s'appuie sur X-Forwarded-For ou sur un en-tête d'identité posé par la passerelle, vérifiez qu'un client ne peut pas les poser lui-même : supprimez-les avec un filtre RequestHeaderModifier à l'entrée avant de les remplacer.
  • Contrôler ce qui est dans le canal expérimental. Un champ expérimental est un contrat qui peut changer : un contrôleur qui l'ignore silencieusement rend un contrôle de sécurité (par exemple un retry sur une méthode non idempotente) inopérant.

En production

  • Une Gateway par périmètre de confiance, pas une par application : une pour les applications internes, une pour les clients, chacune avec son répartiteur de charge Scaleway (donc son coût et son adresse). Plusieurs équipes se partagent une Gateway par les listeners et les sélecteurs.
  • Haute disponibilité de Traefik. Au moins deux répliques, un PodDisruptionBudget et une répartition sur plusieurs nœuds, comme toute charge dont dépend le trafic entrant.
  • Le canari se pilote par Git. Les poids se changent par commit ; l'automatisation (Argo Rollouts, Flagger) sait piloter les poids d'une HTTPRoute pour promouvoir ou annuler selon des métriques. C'est le sujet du cours GitOps.
  • Versions. Mettez à jour les CRD avant le contrôleur, et lisez les notes de version de Gateway API : la 1.5 et la 1.6 ont déplacé TLSRoute, TCPRoute et UDPRoute vers le canal standard et supprimé d'anciennes versions d'API. Testez sur un cluster de pré-production.
  • Migration d'Ingress. Gateway API et Ingress cohabitent : Traefik sait servir les deux. On migre route par route, et on retire l'Ingress seulement quand le trafic a basculé. La page de Kubernetes sur Gateway API précise que l'API Ingress est gelée.
  • Surveiller le statut. Une alerte sur Programmed != True (Gateways) et sur Accepted ou ResolvedRefs différents de True (routes) repère les erreurs avant les utilisateurs.

Exercices

Exercice 1 : deux listeners, deux publics

Une équipe facturation demande à publier facturation.apps.example.com. Son namespace n'a pas l'étiquette acces-passerelle. Qu'observez-vous sur sa HTTPRoute, quelle commande l'indique, et que faites-vous pour l'autoriser sans ouvrir from: All ?

Solution

La route est refusée pour ce parent : kubectl describe httproute -n facturation affiche dans Status > Parents la condition Accepted: False avec la raison NotAllowedByListeners (le nom d'hôte, lui, est compatible). On lui donne accès en étiquetant son namespace : kubectl label namespace facturation acces-passerelle=apps. Dans un environnement géré par GitOps, l'étiquette est dans le manifeste du Namespace, relu par l'équipe plateforme : c'est précisément le contrôle voulu. La route passe à Accepted: True sans autre action.

Exercice 2 : le canari à 20 %

Écrivez la règle qui envoie 20 % du trafic vers signalements-canari et 80 % vers signalements, avec un poids exprimé en nombres entiers les plus petits possibles. Que se passe-t-il si vous mettez 80 et 20 ? Et 8 et 2 ?

Solution
backendRefs:
  - name: signalements
    port: 80
    weight: 4
  - name: signalements-canari
    port: 80
    weight: 1

La part de chacun est son poids divisé par la somme : 4/5 et 1/5. Les poids 80 et 20, ou 8 et 2, donnent exactement la même répartition, seule la proportion compte. Écrire 80/20 est plus lisible pour un pourcentage ; 4/1 est plus compact. L'essentiel est de n'utiliser que des poids positifs et de garder une somme non nulle.

Exercice 3 : référence entre namespaces

La route supervision-signalements du namespace passerelle est Accepted: True, mais les requêtes reçoivent une erreur. Quelle condition regardez-vous, quelle valeur attendez-vous, et quel manifeste corrige le problème ? Dans quel namespace le créez-vous ?

Solution

On regarde la condition ResolvedRefs du parent dans kubectl describe httproute supervision-signalements -n passerelle : elle est à False avec la raison RefNotPermitted. La ReferenceGrant de la section « Les concepts » règle le problème. Elle se crée dans le namespace de la cible, signalements, avec from désignant les HTTPRoute du namespace passerelle et to désignant les Services. La créer dans passerelle n'aurait aucun effet : l'autorisation doit venir du propriétaire du Service.

Récapitulatif

  • Gateway API sépare GatewayClass (fournisseur), Gateway (opérateur) et routes (développeur), liées par des déclarations des deux côtés.
  • Une Gateway a des listeners (port, protocole, nom d'hôte) ; allowedRoutes (Same, All, Selector) contrôle qui peut s'y attacher.
  • Une référence vers un autre namespace exige une ReferenceGrant dans le namespace de la cible.
  • Une règle de HTTPRoute combine matches (ET dans une correspondance, OU entre elles), filters et backendRefs pondérés ; la répartition par weight permet le canari, par requête.
  • Dans le canal standard : timeouts, GRPCRoute, ReferenceGrant, ListenerSet, TLSRoute, TCPRoute et UDPRoute ; expérimental : retry et la persistance de session.
  • Le statut dit pourquoi : Accepted, Programmed, ResolvedRefs, avec une raison normalisée.
  • Traefik : les ports des listeners sont ceux de ses points d'entrée (8000, 8443), pas les ports publics.
  • ingress2gateway produit un brouillon de migration à relire.

Pour aller plus loin

  • La suite logique : TLS et cert-manager, qui ajoute le listener HTTPS et ses certificats.
  • Pour limiter ce que la passerelle peut atteindre en aval, Les NetworkPolicies.
  • Pour la panne « Gateway non programmée faute de ReferenceGrant » en situation, Diagnostiquer le réseau d'un cluster.
  • La documentation de Gateway API, en particulier ses pages sur les rôles, les GEP et la matrice d'implémentations et leur niveau de conformité.
  • Les notes de version de Gateway API (1.2, 1.4, 1.5, 1.6) pour suivre les graduations vers le canal standard.
  • Le cours Sécurité de Kubernetes pour les droits RBAC sur ces objets.
  • Glossaire : Gateway API, HTTPRoute, Ingress.
Voir ma constellation →

Sources