Aller au contenu
English
Compte : non connecté, synchronisation possible Mon apprentissage
Selectionner le thème
English

kubectl diff et wait : vérifier avant, attendre après un apply

Par mis à jour le

Améliorer cette page

Logo Kubernetes

kubectl diff montre ce qu'un apply va changer avant de le lancer ; kubectl wait bloque jusqu'à ce qu'une ressource atteigne un état précis. Cette leçon facultative prolonge kubectl create et apply pour le cas où ce n'est plus vous qui tapez les commandes, mais un script de déploiement ou un pipeline CI/CD. Elle couvre les codes de sortie de diff, les conditions de wait et son délai par défaut, le piège du Job en échec, et ce que prouve vraiment chaque attente. Vous pouvez passer directement à la leçon Kustomize et y revenir quand vous automatiserez vos déploiements.

  • Lire une sortie de kubectl diff et distinguer vos changements des champs gérés par Kubernetes
  • Exploiter les codes de sortie de diff : 0, 1 ou 2
  • Attendre une condition avec kubectl wait, en sachant qu'il abandonne après 30 secondes par défaut
  • Attendre la fin d'un Job sans rester bloqué quand il échoue
  • Choisir entre kubectl wait et kubectl rollout status pour un Deployment
  • Écrire un script diff, apply, attente qui s'arrête au bon moment

Les deux commandes encadrent un apply et ne se remplacent pas. diff sert avant, pour décider s'il y a quelque chose à déployer et le relire. wait sert après, pour que la suite du script ne démarre pas avant que la ressource soit dans l'état voulu. Un apply réussi dit seulement que l'API a accepté le manifest, jamais que les Pods tournent.

MomentCommandeCe qu'elle fait
Avant applykubectl diff -f manifest.yamlAffiche l'écart entre le cluster et le fichier
Après applykubectl wait --for=condition=... --timeout=...Bloque jusqu'à une condition, ou échoue au délai
Après apply d'un Deploymentkubectl rollout status deployment/... --timeout=...Bloque jusqu'à la fin du déploiement de la nouvelle version

kubectl diff -f demande au serveur API ce que deviendrait l'objet si vous lanciez apply, par une requête à blanc (dry-run côté serveur), puis compare ce résultat à l'objet actuel. La sortie est donc toujours du YAML complet, valeurs par défaut du serveur comprises, présenté au format diff unifié : - pour ce qui part, + pour ce qui arrive.

Voici la sortie réelle après avoir changé l'image d'un Deployment dans le fichier. Deux blocs apparaissent, et un seul vient de vous : le second, sur l'image. Le premier, generation, est un compteur que Kubernetes incrémente à chaque modification de la spec ; il sort dans tout diff qui change quelque chose.

Fenêtre de terminal
kubectl diff -f api-gateway.yaml
Sortie (extrait, en-têtes de fichiers temporaires retirés)
@@ -6,7 +6,7 @@
creationTimestamp: "2026-10-13T15:47:33Z"
- generation: 1
+ generation: 2
labels:
app: api-gateway
@@ -31,7 +31,7 @@
spec:
containers:
- - image: nginx:1.30@sha256:9bf97bd7714f5e24c1ccd545ecb9eb5435cb6d109c97cebb15e7e455e0239edb
+ - image: nginx:1.29@sha256:1881968aff6f7cdcc4b888c00a11f4ce241ad7ec957e0cb4a9e19e93a3ff87ea
imagePullPolicy: IfNotPresent
name: nginx

Sur une ressource qui n'existe pas encore, le diff montre l'objet entier en lignes +, avec des champs que vous n'avez jamais écrits : progressDeadlineSeconds, strategy, terminationGracePeriodSeconds. Ce sont les valeurs par défaut posées par le serveur, et leur présence prouve que le calcul a bien été fait par l'API, pas par votre poste.

kubectl diff suit la convention de l'utilitaire Unix diff, documentée dans kubectl diff --help et vérifiée sur un cluster 1.37. C'est ce code, et non le texte affiché, qu'un script doit lire. Retenez surtout que 1 n'est pas une erreur : c'est la réponse « il y a des changements ».

CodeSignificationCas mesurés
0Aucune différenceFichier identique au cluster
1Des différences existentImage modifiée ; ressource pas encore créée
2 (plus de 1)kubectl ou diff a échouékind mal orthographié, fichier absent, droits insuffisants

Deux conséquences pratiques. Un enchaînement kubectl diff -f . && kubectl apply -f . fait l'inverse de ce qu'on attend : apply ne part que lorsqu'il n'y a rien à faire. Et sous set -e, le code 1 arrête le script : il faut capturer le code explicitement, comme le fait le script complet plus bas.

diff accepte les mêmes sources qu'apply : plusieurs -f, un dossier avec -R pour descendre dans les sous-dossiers, ou un overlay Kustomize avec -k, que la leçon Kustomize détaille. Le code de sortie porte sur l'ensemble : il vaut 1 dès qu'un seul objet diffère.

Fenêtre de terminal
kubectl diff -f ./k8s/prod/ -R
kubectl diff -k ./overlays/production/

kubectl diff appelle la commande diff de votre système avec les options -u -N. La variable KUBECTL_EXTERNAL_DIFF la remplace, options comprises, par exemple pour afficher des libellés lisibles à la place des chemins temporaires. L'outil choisi doit respecter la même convention de codes 0, 1 et 2, sans quoi votre script se trompera.

Fenêtre de terminal
export KUBECTL_EXTERNAL_DIFF="diff -u --label cluster --label fichier"
kubectl diff -f api-gateway.yaml

Le calcul passe toujours par le serveur. L'option --server-side change seulement la façon de fusionner : elle simule un server-side apply au lieu de l'apply classique. Utilisez-la si vous déployez vous-même avec kubectl apply --server-side : le diff montre alors exactement ce que ce mode fera, conflits de field manager compris.

Une requête à blanc est autorisée comme une vraie écriture. Un compte qui peut lire un Deployment sans pouvoir le modifier obtient un refus, avec le code 2 :

Sortie, compte de service limité à get et list (namespace élidé)
Error from server (Forbidden): error when patching "api-gateway.yaml": deployments.apps "api-gateway" is forbidden:
User "system:serviceaccount:...:lecteur" cannot patch resource "deployments" in API group "apps" ...

Le compte d'un pipeline qui lance diff a donc besoin des mêmes verbes RBAC (les droits d'accès de Kubernetes) qu'apply : get, patch, et create pour une ressource nouvelle.

kubectl wait surveille une ou plusieurs ressources et rend la main dès qu'une condition est remplie, avec le code 0, ou à l'expiration du délai, avec le code 1. C'est l'outil qui synchronise un script avec ce qui se passe réellement dans le cluster.

Sans --timeout, kubectl wait attend 30 secondes, puis échoue : c'est la valeur affichée par kubectl wait --help en 1.37, et la mesure le confirme. --timeout=0 vérifie une seule fois sans attendre ; une valeur négative attend une semaine. Fixez donc toujours le délai vous-même, adapté à ce que vous attendez : 30 secondes suffisent rarement à un déploiement qui tire une image.

Fenêtre de terminal
kubectl wait --for=condition=Ready pod/pod-ko
Sortie, au bout de 30 secondes, code 1
error: timed out waiting for the condition on pods/pod-ko

Chaque type publie ses conditions dans son status, et --for les désigne par leur nom. Pour une ressource précise, kubectl get <type>/<nom> -o yaml montre celles qui existent et leur valeur du moment. Les CRD des opérateurs publient souvent les leurs, une condition Ready le plus souvent, et wait les attend de la même façon.

--for=Sur quoiCe que ça attend
condition=ReadyPodLa readiness probe passe (sans probe : dès le démarrage du conteneur)
condition=AvailableDeploymentLe minimum de réplicas disponibles est atteint
condition=CompleteJobLe Job a réussi
condition=FailedJobLe Job a échoué
deleteToute ressourceLa ressource a disparu
createToute ressourceLa ressource apparaît dans l'API
jsonpath='{...}'=valeurToute ressourceUn champ du status vaut la valeur donnée

Plusieurs --for se combinent par un ET, et create est toujours attendu en premier. La commande suivante attend qu'un Pod apparaisse, puis qu'il soit prêt, là où un simple --for=condition=Ready échouerait aussitôt avec Error from server (NotFound) :

Fenêtre de terminal
kubectl wait --for=create --for=condition=Ready pod/web-0 --timeout=120s

Avec -l, wait attend que tous les objets sélectionnés remplissent la condition, et affiche une ligne par objet. Si le sélecteur ne trouve rien, la commande échoue immédiatement avec le code 1, sans attendre : un label mal tapé se voit donc tout de suite.

Fenêtre de terminal
kubectl wait --for=condition=Ready pods -l app=api-gateway --timeout=120s
Sortie, label sans aucun Pod
error: no matching resources found

Un Job lance un ou plusieurs Pods jusqu'à ce qu'une tâche se termine, une migration de base par exemple ; la leçon Jobs et CronJobs le détaille. --for=condition=Complete n'est rempli que par un Job réussi. Mesuré sur un Job qui échoue en 3 secondes : --for=condition=Failed répond aussitôt, mais --for=condition=Complete attend jusqu'au bout du délai avant de rendre le code 1.

Un script qui veut sortir dès que le Job est fixé, dans un sens ou dans l'autre, interroge les deux conditions à tour de rôle avec --timeout=0, qui vérifie sans attendre. La variable Bash SECONDS compte les secondes écoulées depuis le début du script et sert de délai global :

Fenêtre de terminal
JOB=job/migration-db
until kubectl wait --for=condition=Complete "$JOB" --timeout=0 >/dev/null 2>&1; do
if kubectl wait --for=condition=Failed "$JOB" --timeout=0 >/dev/null 2>&1; then
echo "Le Job a échoué." >&2
exit 1
fi
if [ "$SECONDS" -ge 300 ]; then
echo "Délai dépassé." >&2
exit 1
fi
sleep 2
done
echo "Job terminé avec succès."

Rejouée sur deux Jobs, cette boucle a rendu « Job terminé avec succès. » en 9 secondes pour le premier et « Le Job a échoué. », code 1, en 6 secondes pour le second, au lieu des 300 secondes d'un wait sur Complete seul.

--for=jsonpath attend qu'un champ quelconque prenne une valeur, ce qui couvre les ressources sans condition adaptée. La valeur est comparée sous forme de texte, et un champ absent fait attendre jusqu'au délai : vérifiez d'abord ce que contient réellement le champ avec kubectl get -o jsonpath.

Fenêtre de terminal
kubectl wait --for=jsonpath='{.status.succeeded}'=1 job/migration-db --timeout=120s
kubectl wait --for=jsonpath='{.status.availableReplicas}'=2 deployment/api-gateway --timeout=120s

Attention à ce que vous attendez : un Pod dont la sonde échoue est bien en phase Running, et --for=jsonpath='{.status.phase}'=Running le déclare prêt aussitôt. Pour savoir si un Pod reçoit du trafic, c'est la condition Ready qu'il faut attendre.

--for=delete rend la main quand l'objet a disparu de l'API. Utile après un kubectl delete --wait=false, ou avant de recréer un objet du même nom : un Pod met jusqu'à son délai de grâce, 30 secondes par défaut, à s'arrêter si son processus ignore le signal d'arrêt.

Fenêtre de terminal
kubectl delete pod api-server --wait=false
kubectl wait --for=delete pod/api-server --timeout=60s

Pourquoi Available ne prouve pas que la nouvelle version tourne ?

Section intitulée « Pourquoi Available ne prouve pas que la nouvelle version tourne ? »

--for=condition=Available sur un Deployment dit seulement que le minimum de réplicas est disponible, quelle que soit leur version. Mesuré sur un cluster 1.37 : après un apply qui pose une image inexistante, wait --for=condition=Available rend « condition met » en 0 seconde, servi par les anciens Pods encore en place. Dans le même temps, kubectl rollout status attend la nouvelle version et échoue au délai :

Sortie de rollout status, code 1
Waiting for deployment "api-gateway" rollout to finish: 1 out of 2 new replicas have been updated...
error: timed out waiting for the condition

Pour valider un déploiement dans un script, utilisez donc kubectl rollout status --timeout=..., qui suit la nouvelle version. Gardez kubectl wait pour les Pods, les Jobs, les suppressions et les ressources sans rollout.

Ce script assemble la leçon : il décide avec le code de diff, applique, puis attend la nouvelle version avec rollout status. Il a été rejoué sur un cluster kind 1.37 dans les quatre cas qui comptent : première création, relance sans changement, nouvelle image, image cassée, plus un manifest invalide.

#!/usr/bin/env bash
set -euo pipefail
NAMESPACE="${NAMESPACE:-prod}"
MANIFESTS="${MANIFESTS:-./k8s/prod/}"
DEPLOY="api-gateway"
echo "=== 1. Ce qui va changer ==="
rc=0
kubectl diff -n "$NAMESPACE" -f "$MANIFESTS" || rc=$?
case "$rc" in
0) echo "Aucun changement, rien à déployer."; exit 0 ;;
1) echo "Changements détectés, application." ;;
*) echo "kubectl diff a échoué (code $rc)." >&2; exit "$rc" ;;
esac
echo "=== 2. Application ==="
kubectl apply -n "$NAMESPACE" -f "$MANIFESTS"
echo "=== 3. Attente du déploiement ==="
if ! kubectl rollout status "deployment/$DEPLOY" -n "$NAMESPACE" --timeout=180s; then
echo "Déploiement non terminé dans le délai." >&2
kubectl get pods -n "$NAMESPACE" -l "app=$DEPLOY"
exit 1
fi
echo "Déploiement terminé."

La ligne || rc=$? est le point clé : elle empêche set -e d'arrêter le script sur le code 1 de diff tout en conservant ce code pour le case. Avec l'image cassée, le script s'arrête au délai, code 1, et affiche les Pods : le nouveau en ImagePullBackOff, les deux anciens toujours Running.

Le même script se lance tel quel dans un job de CI. Il lui faut un accès au cluster et un compte doté des droits d'apply, et rien d'autre : la sortie du diff reste dans le journal du job, où un relecteur peut la consulter avant de valider une étape manuelle.

Les erreurs se répartissent entre les deux commandes. Côté diff, la surprise la plus fréquente est un écart sur des champs que vous n'avez jamais écrits ; côté wait, une attente qui se termine par un délai dépassé. Dans ce cas, kubectl describe et les événements de la ressource disent presque toujours pourquoi la condition n'arrive pas.

SymptômeCause probableSolution
Le script s'arrête juste après kubectl diffset -e et code 1, qui signifie « changements »Capturez le code avec || rc=$?
diff montre generation ou des champs jamais écritsCompteur et valeurs par défaut posés par le serveurNormal : lisez seulement les lignes qui viennent de votre fichier
diff rend 2 avec Forbidden ... cannot patchLe compte n'a que des droits de lectureDonnez-lui les verbes get, patch et create
error: timed out waiting for the condition après 30 sPas de --timeout, délai par défautFixez un délai adapté, et lisez kubectl describe
wait --for=condition=Complete bloque jusqu'au délaiLe Job a échouéAttendez aussi condition=Failed
error: no matching resources foundAucun objet ne porte le labelVérifiez avec kubectl get pods -l <sélecteur>
Error from server (NotFound) sur waitL'objet n'existe pas encoreAjoutez --for=create
Available atteint, mais l'ancienne version tourneAvailable ignore la version des PodsUtilisez kubectl rollout status
  • kubectl diff calcule le résultat d'un apply par une requête à blanc au serveur, et l'affiche au format diff unifié.
  • Ses codes de sortie : 0 rien à faire, 1 changements (ressource nouvelle comprise), 2 ou plus pour une erreur ; sous set -e, capturez-les avec || rc=$?.
  • diff exige les mêmes droits qu'apply : une requête à blanc est autorisée comme une écriture.
  • kubectl wait abandonne après 30 secondes par défaut : fixez toujours --timeout.
  • --for=condition=Complete n'est rempli que par un Job réussi ; un Job en échec se détecte avec condition=Failed.
  • Un sélecteur sans résultat fait échouer wait immédiatement ; --for=create attend qu'un objet apparaisse.
  • Pour un Deployment, kubectl rollout status --timeout valide la nouvelle version ; condition=Available peut être servi par les anciens Pods.

Six questions pour vérifier que vous savez lire les codes de sortie de kubectl diff, régler le délai de kubectl wait, attendre un Job sans rester bloqué et choisir la bonne attente pour un Deployment.

Contrôle de connaissances

Validez vos connaissances avec ce quiz interactif

6 questions
5 min.
80% requis

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

Ce projet vous est utile ?

Les guides, formations, quiz et labs restent gratuits. Si vous les utilisez régulièrement, vous pouvez contribuer à leur maintenance et à leur développement.

Devenir membre de soutien (nouvel onglet) Soutenir une fois (nouvel onglet) En savoir plus

Suivez aussi le projet sur LinkedIn