kubectl apply s’utilise partout comme une évidence, la commande par défaut pour déployer un manifest. Ce qu’elle fait réellement dépasse largement « appliquer ce fichier » : elle calcule une fusion à trois voies entre trois états distincts, un mécanisme invisible dont l’ignorance explique un bug classique — la modification manuelle qui disparaît sans prévenir.
Trois états, pas deux
kubectl apply compare l’état actuellement stocké dans le cluster, la dernière configuration appliquée (conservée dans une annotation), et le fichier qu’on est en train d’appliquer maintenant :
# L'annotation kubectl.kubernetes.io/last-applied-configuration
# stocke exactement ce que le dernier apply a envoyé
kubectl get deployment mon-app -o jsonpath='{.metadata.annotations.kubectl\.kubernetes\.io/last-applied-configuration}'
kubectl create ou kubectl replace ne connaissent que le fichier fourni : ils écrasent ou créent, sans mémoire du passé. kubectl apply seul retient cette troisième référence, ce qui lui permet de détecter qu’un champ a été retiré du fichier depuis le dernier apply et de le supprimer sur le cluster, pas seulement d’ajouter ce qui est nouveau.
Le piège classique : le kubectl edit qui disparaît
Une modification manuelle via kubectl edit, jamais reportée dans le fichier source, survit tant que personne ne relance kubectl apply sur ce même fichier. Au prochain apply, la fusion à trois voies compare le fichier (qui n’a pas changé) à la dernière configuration appliquée (qui non plus) : le champ modifié à la main, absent des deux, est traité comme n’ayant jamais dû exister, et disparaît silencieusement, sans avertissement ni confirmation.
$ kubectl edit deployment mon-app
# une réplique passée de 3 à 5 à la main, en urgence
$ kubectl apply -f deployment.yaml
# le fichier source dit toujours 3 : le apply ramène silencieusement à 3
Ce n’est pas un bug de kubectl apply : c’est le comportement attendu d’un outil déclaratif qui suppose que le fichier source est la seule vérité. Le vrai bug est ailleurs, dans l’habitude de modifier un cluster à la main sans reporter le changement dans le fichier qui sera réappliqué tôt ou tard, exactement le risque de dérive que couvre l’article sur l’infrastructure as code pour Terraform, transposé à Kubernetes.
Server-side apply : la fusion déplacée vers l’API server
Le apply classique (« client-side ») calcule la fusion localement, dans kubectl, avant d’envoyer le résultat. Le server-side apply, activable via --server-side, déplace ce calcul dans l’API server lui-même, avec un bénéfice concret : la notion de propriété de champ (field ownership), où le serveur retient qui a écrit quel champ en dernier.
kubectl apply --server-side -f deployment.yaml
Deux contrôleurs différents (un opérateur qui gère spec.replicas selon une métrique, un déploiement GitOps qui gère le reste) peuvent alors coexister sur le même objet sans se marcher dessus, chacun propriétaire de ses propres champs. Le client-side apply, plus ancien et toujours celui utilisé par défaut sans le flag, ne fait aucune distinction de propriété : c’est le dernier apply qui gagne, sur l’objet entier.
Ce qui reste identique quel que soit le mode
Le principe de base ne change pas entre client-side et server-side : le fichier source reste la seule source de vérité que l’outil connaît. Un pipeline GitOps qui réconcilie en continu depuis Git repose entièrement sur cette hypothèse : toute modification qui contourne Git, manuelle ou non, est vouée à disparaître au prochain cycle de réconciliation, exactement comme un kubectl edit disparaît au prochain apply.
À retenir
kubectl apply fusionne trois états (cluster, dernière configuration appliquée, fichier actuel), pas deux, ce qui explique pourquoi une modification manuelle jamais reportée dans le fichier source disparaît silencieusement au prochain déploiement. Ce n’est pas un bug, c’est la conséquence directe d’un outil déclaratif qui traite le fichier comme la seule vérité. Server-side apply ajoute une notion de propriété de champ qui permet à plusieurs contrôleurs de coexister sur un même objet sans conflit, un socle qui compte dès qu’une migration Kubernetes introduit plusieurs acteurs sur les mêmes ressources.