Chez Scaleway, chaque Service de type LoadBalancer fait naître un vrai répartiteur facturé 0,023 €/h, qu'aucun de vos manifestes ne nomme. Cette leçon installe un contrôleur Ingress sur Kapsule et compte les Load Balancers réellement créés à chaque étape, au lieu de répéter l'argument d'architecture habituel. Vous verrez qu'un Ingress à deux routes n'en consomme qu'un seul là où deux Services en consomment deux de plus, et vous prouverez le routage par chemin avec un contrôle négatif, parce que deux tentatives naïves ne prouvaient rien.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Installer un contrôleur Ingress et compter ce qu'il fait naître côté Scaleway.
- Chiffrer l'écart entre un Ingress et N Services de type
LoadBalancer. - Prouver le routage par chemin, contrôle négatif à l'appui.
- Supprimer dans l'ordre qui ne laisse aucun répartiteur orphelin.
Prérequis
Section intitulée « Prérequis »- Un cluster Kapsule avec au moins deux nœuds : voir créer un cluster Kapsule.
kubectlconfiguré sur ce cluster,jqet la CLIscw2.62.0.- De quoi émettre des requêtes HTTP,
curlsuffit.
Ingress ou Service LoadBalancer : lequel pour quel besoin ?
Section intitulée « Ingress ou Service LoadBalancer : lequel pour quel besoin ? »Le critère n'est pas technique, il est budgétaire et opérationnel. Les deux approches exposent un service ; elles ne facturent pas la même chose et ne s'administrent pas au même endroit.
| Critère | Service type=LoadBalancer | Ingress |
|---|---|---|
| Répartiteurs facturés | un par Service | un pour tous |
| Niveau de routage | par service et port | par chemin, nom d'hôte et en-tête |
| Terminaison TLS | possible au Load Balancer, par les annotations scw-loadbalancer-protocol-http et scw-loadbalancer-certificate-ids | centralisée au contrôleur, un seul point pour toutes les applications |
| Quand le choisir | un protocole non HTTP, ou un service unique | plusieurs services HTTP derrière une même entrée |
L'Ingress n'est pas toujours le bon choix. Un service qui parle PostgreSQL, SMTP, ou tout protocole TCP ou UDP qui n'est pas du HTTP n'a rien à faire derrière un contrôleur HTTP : le Service de type LoadBalancer est alors la réponse juste, et son coût est le prix de la fonction. gRPC n'entre pas dans cette catégorie : il repose sur HTTP/2 et se route très bien par un contrôleur Ingress qui le prend en charge.
Et la terminaison TLS n'est pas un argument en faveur de l'Ingress. Le cloud controller de Scaleway sait faire terminer TLS par le Load Balancer lui-même, au moyen des annotations scw-loadbalancer-protocol-http, scw-loadbalancer-certificate-ids et scw-loadbalancer-http-backend-tls. Le vrai argument de l'Ingress est la centralisation : un seul point de terminaison pour N applications, au lieu de N certificats à renouveler indépendamment. Inversement, dès que deux applications web cohabitent, chaque Service supplémentaire ajoute un répartiteur, une adresse à documenter et un certificat à gérer.
Combien de Load Balancers chaque approche fait-elle naître ?
Section intitulée « Combien de Load Balancers chaque approche fait-elle naître ? »Comptez-les à chaque étape plutôt que de raisonner sur le principe. Une seule commande donne la vérité facturée :
scw lb lb list zone=fr-par-1 -o json | jq -r 'length'-
Deux applications exposées en
ClusterIP, le point de départ.Fenêtre de terminal for app in boutique blog; dokubectl create deployment "${app}" \--image=nginx:1.29.1@sha256:8adbdcb969e2676478ee2c7ad333956f0c8e0e4c5a7463f4611d7a2e7a7ff5dckubectl expose deployment "${app}" --port=80 --target-port=80 --type=ClusterIPdonescw lb lb list zone=fr-par-1 -o json | jq -r 'length'La sortie doit afficher
0.ClusterIPne facture rien : le service n'existe qu'à l'intérieur du cluster. -
Installer le contrôleur Ingress, avec l'annotation qui fera remonter l'adresse du client.
Fenêtre de terminal helm repo add traefik https://traefik.github.io/charts && helm repo updatehelm install traefik traefik/traefik --version 41.5.0 \--namespace traefik --create-namespace \--set-string 'service.annotations.service\.beta\.kubernetes\.io/scw-loadbalancer-proxy-protocol-v2=true' \--set-string 'service.annotations.service\.beta\.kubernetes\.io/scw-loadbalancer-use-hostname=true' \--set 'ports.web.proxyProtocol.trustedIPs={0.0.0.0/0}' \--set 'ports.web.forwardedHeaders.trustedIPs={0.0.0.0/0}' \--wait --timeout 6mLa sortie doit finir par
Install complete. Mesuré le 2026-09-12 : 23 secondes, pour Traefik 3.7.13. -
Compter de nouveau.
Fenêtre de terminal scw lb lb list zone=fr-par-1 -o json | jq -r '.[] | "\(.name[0:36]) type \(.type)"'La sortie doit afficher une seule ligne, de type
lb-s. Le contrôleur expose son propreServicede typeLoadBalancer: c'est lui, et lui seul, qui crée un répartiteur.
Un Ingress à plusieurs routes coûte-t-il plus cher ?
Section intitulée « Un Ingress à plusieurs routes coûte-t-il plus cher ? »Non, et c'est tout l'intérêt. Ajoutez un Ingress qui route vers les deux applications, puis recomptez.
cat <<'YAML' | kubectl apply -f -apiVersion: networking.k8s.io/v1kind: Ingressmetadata: name: vitrinespec: ingressClassName: traefik rules: - http: paths: - path: /boutique pathType: Prefix backend: service: name: boutique port: number: 80 - path: /blog pathType: Prefix backend: service: name: blog port: number: 80YAML
scw lb lb list zone=fr-par-1 -o json | jq -r 'length'La sortie doit toujours afficher 1. L'Ingress réutilise le répartiteur du contrôleur : ajouter des routes ne crée aucune ressource facturée.
Comparez maintenant avec l'autre architecture :
kubectl patch svc boutique -p '{"spec":{"type":"LoadBalancer"}}'kubectl patch svc blog -p '{"spec":{"type":"LoadBalancer"}}'sleep 20scw lb lb list zone=fr-par-1 -o json | jq -r 'length'La sortie doit afficher 3. Mesuré le 2026-09-12 : les deux répartiteurs supplémentaires sont provisionnés en 5 secondes.
| Architecture | Load Balancers facturés | Coût horaire |
|---|---|---|
2 Services ClusterIP | 0 | 0 € |
| Contrôleur Ingress seul | 1 | 0,023 €/h |
| Contrôleur + Ingress à 2 routes | 1 | 0,023 €/h |
2 Services type=LoadBalancer en plus | 3 | 0,069 €/h |
Remettez les deux Services en ClusterIP pour arrêter les frais :
kubectl patch svc boutique -p '{"spec":{"type":"ClusterIP"}}'kubectl patch svc blog -p '{"spec":{"type":"ClusterIP"}}'L'écart de trois à un ne se lit pas seulement sur la facture, et c'est ce qui rend l'argument solide au-delà du prix. Chaque Service de type LoadBalancer apporte sa propre adresse publique, donc son propre enregistrement DNS à créer, son propre certificat TLS à obtenir et à renouveler, et sa propre règle de pare-feu à maintenir. Dix services web exposés ainsi, ce sont dix adresses que quelqu'un doit documenter et dix renouvellements de certificat qui peuvent échouer indépendamment. Derrière un Ingress, il y a une adresse, un point de terminaison TLS et un endroit où lire la configuration de routage de toutes les applications. Le gain d'exploitation dépasse largement les 0,046 €/h économisés dans cet exemple, et il croît avec le nombre de services, là où le gain financier, lui, reste linéaire.
L'Ingress route-t-il vraiment vers la bonne application ?
Section intitulée « L'Ingress route-t-il vraiment vers la bonne application ? »Il faut le prouver, et deux méthodes naïves n'y parviennent pas. Je les donne toutes les deux, parce que l'erreur est instructive.
La première compare les codes HTTP. Sur ce lab, /boutique, /blog et /inconnu renvoyaient tous un 404. Impossible de conclure : soit l'Ingress a routé et c'est le nginx applicatif qui répond 404 faute de fichier à ce chemin, soit il n'a pas routé et c'est son backend par défaut qui répond.
La seconde compare le corps de la réponse, en supposant que le backend par défaut ne ressemblerait pas à un nginx. C'est faux : le backend par défaut d'un contrôleur peut ressembler à celui de vos applications. Avec ingress-nginx, dont le backend par défaut est lui aussi un nginx, la page d'erreur portait exactement la même signature. Le test déclarait le routage correct y compris sur /inconnu, ce qui était absurde.
La méthode qui tranche donne à chaque application un contenu unique, servi exactement au chemin routé :
for app in boutique blog; do pod=$(kubectl get pods -l app="${app}" -o jsonpath='{.items[0].metadata.name}') kubectl exec "${pod}" -- sh -c \ "mkdir -p /usr/share/nginx/html/${app} && echo 'JE-SUIS-${app}' > /usr/share/nginx/html/${app}/index.html"done
ADRESSE=$(kubectl get svc -n traefik traefik \ -o jsonpath='{.status.loadBalancer.ingress[0].ip}{.status.loadBalancer.ingress[0].hostname}')
for chemin in boutique/ blog/ inconnu/; do printf '%-12s %s\n' "/${chemin}" "$(curl -s --max-time 10 "http://${ADRESSE}/${chemin}" | head -1)"doneLa sortie doit afficher trois lignes distinctes :
/boutique/ JE-SUIS-boutique/blog/ JE-SUIS-blog/inconnu/ 404 page not foundDeux corps différents sur deux chemins prouvent le routage ; le troisième est le contrôle négatif. Sans lui, un test qui réussit partout ne démontrerait rien du tout.
Comment obtenir un nom plutôt qu'une adresse IP ?
Section intitulée « Comment obtenir un nom plutôt qu'une adresse IP ? »Par une annotation du cloud controller, sinon vous recevez une IP brute. Le comportement par défaut renseigne status.loadBalancer.ingress[0].ip.
metadata: annotations: service.beta.kubernetes.io/scw-loadbalancer-use-hostname: "true"Préférez le nom. Une adresse IP de Load Balancer est rendue au fournisseur puis réattribuée : un enregistrement DNS qui la cite en dur finit par pointer vers la machine de quelqu'un d'autre. C'est aussi la raison pour laquelle aucune adresse publique réelle ne figure dans les guides de ce site.
Quelle adresse IP votre application voit-elle vraiment ?
Section intitulée « Quelle adresse IP votre application voit-elle vraiment ? »Elle voit le contrôleur Ingress, jamais votre visiteur, et cela reste vrai même avec le PROXY protocol. C'est le défaut classique d'une chaîne à plusieurs sauts, et il casse silencieusement tout ce qui repose sur l'adresse du client : limitation de débit, filtrage géographique, journaux d'audit, détection d'abus.
Le trafic traverse trois sauts, et chacun ne connaît directement que le précédent :
visiteur -> Load Balancer -> contrôleur Traefik -> applicationLe PROXY protocol agit sur le premier saut, en annonçant au contrôleur l'adresse d'origine que le répartiteur connaît. L'en-tête X-Forwarded-For agit sur le dernier, en transmettant cette origine à l'application. Il faut donc les deux, et chacun règle un problème différent.
Trois adresses circulent ainsi, et les confondre est l'erreur courante :
| Adresse | Qui la porte | Ce que l'application en voit |
|---|---|---|
| Adresse du visiteur | le client sur Internet | rien, sauf réglage explicite |
| Adresse privée du Load Balancer | le répartiteur Scaleway | ce que le contrôleur voit par défaut |
| Adresse du pod du contrôleur | Traefik dans le cluster | ce que l'application voit dans remote_addr |
Le PROXY protocol résout le premier saut. C'est un court en-tête que le Load Balancer place devant la connexion TCP pour annoncer l'adresse d'origine. Il se demande par une annotation, et le contrôleur doit être configuré pour lui faire confiance, sans quoi il rejette la connexion. Les deux réglages figurent dans la commande d'installation de cette leçon.
Le mesurer, avec son contrôle négatif
Section intitulée « Le mesurer, avec son contrôle négatif »Une mesure seule ne prouverait qu'une corrélation. Comparez donc les deux états, en lisant les journaux de l'application :
POD=$(kubectl get pods -l app=boutique -o jsonpath='{.items[0].metadata.name}')curl -s -o /dev/null "http://${ADRESSE}/boutique/"sleep 3kubectl logs "${POD}" --tail=1La sortie doit afficher une ligne de journal se terminant par l'adresse transmise dans X-Forwarded-For. Retirez ensuite l'annotation, attendez que le cloud controller reconfigure le répartiteur, et refaites la même lecture :
kubectl annotate svc -n traefik traefik \ 'service.beta.kubernetes.io/scw-loadbalancer-proxy-protocol-v2-'sleep 60curl -s -o /dev/null "http://${ADRESSE}/boutique/"sleep 3kubectl logs "${POD}" --tail=1Mesuré le 2026-09-12, adresses remplacées par les plages de documentation de la RFC 5737 :
remote_addr vu par l'application | X-Forwarded-For | |
|---|---|---|
| Avec l'annotation | 198.51.100.10, le pod Traefik | 203.0.113.42, le visiteur |
| Sans l'annotation | 198.51.100.10, le pod Traefik | 198.51.100.20, le Load Balancer |
Deux enseignements, et le second surprend plus que le premier.
Le PROXY protocol est bien la cause de ce qui remonte : privé de l'annotation, Traefik ne connaît que le répartiteur et propage cette adresse-là, en toute bonne foi. Rien n'échoue, rien n'alerte, et une limitation de débit posée sur cette valeur limiterait votre propre Load Balancer.
Et surtout, remote_addr ne change pas. Dans les deux cas, l'application voit l'adresse du pod Traefik. La vraie adresse du visiteur arrive par un en-tête, jamais par la connexion : votre application doit être configurée pour lire X-Forwarded-For, et pour ne faire confiance à cet en-tête que s'il vient d'un intermédiaire de confiance.
Nettoyer
Section intitulée « Nettoyer »L'ordre est impératif : ce qui crée un répartiteur part avant le cluster. Supprimer le cluster d'abord laisse les Load Balancers orphelins, sans personne pour les réclamer, et leur facturation continue.
-
Supprimer l'Ingress et les charges.
Fenêtre de terminal kubectl delete ingress vitrine --ignore-not-found=truekubectl delete deployment boutique blog --ignore-not-found=truekubectl delete svc boutique blog --ignore-not-found=true -
Supprimer les Services de type
LoadBalancer, et eux seuls.Fenêtre de terminal kubectl get svc --all-namespaces -o json \| jq -r '.items[] | select(.spec.type == "LoadBalancer")| "\(.metadata.namespace) \(.metadata.name)"' \| while read -r ns nom; doecho "suppression de ${ns}/${nom}"kubectl delete svc -n "${ns}" "${nom}"doneSélectionnez sur le type, jamais sur l'emplacement. Une commande qui supprimerait « tous les Services sauf ceux de
default» emporterait aussikube-system/coredns, et casserait la résolution DNS de tout le cluster. Seul unServicede typeLoadBalancerprovisionne une ressource facturée : c'est lui qu'un nettoyage doit viser. -
Attendre la libération effective des répartiteurs.
Fenêtre de terminal until [ "$(scw lb lb list zone=fr-par-1 -o json | jq -r 'length')" = "0" ]; doprintf '.'; sleep 10doneecho " tous les Load Balancers sont libérés"La sortie doit finir par la phrase de confirmation. Ne passez pas à la suite avant ce zéro : la suppression est asynchrone, et « j'ai demandé » n'est pas « c'est fait ».
-
Supprimer le namespace du contrôleur.
Fenêtre de terminal helm uninstall traefik --namespace traefikkubectl delete namespace traefik --ignore-not-found=truescw lb lb list zone=fr-par-1 -o json | jq -r 'length'La dernière sortie doit afficher
0.
Limites, quotas et plafonds
Section intitulée « Limites, quotas et plafonds »| Plafond | Valeur | Source |
|---|---|---|
Prix d'un Load Balancer lb-s | 0,023 €/h | grille réseau officielle, 2026-09-12 |
Bande passante d'un lb-s | 200 Mbit/s | grille réseau officielle, 2026-09-12 |
| Zones sans Load Balancer | fr-par-3 | aide de scw lb lb create, 2026-09-12 |
| Type créé par le cloud controller | lb-s par défaut | scw lb lb list, mesuré le 2026-09-12 |
| Nom des Load Balancers créés | préfixé par l'identifiant du cluster | scw lb lb list, mesuré le 2026-09-12 |
Le préfixe du nom n'est pas un détail cosmétique : c'est lui qui permet à scw k8s cluster delete with-additional-resources=true de reconnaître les répartiteurs à emporter avec le cluster.
Dépannage
Section intitulée « Dépannage »| Symptôme | Cause | Solution |
|---|---|---|
status.loadBalancer reste vide | le cloud controller n'a pas fini de provisionner | attendre ; vérifier kubectl describe svc pour les événements du CCM |
| L'adresse répond alors qu'aucune route ne correspond | c'est le backend par défaut du contrôleur qui répond 404 | ajouter un contrôle négatif à vos tests pour le distinguer |
| Un Load Balancer subsiste après la suppression du cluster | le Service n'a pas été supprimé avant le cluster | le supprimer à la main avec scw lb lb delete |
kubectl get svc affiche une IP, pas un nom | comportement par défaut du cloud controller | annoter avec service.beta.kubernetes.io/scw-loadbalancer-use-hostname |
Antipatterns à éviter
Section intitulée « Antipatterns à éviter »Ces quatre erreurs partagent une racine : elles traitent le répartiteur comme un objet Kubernetes, alors que c'est une ressource facturée chez l'hébergeur.
| Antipattern | Conséquence | Discipline |
|---|---|---|
Un Service de type LoadBalancer par application web | autant de répartiteurs, d'adresses et de certificats que d'applications | un contrôleur Ingress, une adresse, un point de terminaison TLS |
Supprimer le cluster avant les Service | des répartiteurs orphelins continuent d'être facturés | supprimer les Service, attendre le zéro, puis le cluster |
| Nettoyer en ciblant un namespace plutôt qu'un type | kube-system/coredns disparaît, le DNS du cluster tombe | sélectionner sur spec.type == "LoadBalancer" |
| Coder en dur l'adresse IP du répartiteur | l'adresse est recyclée et pointe un jour ailleurs | utiliser le nom, via l'annotation du cloud controller |
L'exposition sous l'angle Well-Architected
Section intitulée « L'exposition sous l'angle Well-Architected »Optimisation des coûts
Section intitulée « Optimisation des coûts »Combien de répartiteurs payez-vous en ce moment, et pourquoi ? La réponse ne se lit pas dans vos manifestes, puisque le cloud controller les crée sans qu'aucun ne les nomme.
Discipline : compter régulièrement avec scw lb lb list, confronter ce nombre à la liste des Service de type LoadBalancer, et rapatrier derrière un Ingress tout ce qui parle HTTP.
Fiabilité
Section intitulée « Fiabilité »Que devient votre trafic si le contrôleur tombe ? Mutualiser une entrée, c'est aussi mutualiser une panne : un contrôleur unique est un point de défaillance pour toutes les applications qu'il sert.
Discipline : exécuter plusieurs répliques du contrôleur, réparties sur des nœuds distincts, et leur poser un PodDisruptionBudget. L'économie d'un répartiteur ne doit pas se payer d'une indisponibilité générale.
Excellence opérationnelle
Section intitulée « Excellence opérationnelle »Vos tests d'exposition distinguent-ils une route correcte d'une réponse d'erreur générique ? Sur ce lab, deux méthodes de vérification concluaient juste pour de mauvaises raisons.
Discipline : donner à chaque destination une réponse identifiable, inclure un contrôle négatif sur un chemin non routé, et se demander avant chaque assertion ce qui différerait si la chose testée ne fonctionnait pas.
Contrôle de connaissances
Section intitulée « Contrôle de connaissances »Vérifiez que l'essentiel de ce guide est acquis. Les questions portent uniquement sur ce qui vient d'être expliqué ici.
Contrôle de connaissances
Validez vos connaissances avec ce quiz interactif
Informations
- Le chronomètre démarre au clic sur Démarrer
- Questions à choix multiples, vrai/faux et réponses courtes
- Vous pouvez naviguer entre les questions
- Les résultats détaillés sont affichés à la fin
Lance le quiz et démarre le chronomètre
Vérification
(0/0)Profil de compétences
Quoi faire maintenant
Ressources pour progresser
Des indices pour retenter votre chance ?
Nouveau quiz complet avec des questions aléatoires
Retravailler uniquement les questions ratées
Retour à la liste des certifications
À retenir
Section intitulée « À retenir »- Un
Servicede typeLoadBalancercrée un répartiteur facturé 0,023 €/h, absent de tous vos manifestes. ClusterIPne facture rien : c'est le point de départ de toute comparaison.- Un contrôleur Ingress crée un seul répartiteur, quel que soit le nombre de routes. Mesuré le 2026-09-12 : 0 avec deux
ClusterIP, 1 avec le contrôleur, 1 encore avec un Ingress à deux routes, 3 dès que deux Services passent enLoadBalancer. - Le cloud controller crée des
lb-s, nommés avec le préfixe de l'identifiant du cluster. - Traefik 3.7.13 est prêt en 23 secondes ;
ingress-nginxest archivé depuis le 2026-03-24 et ne doit plus être enseigné. - Une annotation passée par Helm exige
--set-string: avec--set,truedevient un booléen et la pose échoue. scw-loadbalancer-proxy-protocol-v2fait remonter la vraie adresse du client dansX-Forwarded-For; sans elle, c'est l'adresse privée du Load Balancer qui arrive.- Prouvez le routage avec un contenu unique par destination et un contrôle négatif sur un chemin non routé.
- Nettoyez en sélectionnant sur
spec.type, jamais sur le namespace, sous peine de supprimercoredns. - Les
Servicepartent avant le cluster, sinon les répartiteurs restent orphelins et facturés.
Pour aller plus loin
Section intitulée « Pour aller plus loin »- Monter un cluster de version : Mesure ce que votre exposition encaisse pendant que les nœuds sont remplacés.
Ressources externes
Section intitulée « Ressources externes »- Déployer un contrôleur Ingress sur Kapsule : la procédure officielle, avec les variantes Nginx et Traefik.
- Annotations du Load Balancer Scaleway : la liste complète des annotations reconnues par le cloud controller, dont celle qui renvoie un nom plutôt qu'une adresse.
- Exposer ses services sur Kubernetes : le comparatif officiel entre
Service, Ingress et passerelles. - Tarifs réseau Scaleway : la grille des Load Balancers, du
lb-saulb-gp-xl, avec leurs bandes passantes.