Ingress et Gateway API#
Vos applications tournent dans des Pods, exposées par des Services. Mais comment le trafic HTTP/HTTPS provenant d’Internet arrive-t-il jusqu’à elles ? C’est le rôle de l”Ingress et de la Gateway API, les deux mécanismes de Kubernetes pour exposer des services vers l’extérieur du cluster.
Le problème : exposer des services HTTP#
Sans Ingress, pour exposer une application web en HTTPS, vous devriez :
Créer un Service de type
LoadBalancerpour chaque application (un load balancer cloud par service = coûteux)Gérer les certificats TLS dans chaque application ou Service
Gérer le routage par host/path manuellement
L”Ingress centralise tout ça : un seul point d’entrée, des règles de routage déclaratives, et la terminaison TLS.
La ressource Ingress#
Un objet Ingress déclare les règles de routage HTTP : quel host et quel path doivent être redirigés vers quel Service.
# ingress-exemple.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: mon-ingress
namespace: production
annotations:
# Annotations spécifiques au contrôleur Nginx
nginx.ingress.kubernetes.io/rewrite-target: /
nginx.ingress.kubernetes.io/proxy-body-size: "10m"
nginx.ingress.kubernetes.io/rate-limit: "100" # Requêtes/seconde
spec:
# Classe d'Ingress Controller à utiliser
ingressClassName: nginx
# Configuration TLS
tls:
- hosts:
- api.exemple.com
- exemple.com
secretName: tls-certificate # Secret contenant le certificat
# Règles de routage
rules:
# Routage par host
- host: api.exemple.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api-svc
port:
number: 8080
- host: exemple.com
http:
paths:
# Routage par path sur le même host
- path: /app
pathType: Prefix
backend:
service:
name: web-svc
port:
number: 3000
- path: /api/v2
pathType: Prefix
backend:
service:
name: api-v2-svc
port:
number: 8081
# Catch-all : si aucun path ne correspond
- path: /
pathType: Prefix
backend:
service:
name: default-svc
port:
number: 80
Types de pathType#
Exact: correspondance exacte (/apine correspond pas à/api/v2)Prefix: correspondance par préfixe (/apicorrespond à/api/v2,/api/users…)ImplementationSpecific: comportement défini par le contrôleur
L’Ingress Controller : le composant qui fait le travail#
La ressource Ingress est juste un objet de configuration. Elle ne fait rien toute seule. Il faut déployer un Ingress Controller dans le cluster — c’est lui qui lit les ressources Ingress et configure réellement le proxy HTTP.
# Installer le contrôleur Nginx Ingress via Helm
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
helm install ingress-nginx ingress-nginx/ingress-nginx \
--namespace ingress-nginx \
--create-namespace \
--set controller.replicaCount=2
# Vérifier l'installation
kubectl get pods -n ingress-nginx
kubectl get svc -n ingress-nginx
# service/ingress-nginx-controller LoadBalancer 10.0.0.100 203.0.113.1 80:31080/TCP,443:31443/TCP
Ingress Controllers populaires :
Contrôleur |
Points forts |
|---|---|
Nginx Ingress |
Stable, très répandu, documentation riche |
Traefik |
Découverte automatique, dashboard intégré, Let’s Encrypt natif |
HAProxy |
Hautes performances, configurations avancées |
Contour |
Basé sur Envoy, gateway API ready |
Kong |
API Gateway features (auth, rate limiting, plugins) |
Annotations Nginx Ingress courantes#
annotations:
# Réécriture de l'URL (ex: /api/v1/users → /users)
nginx.ingress.kubernetes.io/rewrite-target: /$2
# Authentification basique
nginx.ingress.kubernetes.io/auth-type: basic
nginx.ingress.kubernetes.io/auth-secret: basic-auth-secret
# Rate limiting
nginx.ingress.kubernetes.io/limit-rps: "10"
# CORS
nginx.ingress.kubernetes.io/enable-cors: "true"
nginx.ingress.kubernetes.io/cors-allow-origin: "https://mon-frontend.com"
# Timeout
nginx.ingress.kubernetes.io/proxy-read-timeout: "60"
nginx.ingress.kubernetes.io/proxy-send-timeout: "60"
# Redirect HTTP → HTTPS
nginx.ingress.kubernetes.io/ssl-redirect: "true"
# Taille max du body (upload)
nginx.ingress.kubernetes.io/proxy-body-size: "50m"
TLS avec Ingress et cert-manager#
Secret TLS manuel#
# Créer un Secret TLS à partir de fichiers certificat
kubectl create secret tls tls-certificate \
--cert=certificat.crt \
--key=cle-privee.key \
--namespace production
cert-manager : automatisation des certificats#
cert-manager est un contrôleur Kubernetes qui automatise la gestion des certificats TLS, notamment avec Let’s Encrypt (certificats gratuits et renouvelés automatiquement).
# Installer cert-manager
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/latest/download/cert-manager.yaml
# Vérifier
kubectl get pods -n cert-manager
# cluster-issuer-letsencrypt.yaml
# Émetteur de certificats Let's Encrypt (production)
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: admin@exemple.com # Email pour les notifications d'expiration
privateKeySecretRef:
name: letsencrypt-prod-key
solvers:
- http01:
ingress:
class: nginx # Utilise le challenge HTTP-01 via Ingress
# Ingress avec cert-manager (renouvellement automatique)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: mon-ingress-tls
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod" # Clé magique !
spec:
ingressClassName: nginx
tls:
- hosts:
- exemple.com
secretName: exemple-com-tls # cert-manager crée ce Secret automatiquement
rules:
- host: exemple.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web-svc
port:
number: 80
Cycle de vie d’un certificat avec cert-manager
cert-manager détecte l’annotation
cert-manager.io/cluster-issuerIl crée un objet
CertificateRequestet contacte Let’s EncryptLet’s Encrypt vérifie que vous contrôlez le domaine (challenge HTTP-01 ou DNS-01)
cert-manager reçoit le certificat et le stocke dans le Secret indiqué
cert-manager surveille l’expiration (30 jours avant) et renouvelle automatiquement
Gateway API : la nouvelle génération#
La Gateway API est la successeure de l’Ingress. Elle résout plusieurs limitations de l’Ingress :
L’Ingress est trop couplé à Nginx (annotations spécifiques au contrôleur)
Pas de support natif pour le routage TCP/UDP
Séparation des responsabilités floue (infrastructure vs application)
La Gateway API introduit trois ressources distinctes avec des responsabilités claires :
Ressource |
Qui la gère |
Rôle |
|---|---|---|
|
Admin infrastructure |
Définit quel contrôleur utiliser (Nginx, Envoy…) |
|
Admin cluster |
Configure le point d’entrée (ports, TLS, protocoles) |
|
Développeur |
Définit les règles de routage de l’application |
# gatewayclass.yaml (géré par l'admin infra)
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: nginx-gateway
spec:
controllerName: k8s.nginx.org/nginx-gateway-controller
# gateway.yaml (géré par l'admin cluster)
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: passerelle-prod
namespace: infra
spec:
gatewayClassName: nginx-gateway
listeners:
- name: https
protocol: HTTPS
port: 443
tls:
mode: Terminate
certificateRefs:
- name: tls-wildcard-cert
allowedRoutes:
namespaces:
from: Selector
selector:
matchLabels:
gateway: autorisee # Seuls les namespaces avec ce label peuvent y accéder
# httproute.yaml (géré par le développeur)
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: route-api
namespace: production
spec:
parentRefs:
- name: passerelle-prod
namespace: infra
hostnames:
- "api.exemple.com"
rules:
- matches:
- path:
type: PathPrefix
value: /v2
backendRefs:
- name: api-v2-svc
port: 8080
weight: 90 # 90% du trafic (canary deployment !)
- name: api-v3-canary-svc
port: 8080
weight: 10 # 10% vers la nouvelle version
- matches:
- headers:
- name: "x-beta-user"
value: "true" # Routage par header (feature flag)
backendRefs:
- name: api-beta-svc
port: 8080
Simulation Python : routeur Ingress#
from dataclasses import dataclass, field
from typing import List, Optional, Tuple
import re
@dataclass
class RegleIngress:
"""Représente une règle de routage Ingress."""
host: str
path: str
path_type: str # Exact, Prefix
service_nom: str
service_port: int
priorite: int = 0 # Plus bas = plus prioritaire
def correspond(self, host: str, path: str) -> bool:
"""Vérifie si une requête correspond à cette règle."""
# Vérification du host (wildcards supportés : *.exemple.com)
if self.host.startswith("*."):
domaine = self.host[2:]
if not (host.endswith("." + domaine) or host == domaine):
return False
elif self.host != "*" and self.host != host:
return False
# Vérification du path
if self.path_type == "Exact":
return path == self.path
elif self.path_type == "Prefix":
return path == self.path or path.startswith(self.path.rstrip("/") + "/")
return False
@dataclass
class RouterIngress:
"""Simulation d'un routeur Ingress (comme nginx-ingress-controller)."""
nom: str
regles: List[RegleIngress] = field(default_factory=list)
def ajouter_regle(self, regle: RegleIngress):
self.regles.append(regle)
# Trier par priorité puis par longueur de path (plus long = plus spécifique)
self.regles.sort(key=lambda r: (r.priorite, -len(r.path)))
def router(self, host: str, path: str) -> Optional[Tuple[str, int, str]]:
"""
Route une requête vers le service approprié.
Retourne (service_nom, service_port, regle_matchée) ou None.
"""
for regle in self.regles:
if regle.correspond(host, path):
return regle.service_nom, regle.service_port, f"{regle.host}{regle.path}"
return None
def tester(self, requetes: List[Tuple[str, str]]):
"""Teste une liste de requêtes et affiche le résultat."""
print(f"\nRouteur Ingress : {self.nom}")
print(f"{'Requête':<45} {'Résultat'}")
print("-" * 75)
for host, path in requetes:
resultat = self.router(host, path)
url = f"{host}{path}"
if resultat:
svc, port, regle = resultat
print(f" {url:<43} → {svc}:{port} (via règle '{regle}')")
else:
print(f" {url:<43} → ✗ 404 Not Found (aucune règle ne correspond)")
# Construire un routeur Ingress de démonstration
routeur = RouterIngress("ingress-production")
# Ajouter les règles (de la plus spécifique à la moins spécifique)
routeur.ajouter_regle(RegleIngress("api.exemple.com", "/v2/users", "Exact", "api-v2-svc", 8081))
routeur.ajouter_regle(RegleIngress("api.exemple.com", "/v2", "Prefix", "api-v2-svc", 8081))
routeur.ajouter_regle(RegleIngress("api.exemple.com", "/", "Prefix", "api-v1-svc", 8080))
routeur.ajouter_regle(RegleIngress("exemple.com", "/app", "Prefix", "web-svc", 3000))
routeur.ajouter_regle(RegleIngress("exemple.com", "/admin", "Prefix", "admin-svc", 9000))
routeur.ajouter_regle(RegleIngress("exemple.com", "/", "Prefix", "homepage-svc", 80))
routeur.ajouter_regle(RegleIngress("*.exemple.com", "/health", "Exact", "healthcheck-svc", 8888))
# Tests
requetes_test = [
("api.exemple.com", "/v2/users"), # Exact match
("api.exemple.com", "/v2/orders/123"), # Prefix /v2
("api.exemple.com", "/v1/legacy"), # Prefix /
("exemple.com", "/app/dashboard"), # Prefix /app
("exemple.com", "/admin/users"), # Prefix /admin
("exemple.com", "/"), # Catch-all
("blog.exemple.com", "/health"), # Wildcard host
("autre-domaine.io", "/api"), # Pas de règle
("exemple.com", "/inexistant/page"), # Path inexistant → catch-all /
]
routeur.tester(requetes_test)
Routeur Ingress : ingress-production
Requête Résultat
---------------------------------------------------------------------------
api.exemple.com/v2/users → api-v2-svc:8081 (via règle 'api.exemple.com/v2/users')
api.exemple.com/v2/orders/123 → api-v2-svc:8081 (via règle 'api.exemple.com/v2')
api.exemple.com/v1/legacy → api-v1-svc:8080 (via règle 'api.exemple.com/')
exemple.com/app/dashboard → web-svc:3000 (via règle 'exemple.com/app')
exemple.com/admin/users → admin-svc:9000 (via règle 'exemple.com/admin')
exemple.com/ → homepage-svc:80 (via règle 'exemple.com/')
blog.exemple.com/health → healthcheck-svc:8888 (via règle '*.exemple.com/health')
autre-domaine.io/api → ✗ 404 Not Found (aucune règle ne correspond)
exemple.com/inexistant/page → homepage-svc:80 (via règle 'exemple.com/')
Ingress vs Gateway API : tableau comparatif#
Points clés à retenir#
L”Ingress centralise l’exposition HTTP/HTTPS : un seul point d’entrée pour plusieurs applications
L”Ingress Controller (Nginx, Traefik…) est le composant qui lit les ressources Ingress et fait réellement le routage — il faut l’installer séparément
Les règles de routage combinent host et path : la règle la plus spécifique (path le plus long) est appliquée en premier
cert-manager automatise la gestion des certificats TLS avec Let’s Encrypt (renouvellement automatique)
La Gateway API est la nouvelle génération : elle sépare les responsabilités (infra / cluster / dev) et supporte nativement le routage TCP/UDP, les poids de trafic (canary), et le routage par header
Pour les nouveaux projets, préférer la Gateway API ; l’Ingress reste pertinent pour les projets existants