Monter un cluster Kapsule de version prend un peu plus de sept minutes et coûte deux requêtes perdues sur 249. Cette leçon monte un cluster de 1.36.4 à 1.37.0 pendant qu'une application reçoit du trafic en continu, et mesure chaque étape. Vous verrez que le control plane et les nœuds se montent séparément, que le pool remplace ses machines au lieu de les mettre à jour, et que le PodDisruptionBudget cadence l'opération plutôt que de la bloquer. Vous saurez aussi écrire une attente qui ne déclare pas la montée terminée trop tôt.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Monter le control plane puis les nœuds, en sachant pourquoi c'est séparé.
- Mesurer la disponibilité réelle de votre application pendant l'opération.
- Reconnaître le remplacement roulant, et ce qu'il implique pour la capacité.
- Écrire une attente qui ne se trompe pas de critère.
Prérequis
Section intitulée « Prérequis »- Un cluster Kapsule sur une version antérieure à la dernière disponible.
kubectl, la CLIscw2.62.0,jqetcurl.- La lecture de créer un cluster Kapsule, dont ce lab prolonge le cluster.
Jusqu'où peut-on monter, et quand faut-il le faire ?
Section intitulée « Jusqu'où peut-on monter, et quand faut-il le faire ? »On ne saute pas une version mineure. La cible doit être un correctif de la version courante, ou la mineure immédiatement suivante. Passer de 1.35 à 1.37 exige donc deux montées successives.
CLUSTER_ID=$(scw k8s cluster list region=fr-par -o json | jq -r '.[0].id')
scw k8s cluster get "${CLUSTER_ID}" region=fr-par -o json \ | jq -r '"cluster : \(.version) (\(.status))"'
scw k8s version list region=fr-par -o json \ | jq -r '.[] | "\(.name) dépréciée \(.deprecated_at[0:10]) fin \(.end_of_life_at[0:10])"'La première sortie doit afficher votre version et ready, la seconde une ligne par version encore proposée, avec ses deux échéances.
Les dates décident du calendrier à votre place. Scaleway supporte chaque mineure 14 mois, puis monte lui-même les clusters restants vers la mineure suivante, dans les 30 jours, en prévenant par ticket. Choisir de ne rien faire, c'est donc choisir que la montée se produise à un moment que vous ne maîtrisez pas.
Tout monter d'un coup, ou en deux temps ?
Section intitulée « Tout monter d'un coup, ou en deux temps ? »L'argument upgrade-pools=true enchaîne le control plane et les nœuds. Lisez ce tableau par la colonne du milieu : elle dit dans quelle situation chaque stratégie est la bonne.
| Stratégie | Quand la choisir | Ce que vous acceptez |
|---|---|---|
upgrade-pools=true, tout d'un coup | cluster de développement, fenêtre de maintenance courte | une seule opération longue, sans point de contrôle intermédiaire |
| Control plane seul, puis pools plus tard | production : valider les API avant de toucher aux nœuds | vivre quelques jours avec un écart de version, ce que Kubernetes autorise |
| Pool neuf à la version cible, puis bascule | charges sensibles, retour arrière souhaité | payer deux pools en parallèle le temps de la migration |
La troisième mérite d'être connue, parce qu'elle restaure le retour arrière que la montée en place supprime. Scaleway la documente : au lieu de monter le pool existant, on en crée un neuf directement à la version cible, on y déplace les charges, puis on supprime l'ancien. Tant que les deux pools coexistent, revenir en arrière revient à replanifier les pods sur l'ancien.
Préparer l'application pour pouvoir mesurer
Section intitulée « Préparer l'application pour pouvoir mesurer »Sans sonde continue, vous ne saurez jamais ce que la montée a coûté à vos utilisateurs. Déployez une application à plusieurs répliques, protégée par un budget de disruption.
kubectl create deployment web \ --image=nginx:1.29.1@sha256:8adbdcb969e2676478ee2c7ad333956f0c8e0e4c5a7463f4611d7a2e7a7ff5dc \ --replicas=4kubectl create poddisruptionbudget web-pdb --selector=app=web --min-available=2kubectl expose deployment web --port=80 --target-port=80 --type=LoadBalancerkubectl rollout status deployment/web --timeout=300sLa sortie doit finir par deployment "web" successfully rolled out.
Attendez ensuite une vraie réponse, et non l'attribution d'une adresse : le Load Balancer reçoit son adresse avant que ses backends n'aient passé leurs contrôles de santé.
ADRESSE=$(kubectl get svc web \ -o jsonpath='{.status.loadBalancer.ingress[0].ip}{.status.loadBalancer.ingress[0].hostname}')
until [ "$(curl -s -o /dev/null -w '%{http_code}' --max-time 5 "http://${ADRESSE}/")" = "200" ]; do printf '.'; sleep 5doneecho " service joignable"La sortie doit finir par service joignable. Lancez alors la sonde, qui tournera pendant toute l'opération :
( while :; do curl -s -o /dev/null -w '%{http_code}\n' --max-time 3 "http://${ADRESSE}/" >> codes-http.txt sleep 1 done ) &PID_SONDE=$!Que fait la montée du control plane ?
Section intitulée « Que fait la montée du control plane ? »Elle ne touche pas à vos nœuds, et c'est le premier fait à comprendre.
scw k8s cluster upgrade "${CLUSTER_ID}" region=fr-par version=1.37.0 -o json | jq -r '.status'
until [ "$(scw k8s cluster get "${CLUSTER_ID}" region=fr-par -o json | jq -r '.status')" = "ready" ]; do printf '.'; sleep 15doneecho " control plane monté"La première sortie doit afficher updating, la dernière control plane monté. Mesuré le 2026-09-12 : 77 secondes.
Regardez maintenant l'écart que cette opération vient de créer :
scw k8s cluster get "${CLUSTER_ID}" region=fr-par -o json | jq -r '"control plane : \(.version)"'kubectl get nodes -o 'custom-columns=NOM:.metadata.name,KUBELET:.status.nodeInfo.kubeletVersion'La sortie doit afficher un control plane en 1.37.0 et des nœuds encore en v1.36.4.
Cet écart est normal et supporté. Kubernetes autorise un control plane en avance sur ses kubelets, ce qui rend la montée en deux temps possible : vous pouvez monter le control plane un jour et les nœuds un autre, en connaissance de cause.
Comment les nœuds sont-ils montés ?
Section intitulée « Comment les nœuds sont-ils montés ? »Ils ne le sont pas : ils sont remplacés, un par un. C'est le comportement à connaître avant de lancer l'opération, parce qu'il décide de la capacité disponible pendant la montée.
POOL_ID=$(scw k8s pool list cluster-id="${CLUSTER_ID}" region=fr-par -o json \ | jq -r '.[0].id')
scw k8s pool upgrade "${POOL_ID}" region=fr-par version=1.37.0 -o json | jq -r '.status'La sortie doit afficher upgrading. Observé le 2026-09-12 sur un pool de deux nœuds : un nœud neuf apparaît, l'ancien passe en deleting, et le second remplacement ne démarre qu'après le premier. Pendant l'opération, les quatre pods se sont retrouvés sur l'unique nœud disponible.
Le remplacement roulant explique à lui seul les trois contraintes officielles de cette leçon, et il vaut de s'y arrêter. Puisque chaque nœud est détruit puis recréé, tout ce qui vivait sur son disque local disparaît avec lui, d'où l'avertissement de Scaleway sur la perte de données locales. Puisque le pool pilote lui-même le nombre de machines pendant l'opération, il refuse qu'on le redimensionne en parallèle, sous peine de conflit. Et puisque le cluster fonctionne avec une machine de moins à chaque étape, la capacité disponible pendant la montée est celle de N-1 nœuds, pas celle de N. Dimensionner au plus juste revient donc à accepter une saturation garantie à chaque maintenance, ce qui transforme une opération de routine en incident.
Écrire une attente qui ne se trompe pas
Section intitulée « Écrire une attente qui ne se trompe pas »Compter les nœuds à la bonne version ne suffit pas, et c'est une erreur que j'ai commise avant de la corriger.
TAILLE=$(scw k8s pool get "${POOL_ID}" region=fr-par -o json | jq -r '.size')
until prets=$(kubectl get nodes --no-headers | awk '$2=="Ready"' | wc -l) && vieux=$(kubectl get nodes --no-headers -o 'custom-columns=V:.status.nodeInfo.kubeletVersion' \ | grep -cv 'v1.37.0') && transit=$(scw k8s node list cluster-id="${CLUSTER_ID}" region=fr-par -o json \ | jq -r '[.[] | select(.status != "ready")] | length') && [ "${prets}" = "${TAILLE}" ] && [ "${vieux}" = "0" ] && [ "${transit}" = "0" ]; do printf '.'; sleep 15doneecho " montée réellement terminée"La sortie doit finir par montée réellement terminée. Mesuré : 369 secondes pour deux nœuds.
Qu'est-ce que le trafic a vu ?
Section intitulée « Qu'est-ce que le trafic a vu ? »Deux requêtes perdues sur 249, soit 99,2 % de disponibilité.
kill "${PID_SONDE}"sort codes-http.txt | uniq -c | sort -rnecho "total : $(wc -l < codes-http.txt) requêtes"La sortie doit afficher la répartition des codes. Mesuré le 2026-09-12 :
247 200 2 000total : 249 requêtesUn code 000 signale une requête que curl n'a pas pu mener à terme, le plus souvent une connexion coupée pendant que le Load Balancer retirait un backend.
Le rôle réel du budget de disruption
Section intitulée « Le rôle réel du budget de disruption »Il cadence la montée, il ne la bloque pas. Observé en direct pendant le drain du premier nœud :
kubectl get pdb web-pdb -o 'custom-columns=MIN:.spec.minAvailable,SAINS:.status.currentHealthy,EXIGE:.status.desiredHealthy,EVICTIONS:.status.disruptionsAllowed'La sortie affichait 2 2 2 0 : zéro éviction autorisée, puisque le nombre de pods sains égalait exactement le minimum exigé. Le drain a repris de lui-même à mesure que les pods recréés devenaient sains.
Kapsule n'a jamais eu à forcer. C'est important, parce qu'il en aurait le pouvoir : max-termination-grace-period, réglable à la création du pool, vaut 15 minutes par défaut et passe outre votre budget au-delà. Un budget trop strict ne protège donc pas indéfiniment, il retarde.
Vérifiez enfin que le stockage a traversé l'épreuve :
kubectl get nodes -o 'custom-columns=NOM:.metadata.name,ETAT:.status.conditions[-1].type,KUBELET:.status.nodeInfo.kubeletVersion'kubectl get pvc -o 'custom-columns=NOM:.metadata.name,ETAT:.status.phase'La sortie doit afficher tous les nœuds en v1.37.0 et les PersistentVolumeClaim toujours Bound. Mesuré : le volume a survécu au remplacement complet des deux nœuds.
Limites, quotas et plafonds
Section intitulée « Limites, quotas et plafonds »| Plafond | Valeur | Source |
|---|---|---|
| Saut de version autorisé | une mineure, ou un correctif | aide de scw k8s cluster upgrade |
| Retour arrière | impossible | doc de montée de version |
| Redimensionnement pendant la montée | interdit | doc de montée de version |
| Fenêtre de support d'une mineure | 14 mois | politique de support des versions |
| Montée imposée après la fin de support | dans les 30 jours | politique de support des versions |
| Délai avant drain forcé | 15 min par défaut, 1 h au maximum | aide de scw k8s pool create |
Dépannage
Section intitulée « Dépannage »Les deux premiers symptômes portent la valeur exacte renvoyée par l'API.
| Symptôme | Cause | Solution |
|---|---|---|
Le cluster répond updating alors que version affiche déjà la cible | le champ de version bascule avant la fin de l'opération | attendre sur status, jamais sur version |
Le pool répond upgrading et un nœud manque dans kubectl get nodes | remplacement roulant : l'ancien est parti, le neuf n'est pas enregistré | combiner nombre, version et status != "ready" côté API |
| Le drain d'un nœud n'avance plus | le PodDisruptionBudget autorise zéro éviction | augmenter les répliques, ou assouplir min-available |
| Des données ont disparu après la montée des nœuds | elles étaient sur le disque local d'un nœud remplacé | passer par un PersistentVolumeClaim |
Antipatterns à éviter
Section intitulée « Antipatterns à éviter »Ces quatre erreurs traitent la montée comme une mise à jour en place. C'en est une destruction suivie d'une reconstruction.
| Antipattern | Conséquence | Discipline |
|---|---|---|
| Monter sans sonde continue | on ne saura jamais ce que l'opération a coûté aux utilisateurs | mesurer les codes HTTP pendant toute la durée |
| Dimensionner le cluster au plus juste | saturation garantie à chaque maintenance, puisque la capacité tombe à N-1 | prévoir la marge d'un nœud |
| Poser un budget de disruption très strict et l'oublier | le drain traîne, puis Kapsule force après 15 minutes | accorder min-available au nombre réel de répliques |
| Attendre la fin du support pour monter | Scaleway monte à votre place, au moment qu'il choisit | relever deprecated_at et planifier |
La montée de version sous l'angle Well-Architected
Section intitulée « La montée de version sous l'angle Well-Architected »Fiabilité
Section intitulée « Fiabilité »Quelle capacité reste-t-il pendant la maintenance ? Celle de N-1 nœuds, puisque le pool détruit une machine avant d'en fournir une autre. Mesuré : les quatre répliques se sont retrouvées sur un seul nœud.
Discipline : dimensionner pour N-1, poser un PodDisruptionBudget cohérent avec le nombre de répliques, et placer les données sur des volumes persistants plutôt que sur le disque local.
Excellence opérationnelle
Section intitulée « Excellence opérationnelle »Votre attente mesure-t-elle la fin de l'opération, ou un état transitoire qui lui ressemble ? Un critère à une seule condition a rendu ici un chiffre inférieur de 46 % à la réalité.
Discipline : combiner plusieurs conditions dont au moins une côté fournisseur, et se méfier d'une mesure qui tombe plus courte qu'attendu.
Ce que ce lab a prouvé
Section intitulée « Ce que ce lab a prouvé »Le volet Kubernetes se clôt sur l'opération la plus redoutée, et elle est mesurable.
| Ce qui a été prouvé | Mesure |
|---|---|
| Le control plane monte seul, et vite | 77 secondes |
| Les nœuds ne suivent pas : ils sont remplacés | 369 secondes pour deux nœuds |
| Le service survit à l'opération | 247 codes 200 sur 249, soit 99,2 % |
| Le volume persistant ne bouge pas | Bound de bout en bout |
Ce qui a résisté, et c'est le plus instructif : la mesure elle-même. Une attente qui ne compte que les nœuds à la version cible conclut à 198 secondes, soit 46 % de moins que la réalité. Pendant le remplacement roulant, un seul nœud peut être enregistré, déjà à la bonne version : le compte de vieux nœuds tombe à zéro et la boucle croit avoir fini.
Ce qui dément l'intuition : le champ version du cluster bascule avant la fin de l'opération. C'est status qui fait foi, et se fier au premier fait annoncer une montée terminée qui ne l'est pas.
Dépendances de destruction : le cluster emporte ses nœuds, mais pas les répartiteurs créés par ses services, ni les volumes dont la classe de stockage retient le volume. Les deux se vérifient séparément après la suppression du cluster.
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 »- Le control plane et les nœuds se montent séparément : après le premier, les kubelets restent en arrière, et c'est supporté.
- Mesuré le 2026-09-12 : control plane en 77 s, remplacement roulant de deux nœuds en 369 s.
- Le pool remplace ses nœuds, il ne les met pas à jour : tout ce qui vit sur un disque local disparaît.
- La capacité tombe à N-1 pendant toute l'opération.
- 247 codes 200 sur 249 : l'application reste disponible, à deux requêtes près.
- Le
PodDisruptionBudgetcadence le drain, il ne le bloque pas, mais Kapsule peut forcer après 15 minutes. - Le champ
versionbascule avant la fin : c'eststatusqui fait foi. - Une attente doit combiner nombre, version et nœuds en transit, sinon elle conclut trop tôt.
- On ne saute pas une mineure, et aucun retour arrière n'est possible.
- Les pools ne se redimensionnent pas pendant la montée : prévoir la capacité avant.
Pour aller plus loin
Section intitulée « Pour aller plus loin »- PostgreSQL en réseau privé : sauvegarder, perdre, restaurer : la base que l'application déployée sur ce cluster va consommer, et le plan de reprise qui va avec.
- Cockpit : observer un incident sans rien provisionner : la trace que la montée de version vient de laisser dans les métriques, relisible pendant trente et un jours.
Ressources externes
Section intitulée « Ressources externes »- Monter la version de Kubernetes : la procédure officielle, console et CLI, et la voie manuelle par création d'un pool à la version cible.
- Politique de support des versions : le calendrier de dépréciation et la montée imposée après la fin de support.
- Changelog Kubernetes amont : ce que chaque version mineure change réellement dans les API, à lire avant de monter.
- PodDisruptionBudget, documentation Kubernetes : la sémantique exacte du budget, commune à tous les fournisseurs.