Exposer des Services avec la prise en charge de Gateway API de Cilium
GatewayClass, Gateway et HTTPRoute : routage par nom d'hôte, correspondance de chemins, réécriture d'URL et TLS, sans Pods de passerelle dédiés.
Kubernetes Gateway API est le successeur standardisé d’Ingress, et Cilium l’implémente directement en s’appuyant sur son propre chemin de données eBPF et sur un Envoy embarqué pour le traitement de couche 7. Ce guide présente les ressources principales, GatewayClass, Gateway et HTTPRoute, ainsi que les schémas de routage construits à partir d’elles, en supposant que Cilium est déjà installé avec Gateway API activé (voir le guide précédent).
GatewayClass : déclarer Cilium comme contrôleur disponible
Une GatewayClass indique au cluster « ce contrôleur est disponible pour les Gateways » :
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: cilium
spec:
controllerName: io.cilium/gateway-controller
Si vous avez défini gatewayAPI.gatewayClass.create: false lors de l’installation (comme le recommande le guide précédent), c’est la ressource que vous créez vous-même pour combler ce vide.
Personnaliser la classe avec CiliumGatewayClassConfig
Cilium fournit sa propre CRD, CiliumGatewayClassConfig, pour les paramètres qui doivent s’appliquer à chaque Gateway utilisant une classe donnée, le plus souvent le type de Service Kubernetes par lequel la Gateway est exposée :
apiVersion: cilium.io/v2alpha1
kind: CiliumGatewayClassConfig
metadata:
name: gateway-api-config
namespace: default
spec:
service:
type: NodePort
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: cilium
spec:
controllerName: io.cilium/gateway-controller
parametersRef:
group: cilium.io
kind: CiliumGatewayClassConfig
name: gateway-api-config
namespace: default
NodePort est ici spécifiquement un aménagement pour les clusters kind, expliqué plus bas ; sur un cluster disposant d’un vrai répartiteur de charge cloud, LoadBalancer est le choix le plus courant, et les guides suivants de cette série sur l’annonce de routes BGP expliquent comment obtenir une adresse routable sans répartiteur de charge de fournisseur cloud.
Créer une Gateway
Une Gateway est la configuration effective des listeners, qui référence la classe ci-dessus :
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: demo-gateway
namespace: default
spec:
gatewayClassName: cilium
listeners:
- name: http
protocol: HTTP
port: 80
allowedRoutes:
namespaces:
from: All
- name: https
protocol: HTTPS
port: 443
allowedRoutes:
namespaces:
from: All
tls:
mode: Terminate
certificateRefs:
- name: demo-tls-secret
Pas de Pods de passerelle : c’est là que Cilium diffère
Avec la plupart des implémentations de Gateway API, créer une Gateway produit un Deployment et des Pods dédiés à cette instance de passerelle. Avec Cilium, ce n’est pas le cas : le trafic est intercepté via eBPF au niveau du nœud et routé selon la configuration de la Gateway, et seul un Service Kubernetes apparaît, sans aucun Pod dédié à la Gateway elle-même.
kubectl get gateway demo-gateway
kubectl get service -l gateway.networking.k8s.io/gateway-name=demo-gateway
Sur un cluster kind en particulier, un Service NodePort reçoit par défaut un port aléatoire, pas les ports que vous voulez réellement (30080/30443 de la configuration kind précédente). Corrigez-les explicitement :
kubectl patch service <gateway-service-name> --type='json' -p='[
{"op": "replace", "path": "/spec/ports/0/nodePort", "value": 30080},
{"op": "replace", "path": "/spec/ports/1/nodePort", "value": 30443}
]'
Cette étape de correction est uniquement un contournement propre à kind pour pallier l’absence de vrai répartiteur de charge, pas une limitation générale de Cilium. Sur un cluster disposant d’une vraie implémentation cloud de LoadBalancer, ou avec les annonces L2 de Cilium ou l’annonce de routes BGP (un guide ultérieur de cette série), le Service de la Gateway obtient une adresse routable sans correction manuelle.
Router par nom d’hôte
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: python-route
namespace: default
spec:
parentRefs:
- name: demo-gateway
hostnames:
- "python.example.com"
rules:
- backendRefs:
- name: python-service
port: 5000
parentRefs rattache cette route à la Gateway (elles doivent être dans le même namespace, sinon la route nécessite une politique de référence inter-namespace explicite). Toute requête arrivant avec l’en-tête Host python.example.com est envoyée à python-service.
Router par chemin : correspondance exacte ou par préfixe
rules:
- matches:
- path:
type: Exact
value: /
backendRefs:
- name: python-service
port: 5000
Exact ne correspond qu’à ce chemin littéral ; une requête vers /status ne correspondrait pas à une règle limitée à /. Pour tout ce qui dépasse un unique chemin fixe, PathPrefix correspond au préfixe donné et transmet le reste du chemin inchangé :
rules:
- matches:
- path:
type: PathPrefix
value: /
La distinction compte en pratique : une API avec plusieurs endpoints derrière une même route a besoin de PathPrefix, pas d’Exact, sinon chaque endpoint au-delà du chemin littéral renvoie une erreur de route introuvable au lieu d’atteindre le backend.
Réécriture d’URL : un chemin externe propre, un chemin interne différent
Un schéma courant consiste à exposer plusieurs services backend sous un nom d’hôte unique avec un schéma de chemins ordonné, tandis que chaque backend conserve ses propres routes internes sans connaître ce préfixe externe :
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: python-route
namespace: default
spec:
parentRefs:
- name: demo-gateway
hostnames:
- "example.com"
rules:
- matches:
- path:
type: PathPrefix
value: /api/python
filters:
- type: URLRewrite
urlRewrite:
path:
type: ReplacePrefixMatch
replacePrefixMatch: /
backendRefs:
- name: python-service
port: 5000
Une requête vers example.com/api/python/status correspond au préfixe /api/python, puis est réécrite en /status avant d’atteindre python-service, de sorte que le backend n’a jamais besoin de connaître le préfixe /api/python que voit le monde extérieur. Définissez une route parallèle pour chaque service backend (/api/go vers un service Go, et ainsi de suite) afin de construire une passerelle d’API unifiée à partir de plusieurs backends développés indépendamment, chacun ignorant la structure de chemins que la passerelle présente à l’extérieur.
TLS : deux éléments doivent concorder
Servir du trafic HTTPS nécessite deux éléments configurés de manière cohérente :
- Le listener HTTPS de la Gateway (montré plus haut) référence un Secret TLS et définit
mode: Terminate. - Chaque
HTTPRoutequi doit accepter du trafic HTTPS doit avoir une entréeparentRefsqui cible réellement ce listener HTTPS par son nom, et pas seulement la Gateway dans son ensemble :
parentRefs:
- name: demo-gateway
sectionName: https
Sans sectionName pointant spécifiquement vers le listener HTTPS, une route s’attache par défaut au(x) listener(s) que ses critères de correspondance permettent, ce qui peut ne pas inclure celui que vous visiez. Créez le Secret TLS avant d’appliquer une Gateway qui le référence :
kubectl create secret tls demo-tls-secret \
--cert=path/to/cert.pem \
--key=path/to/key.pem \
-n default
kubectl describe gateway demo-gateway est le premier endroit à consulter si un listener n’accepte pas le trafic comme prévu ; un Secret TLS manquant ou mal formé est une raison fréquente pour laquelle un listener apparaît comme non accepté.
Guides populaires
-
Comment Cilium utilise eBPF : identité, politique et sockets Ce qui se passe réellement dans le noyau quand un Pod démarre, communique avec un autre Pod ou fait l'objet d'une décision de politique sur un paquet, et pourquoi c'est rapide. -
Préparer un cluster pour Cilium : désactiver d'abord le CNI par défaut Cilium remplace entièrement le réseau de votre cluster, ce qui rend l'ordre des opérations plus important qu'avec la plupart des add-ons. -
Écrire des politiques réseau : des règles L3/L4 aux règles L7 Kubernetes part du principe que chaque Pod peut joindre tous les autres Pods ; CiliumNetworkPolicy permet de restreindre cela, jusqu'à la méthode HTTP si nécessaire.