Un namespace ou un objet en Terminating depuis des heures, sans jamais disparaître, n’est pas un bug de Kubernetes : c’est un finalizer qui attend qu’un contrôleur exécute un nettoyage, un contrôleur qui, souvent, n’existe plus ou ne répond plus. Comprendre ce mécanisme évite de le contourner à l’aveugle, une opération qui a un vrai coût si elle saute une étape de nettoyage nécessaire.

Ce qu’un finalizer bloque, et pourquoi

Un finalizer est une chaîne de caractères posée dans les métadonnées d’un objet, qui empêche sa suppression définitive tant qu’elle n’a pas été explicitement retirée. kubectl delete sur un objet avec un finalizer ne le supprime pas immédiatement : il marque l’objet en Terminating et attend qu’un contrôleur, celui qui a posé le finalizer, effectue son nettoyage puis le retire lui-même.

metadata:
  finalizers:
    - kubernetes.io/pv-protection
  deletionTimestamp: "2026-07-28T10:00:00Z"

Le finalizer kubernetes.io/pv-protection, par exemple, empêche la suppression définitive d’un PersistentVolume tant qu’un pod l’utilise encore, un garde-fou qui évite de supprimer un volume de stockage encore attaché à une charge active.

Le scénario classique : le contrôleur a disparu

Un finalizer posé par un opérateur ou un webhook personnalisé suppose que ce même composant reste opérationnel pour le retirer au bon moment. Si l’opérateur a été désinstallé, a crashé définitivement, ou n’a jamais correctement géré ce cas, le finalizer reste indéfiniment sur l’objet : personne ne le retirera jamais, et l’objet reste en Terminating pour toujours, un état qui ressemble à un blocage du serveur API alors que la cause réelle se trouve ailleurs, dans un contrôleur absent.

# Identifier quel finalizer bloque réellement la suppression
kubectl get namespace stuck-ns -o jsonpath='{.spec.finalizers}'

Diagnostiquer avant de forcer

La première étape n’est jamais de retirer le finalizer à l’aveugle, mais de comprendre pourquoi il n’a jamais été retiré : le contrôleur associé existe-t-il encore dans le cluster, tourne-t-il, ses logs montrent-ils une erreur au moment de la tentative de nettoyage ? Un finalizer qui bloque parce que son contrôleur rencontre une erreur transitoire (une dépendance externe injoignable) se résout en corrigeant cette cause, pas en forçant la suppression.

# Vérifier si le contrôleur associé est toujours présent
# et actif avant de forcer quoi que ce soit
kubectl get pods -n kube-system | grep <nom-operateur>

Forcer le retrait, en connaissance de cause

Quand le contrôleur est définitivement absent (désinstallé sans nettoyage préalable) et qu’aucune correction n’est possible, retirer manuellement le finalizer débloque l’objet, mais saute délibérément le nettoyage que ce finalizer était censé garantir.

# Force le retrait du finalizer bloquant, en sautant
# le nettoyage qu'il était censé garantir
kubectl patch namespace stuck-ns --type=merge \
  -p '{"spec":{"finalizers":[]}}' --subresource=finalize

Cette commande n’est jamais un premier réflexe : elle est justifiée seulement après avoir confirmé qu’aucun contrôleur ne peut plus effectuer le nettoyage attendu, et en acceptant explicitement que ce nettoyage (parfois une ressource cloud externe, un enregistrement DNS, un certificat) ne se fera jamais.

À retenir

Un objet Kubernetes bloqué en Terminating attend qu’un contrôleur retire un finalizer après avoir effectué un nettoyage, pas un blocage du serveur API lui-même. Le scénario le plus fréquent est un contrôleur disparu ou en échec permanent, qui ne retirera jamais le finalizer qu’il a posé. Diagnostiquer la cause réelle (le contrôleur existe-t-il, fonctionne-t-il) précède toujours le retrait forcé du finalizer, une opération qui saute délibérément un nettoyage, à réserver aux cas où ce nettoyage n’est de toute façon plus possible, une des situations de troubleshooting qui se posent dès qu’une migration Kubernetes désinstalle un opérateur sans avoir anticipé ce cas.