E-NO
Kubernetes 8 min de lecture

Kubernetes Admission Controllers Upgrade and Migration: Practical Examples for Safe Rollout

calendar_today Publié : 2026-08-20
update Dernière mise à jour : 2026-08-20
analytics Efficacité SEO : 100%
Illustration du guide technique pour « Kubernetes Admission Controllers Upgrade and Migration: Practical Examples for Safe Rollout ».

Introduction

Les contrôleurs d'admission (admission controllers) Kubernetes se situent entre l'authentification/l'autorisation et la persistance des objets, interceptant chaque requête API pour valider, muter ou rejeter des ressources. La mise à niveau ou la migration de ces contrôleurs est une opération courante mais à haut risque — que vous passiez d'API obsolètes comme admissionregistration.k8s.io/v1beta1 vers v1, que vous remplaciez PodSecurityPolicy par le contrôleur Pod Security Admission, ou que vous déployiez une mise à jour de webhook personnalisé. Un changement mal exécuté peut bloquer la création de charges de travail à l'échelle du cluster, tandis qu'une migration bien planifiée progresse par étapes contrôlées avec une capacité de restauration instantanée.

Ce guide fournit un flux de travail reproductible, testé en production : inventaire de l'état actuel, exécution d'un pilote ciblé avec des valeurs par défaut sûres, vérification du comportement avec des cas de test concrets, et maintien de procédures de récupération documentées. Chaque étape inclut les commandes kubectl exactes, les manifestes YAML et la sortie attendue pour que vous puissiez exécuter en toute confiance sur vos propres clusters.

Inventaire des Versions et de l'Environnement

Avant de toucher à toute configuration, établissez une base de référence précise. Exécutez ces commandes depuis une machine avec un accès cluster-admin :

kubectl version --short
kubectl get --raw /version

Exemple de sortie :

Client Version: v1.28.4
Kustomize Version: v5.0.4-0.20230601165947-6ce0bf390ce3
Server Version: v1.27.6

Confirmez quelles versions de l'API admissionregistration le cluster dessert. L'API v1 est stable depuis Kubernetes 1.16 ; si votre control plane est plus ancien, planifiez d'abord la mise à niveau du control plane.

kubectl api-versions | grep admissionregistration

Sortie attendue sur un cluster moderne :

admissionregistration.k8s.io/v1

Listez toutes les configurations de webhook actuellement installées :

kubectl get validatingwebhookconfigurations,mutatingwebhookconfigurations

Exemple de sortie :

NAME                           WEBHOOKS   AGE
pod-policy.example.com         1          45d
quota-enforcer.example.com     1          12d

Pour chaque webhook, inspectez la ressource complète pour capturer apiVersion, admissionReviewVersions, failurePolicy, sideEffects et namespaceSelector :

kubectl get validatingwebhookconfiguration pod-policy.example.com -o yaml

Extrait clé à enregistrer :

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
  name: pod-policy.example.com
webhooks:
- name: pod-policy.example.com
  admissionReviewVersions: ["v1"]
  sideEffects: None
  failurePolicy: Fail

Pour les plugins d'admission intégrés, examinez le manifeste de pod statique kube-apiserver. Sur les clusters kubeadm :

grep -A2 'enable-admission-plugins' /etc/kubernetes/manifests/kube-apiserver.yaml

Exemple de sortie :

- --enable-admission-plugins=NodeRestriction,NamespaceLifecycle,LimitRanger,ServiceAccount,PodSecurity
- --disable-admission-plugins=

Documentez toutes les découvertes dans un journal de modifications. Prenez un instantané etcd avant de procéder, et exportez chaque configuration de webhook en YAML pour une restauration immédiate :

mkdir -p /root/admission-backup/$(date +%F)
kubectl get validatingwebhookconfigurations,mutatingwebhookconfigurations -o yaml > /root/admission-backup/$(date +%F)/webhooks-backup.yaml

Chemin de Configuration Sûr : Pilote Ciblé

N'appliquez jamais un changement à l'échelle du cluster en une seule étape. Utilisez un pilote ciblé qui limite la nouvelle configuration à un seul namespace de test, et commencez avec failurePolicy: Ignore pour qu'un webhook défectueux ne puisse pas bloquer des charges de travail légitimes.

Pilote de Migration de Webhook

Créez une ValidatingWebhookConfiguration migrée ciblant l'API v1 avec un sélecteur de namespace :

# validatingwebhook-scoped.yaml
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
  name: pod-policy.example.com
webhooks:
- name: pod-policy.example.com
  clientConfig:
    service:
      name: webhook-service
      namespace: webhook-ns
      path: /validate
      port: 443
    caBundle: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0t...  # remplacer par le CA réel
  rules:
  - operations: ["CREATE", "UPDATE"]
    apiGroups: ["*"]
    apiVersions: ["*"]
    resources: ["pods"]
    scope: Namespaced
  namespaceSelector:
    matchLabels:
      admission-pilot: enabled
  admissionReviewVersions: ["v1"]
  sideEffects: None
  failurePolicy: Ignore
  timeoutSeconds: 5

Appliquez-le :

kubectl apply -f validatingwebhook-scoped.yaml

Sortie attendue :

validatingwebhookconfiguration.admissionregistration.k8s.io/pod-policy.example.com configured

Créez et étiquetez le namespace pilote :

kubectl create namespace pilot-ns
kubectl label namespace pilot-ns admission-pilot=enabled

Sortie attendue :

namespace/pilot-ns created
namespace/pilot-ns labeled

Seules les ressources créées dans pilot-ns atteindront désormais ce webhook. Tous les autres namespaces continueront d'utiliser la configuration précédente (ou aucun webhook s'il s'agit d'un nouveau déploiement).

Migration du Contrôleur Intégré : PodSecurityPolicy vers Pod Security Admission

Pour la migration intégrée, modifiez le manifeste kube-apiserver pour activer le plugin PodSecurity tout en conservant tous les plugins requis existants :

# Sauvegarde d'abord
cp /etc/kubernetes/manifests/kube-apiserver.yaml /root/admission-backup/$(date +%F)/kube-apiserver.yaml.backup

# Modifier le manifeste
vim /etc/kubernetes/manifests/kube-apiserver.yaml

Localisez le drapeau --enable-admission-plugins et assurez-vous que PodSecurity est inclus aux côtés des plugins obligatoires :

- --enable-admission-plugins=NodeRestriction,NamespaceLifecycle,LimitRanger,ServiceAccount,PodSecurity

Ne supprimez pas NodeRestriction, NamespaceLifecycle, LimitRanger ou ServiceAccount à moins d'avoir validé leur suppression dans un cluster de test. Le kubelet redémarrera automatiquement l'API server lorsque le manifeste change.

Maintenant, étiquetez le namespace pilote avec les modes Pod Security Admission. Utilisez baseline pour l'application (bloque les élévations de privilèges connues) et restricted pour les avertissements (alerte sur les charges de travail hautement privilégiées) :

kubectl label namespace pilot-ns pod-security.kubernetes.io/enforce=baseline
kubectl label namespace pilot-ns pod-security.kubernetes.io/warn=restricted
kubectl label namespace pilot-ns pod-security.kubernetes.io/audit=restricted

Sortie attendue :

namespace/pilot-ns labeled
namespace/pilot-ns labeled
namespace/pilot-ns labeled

Cette application ciblée signifie que seules les charges de travail de pilot-ns sont évaluées selon les nouvelles politiques. Le reste du cluster reste sur l'ancien PodSecurityPolicy (ou sans restriction) jusqu'à ce que vous étendiez les étiquettes.

Vérification et Diagnostics

La vérification a deux objectifs : confirmer que le contrôleur est actif, et observer les décisions exactes d'acceptation/refus pour des charges de travail représentatives.

Vérification du Webhook

Créez un Pod de test dans le namespace pilote qui devrait être refusé par votre politique (par exemple, un conteneur s'exécutant en root alors que la politique exige runAsNonRoot: true) :

# test-deny.yaml
apiVersion: v1
kind: Pod
metadata:
  name: test-deny
  namespace: pilot-ns
spec:
  containers:
  - name: nginx
    image: nginx:1.25
    securityContext:
      runAsNonRoot: false

Appliquez-le :

kubectl apply -f test-deny.yaml

Sortie de refus attendue :

Error from server (Forbidden): error when creating "test-deny.yaml": admission webhook "pod-policy.example.com" denied the request: Pod does not meet security policy: runAsNonRoot must be true

Créez maintenant un Pod conforme qui devrait être admis :

# test-allow.yaml
apiVersion: v1
kind: Pod
metadata:
  name: test-allow
  namespace: pilot-ns
spec:
  containers:
  - name: nginx
    image: nginx:1.25
    securityContext:
      runAsNonRoot: true
      runAsUser: 1000
kubectl apply -f test-allow.yaml

Sortie de succès attendue :

pod/test-allow created

Si le webhook ne se déclenche pas du tout, dépannez dans cet ordre :

  1. Correspondance du sélecteur de namespace : kubectl get namespace pilot-ns --show-labels — confirmez que admission-pilot=enabled est présent.
  2. Accessibilité du service : kubectl get endpoints -n webhook-ns webhook-service — vérifiez que l'IP de l'endpoint correspond à un pod webhook en cours d'exécution.
  3. Validité du bundle CA : Le caBundle dans la configuration du webhook doit correspondre au certificat de service. Décodez et inspectez : echo "<caBundle>" | base64 -d | openssl x509 -text -noout.
  4. Logs du webhook : kubectl logs -n webhook-ns -l app=webhook-service — recherchez les requêtes /validate entrantes et les erreurs de handshake TLS.

Vérifiez les événements récents dans le namespace pilote pour les entrées liées à l'admission :

kubectl get events -n pilot-ns --sort-by=.lastTimestamp

Exemple de sortie :

LAST SEEN   TYPE      REASON        OBJECT           MESSAGE
12s         Warning   FailedCreate  pod/test-deny    admission webhook "pod-policy.example.com" denied the request: Pod does not meet security policy: runAsNonRoot must be true

Vérification de Pod Security Admission

Testez le contrôleur intégré avec un Pod privilégié qui viole le profil baseline :

# test-privileged.yaml
apiVersion: v1
kind: Pod
metadata:
  name: test-privileged
  namespace: pilot-ns
spec:
  containers:
  - name: test
    image: busybox:1.36
    command: ["sleep", "3600"]
    securityContext:
      privileged: true
kubectl apply -f test-privileged.yaml -n pilot-ns

Refus attendu (application baseline) :

Error from server (Forbidden): pods "test-privileged" is forbidden: violates PodSecurity "baseline: v1.27": privileged (container "test" must not set securityContext.privileged=true)

Un Pod conforme (pas de privileged, pas de hostPath, pas de hostNetwork, etc.) devrait être créé avec succès.

Confirmation par les Journaux d'Audit

Si la journalisation d'audit de l'API server est activée, interrogez pour les décisions d'admission :

grep 'admission webhook' /var/log/kubernetes/audit.log | tail -n 5

Exemple d'entrée d'audit (tronquée) :

{"kind":"Event","apiVersion":"audit.k8s.io/v1","level":"RequestResponse","verb":"create","requestURI":"/api/v1/namespaces/pilot-ns/pods","user":{"username":"admin"},"responseStatus":{"code":403},"annotations":{"authorization.k8s.io/decision":"allow","authorization.k8s.io/reason":"RBAC: allowed by ClusterRoleBinding"}}

Un responseStatus.code de 200 ou 201 signifie admis ; 403 signifie refusé par un contrôleur d'admission.

Confirmez la politique d'échec en direct pendant le pilote :

kubectl get --raw /apis/admissionregistration.k8s.io/v1/validatingwebhookconfigurations/pod-policy.example.com | jq .webhooks[0].failurePolicy

Sortie attendue pendant le pilote :

"Ignore"

Modes d'Échec et Récupération

Les changements de contrôleurs d'admission échouent de manière prévisible. Le tableau ci-dessous associe chaque mode d'échec à son symptôme observable et à la commande de récupération exacte.

Mode d'ÉchecSymptômeAction de Récupération
Endpoint webhook en panne avec failurePolicy: FailToutes les créations de pods correspondantes bloquent puis échouent par timeoutDéfinir failurePolicy: Ignore ou supprimer le webhook
caBundle invalide ou mauvais nom de serviceErreurs "connection refused" ou TLS dans les logs de l'API serverCorriger caBundle ou supprimer le webhook
Champs v1 requis manquants (sideEffects, admissionReviewVersions)kubectl apply retourne une erreur de validation de champAjouter les champs manquants et réappliquer
Erreur de frappe dans --enable-admission-pluginskube-apiserver ne démarre pas, crashloopRestaurer le manifeste depuis la sauvegarde
Sélecteur de namespace trop large (ex: absent ou matchExpressions trop large)Le webhook rejette des ressources dans les namespaces de productionResserrer matchLabels et réappliquer

Commandes de Récupération

1. Échec de l'endpoint webhook — basculer immédiatement sur Ignore :

kubectl patch validatingwebhookconfiguration pod-policy.example.com \
  --type merge \
  -p '{"webhooks":[{"name":"pod-policy.example.com","failurePolicy":"Ignore"}]}'

Sortie attendue :

validatingwebhookconfiguration.admissionregistration.k8s.io/pod-policy.example.com patched

2. kube-apiserver ne démarre pas à cause d'une mauvaise configuration de plugin — restaurer le manifeste :

cp /etc/kubernetes/manifests/kube-apiserver.yaml /tmp/kube-apiserver.yaml.broken
cp /root/admission-backup/$(date +%F)/kube-apiserver.yaml.backup /etc/kubernetes/manifests/kube-apiserver.yaml

Le kubelet détecte le changement de fichier et redémarre automatiquement l'API server. Évitez systemctl restart kubelet sauf si le kubelet lui-même est défaillant.

3. Annuler une migration de webhook — supprimer la nouvelle config, appliquer la sauvegarde :

kubectl delete validatingwebhookconfiguration pod-policy.example.com
kubectl apply -f /root/admission-backup/$(date +%F)/webhooks-backup.yaml

4. Désactiver l'application Pod Security Admission sur un namespace :

kubectl label namespace pilot-ns pod-security.kubernetes.io/enforce-
kubectl label namespace pilot-ns pod-security.kubernetes.io/warn-
kubectl label namespace pilot-ns pod-security.kubernetes.io/audit-

Sortie attendue :

namespace/pilot-ns unlabeled
namespace/pilot-ns unlabeled
namespace/pilot-ns unlabeled

5. Vérification de santé post-récupération :

kubectl get nodes
kubectl get pods -A

Confirmez que tous les nœuds sont Ready et qu'aucun pod système n'est en CrashLoopBackOff ou Pending à cause de échecs d'admission.

Liste de Contrôle Opérationnelle

Utilisez cette liste de contrôle pour chaque mise à niveau ou migration de contrôleur d'admission. Cochez chaque élément et enregistrez la sortie dans votre journal de modifications.

  • [ ] Confirmer la version du cluster (kubectl version --short) et la version de l'API admissionregistration (kubectl api-versions | grep admissionregistration).
  • [ ] Exporter tous les YAML ValidatingWebhookConfiguration et MutatingWebhookConfiguration vers un répertoire de sauvegarde horodaté.
  • [ ] Prendre un instantané etcd (etcdctl snapshot save /backup/etcd-$(date +%F).db) et vérifier qu'il est restaurable sur un cluster de test.
  • [ ] Identifier le webhook ou le plugin intégré exact à modifier, son périmètre actuel et son failurePolicy.
  • [ ] Créer un namespace pilote avec une étiquette unique (ex: admission-pilot=enabled).
  • [ ] Appliquer la nouvelle configuration avec failurePolicy: Ignore (webhooks) ou des étiquettes de namespace ciblées (Pod Security Admission).
  • [ ] Exécuter un test positif (ressource conforme → admise) et un test négatif (ressource non conforme → refusée avec le message attendu) dans le namespace pilote.
  • [ ] Capturer la sortie exacte de refus/admission et la coller dans l'enregistrement de modification.
  • [ ] Vérifier les journaux d'audit de l'API server ou les logs du webhook pour les entrées de décision d'admission.
  • [ ] Une fois le pilote validé, étendre progressivement le namespaceSelector (ajouter des étiquettes à plus de namespaces) ou basculer failurePolicy sur Fail.
  • [ ] Documenter l'emplacement de l'artefact de restauration et la commande exacte pour restaurer (ex: kubectl apply -f /root/admission-backup/2024-01-15/webhooks-backup.yaml).
  • [ ] Planifier une révision 24 à 48 heures après le déploiement complet pour rechercher les échecs silencieux.

Commande de révision post-déploiement :

kubectl get events -A --field-selector reason=FailedCreate | grep -i admission

Si cela retourne des événements, investiguez la logique du webhook ou de la politique avant de considérer la migration comme terminée.

Conclusion

Une mise à niveau ou migration sécurisée d'un contrôleur d'admission suit une séquence disciplinée : inventorier l'état exact du cluster et la configuration actuelle, isoler le changement derrière un namespace pilote étiqueté avec failurePolicy: Ignore, vérifier chaque chemin d'acceptation et de refus avec des cas de test concrets et une sortie capturée, et maintenir des procédures de restauration documentées en une seule commande. Commencez par collecter la version du cluster, tous les YAML de webhook et la liste des plugins kube-apiserver. Appliquez les changements uniquement au namespace pilote — que ce soit via un namespaceSelector de webhook ou des étiquettes Pod Security Admission — et validez avec des pods de test à la fois négatifs et positifs. Gardez les manifestes de sauvegarde et les instantanés etcd accessibles pour pouvoir restaurer en quelques minutes si l'API server ne démarre pas ou si un webhook se comporte mal. Répétez la liste de contrôle opérationnelle pour chaque changement subséquent, et auditez les événements après le déploiement complet pour détecter les régressions silencieuses. Ces étapes transforment un changement risqué à l'échelle du cluster en une migration contrôlée et reproductible qui élimine les temps d'arrêt non planifiés et réduit le retravail.

Recherches connexes

Score de qualité de l’article

Utilité pour le lecteur 100%
  • check_circle Guide prêt à lire
  • check_circle Exemples pratiques inclus
  • check_circle URL d’article optimisée pour le SEO