Routage HTTP avec Envoy Gateway en pratique : host, path, header et répartition du trafic
Les deux premiers articles vous ont donné la base "qu'est-ce que c'est". Celui-ci entre dans ce que vous modifierez réellement au quotidien : HTTPRoute.
Voyez HTTPRoute comme un script de routage pour le trafic entrant, sauf qu'au lieu d'écrire des règles Nginx ou de mémoriser des annotations de contrôleur, vous utilisez des ressources natives Kubernetes.
Une chose à clarifier tout de suite : cet article ne couvre que HTTPRoute, parce qu'il gère la sémantique HTTP/HTTPS. Pour gRPC, vous vous orienterez vers GRPCRoute ; pour du TCP brut, TCPRoute ; pour le TLS passthrough, TLSRoute. Ne forcez pas tout dans HTTPRoute : le YAML commence alors à dégager une forte impression de bricolage.
Le routage le plus courant : host et path
Voici l'exemple le plus typique :
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: app-route
spec:
parentRefs:
- name: eg
hostnames:
- "app.example.com"
rules:
- matches:
- path:
type: PathPrefix
value: /api
backendRefs:
- name: api-service
port: 8080Ce que cela signifie :
- Seul
Host: app.example.comcorrespondra à cette route - Le chemin doit commencer par
/api - Le trafic est envoyé vers
api-service:8080
Si vous retirez hostnames, la route devient "basée uniquement sur le chemin, quel que soit le domaine".
Ce que matches peut faire correspondre
matches est l'un des concepts les plus centraux dans HTTPRoute.
Pensez-y comme à "quelles conditions déclenchent cette règle".
Conditions de matching courantes :
pathheadersqueryParamsmethod
Exemple :
matches:
- method: GET
path:
type: PathPrefix
value: /api
headers:
- name: version
value: v2Cela signifie :
- Seulement les requêtes
GET - Le chemin commence par
/api - La requête doit inclure le header
version: v2
Ce pattern est idéal pour tester une nouvelle version d'API ou pour déployer progressivement auprès d'utilisateurs ciblés.
Une route, plusieurs règles
Un même HTTPRoute peut router différents chemins vers différents services :
rules:
- matches:
- path:
type: PathPrefix
value: /api
backendRefs:
- name: api-service
port: 8080
- matches:
- path:
type: PathPrefix
value: /admin
backendRefs:
- name: admin-service
port: 8080C'est parfait pour découper plusieurs sous-chemins sous un même domaine, par exemple :
/apiva vers le service API/adminva vers le backend d'administration
La logique de routage devient immédiatement visible. Contrairement à certains vieux fichiers de configuration où, arrivé à la moitié, vous commencez à douter de votre propre existence.
Header, méthode et query comme conditions
HTTPRoute ne se limite pas au matching de chemin, il peut aussi faire du matching sur les headers, les méthodes et les query params.
Par exemple, envoyer uniquement les requêtes avec version: v2 vers le nouveau service :
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: versioned-route
spec:
parentRefs:
- name: eg
rules:
- matches:
- path:
type: PathPrefix
value: /api
headers:
- name: version
value: v2
backendRefs:
- name: api-v2
port: 8080
- matches:
- path:
type: PathPrefix
value: /api
backendRefs:
- name: api-v1
port: 8080Ce pattern fonctionne bien pour :
- Les déploiements canary
- Permettre à certains clients d'essayer plus tôt les nouvelles versions
- La validation manuelle de nouvelles fonctionnalités
Comparé au basculement vers un tout nouveau domaine, le routage par header est plus fin et plus simple à expérimenter.
filters : traiter les requêtes avant le forwarding
HTTPRoute ne fait pas seulement "matcher puis transférer" : il peut aussi appliquer des transformations avant ou après le forwarding. C'est à cela que servent les filters.
Un cas d'usage courant consiste à ajouter des headers :
rules:
- filters:
- type: RequestHeaderModifier
requestHeaderModifier:
add:
- name: x-env
value: prod
backendRefs:
- name: api-service
port: 8080C'est utile pour :
- Injecter des métadonnées de traçage avant le forwarding vers le backend
- Ajouter des headers fixes au niveau de l'entrée
- Faire des transformations de requête/réponse
Cela dit, les filters sont puissants, mais ils ne vous donnent pas carte blanche pour transformer la couche d'entrée en énorme blob de middleware. Trop de logique empilée sur les routes et le débogage finit par donner l'impression d'avoir besoin de sa propre séance de thérapie.
Répartition pondérée du trafic : le pattern canary le plus courant
Pour envoyer 90 % du trafic vers l'ancienne version et 10 % vers la nouvelle, utilisez weight :
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: canary-route
spec:
parentRefs:
- name: eg
hostnames:
- "app.example.com"
rules:
- backendRefs:
- name: api-v1
port: 8080
weight: 90
- name: api-v2
port: 8080
weight: 10Il s'agit d'un canary basique / d'une répartition de trafic.
Vous n'avez pas besoin d'un service mesh complet juste pour mener une expérience de trafic. Pour beaucoup de services API, cela suffit largement, et c'est bien plus propre à configurer.
💡
weightest relatif, il n'a pas besoin de totaliser 100.9et1expriment le même ratio que90et10.
timeouts : ne laissez pas les requêtes pendre pour toujours
HTTPRoute prend en charge la configuration de timeout au niveau de la règle. C'est important : si la couche d'entrée n'impose aucune limite, les mauvaises requêtes restent là indéfiniment comme un client impoli qui refuse de partir.
rules:
- matches:
- path:
type: PathPrefix
value: /reports
timeouts:
request: 10s
backendRequest: 2s
backendRefs:
- name: report-service
port: 8080Comprendre les deux champs :
request: temps d'attente total maximal pour la requête du clientbackendRequest: temps d'attente maximal pour une requête unique vers le backend
Notez que backendRequest ne devrait pas dépasser request : la logique reviendrait à dire "le trajet entier dure 10 minutes, mais un seul segment de bus peut attendre 20 minutes". L'univers se mettrait à buguer.
Deux nuances courantes
1. parentRefs peut cibler un listener précis
Si un Gateway possède plusieurs listeners, vous pouvez vous lier à un seul :
parentRefs:
- name: eg
sectionName: httpCela signifie que la route ne s'attache qu'au listener http.
Dans les configurations multi-listeners, c'est bien plus prévisible que de s'attacher à tout.
2. Pas de matches = match sur tout
Si une règle n'a pas de matches, elle matche par défaut sur / — en pratique, c'est un catch-all.
C'est pratique, mais cela peut avaler silencieusement ce que vous pensiez être des règles plus spécifiques. Ne l'omettez pas par paresse pour ensuite passer une heure à vous demander pourquoi vos règles précises ne se déclenchent pas.
Vérifier que le routage fonctionne
Après avoir configuré les routes, validez avec curl :
curl -H "Host: app.example.com" http://$GATEWAY_HOST/api/users
curl -H "Host: app.example.com" -H "version: v2" http://$GATEWAY_HOST/api/usersPour vérifier le status du HTTPRoute :
kubectl get httproute app-route -o yamlPortez particulièrement attention à :
status.parents- La condition
Accepted
Parfois, le YAML semble correct, mais le Gateway l'a rejeté. C'est comme envoyer une candidature et supposer que vous êtes embauché parce que personne ne vous a répondu.
Résumé en une ligne
Le coeur de HTTPRoute, c'est "on fait d'abord le matching, puis on transfère". Une fois que vous maîtrisez host, path, header et weight, vous couvrez environ 80 % des besoins HTTP d'entrée au quotidien.
Étape suivante
Le prochain article aborde quelque chose que vous rencontrerez dans chaque environnement de production : HTTPS, certificats, terminaison TLS et sécurité : 👉 TLS et sécurité