Aller au contenu
English
Conteneurs & Orchestration medium

Gérer les dépréciations d'API Kubernetes

45 min de lecture

logo kubernetes

Les API Kubernetes évoluent : certaines versions sont dépréciées puis supprimées lors des mises à jour. Si vos manifests utilisent une API supprimée, vos déploiements échoueront après la mise à jour du cluster. Ce guide vous explique le cycle de vie des API, comment détecter les versions obsolètes, et comment migrer vos ressources avant qu'il ne soit trop tard.

  • Comprendre le cycle de vie des API Kubernetes (alpha → beta → stable)
  • Distinguer une API dépréciée (avertissement) d'une API supprimée (erreur)
  • Détecter les API obsolètes dans vos fichiers YAML, charts Helm et cluster
  • Migrer vos ressources avec kubectl convert et les outils appropriés
  • Automatiser la détection dans votre pipeline CI/CD

Prérequis : savoir écrire des manifests Kubernetes et comprendre le versioning sémantique.

Kubernetes fait évoluer ses API selon un processus annoncé à l'avance, qui laisse le temps de réagir. Chaque fonctionnalité traverse des phases de maturité successives avant d'être stabilisée ou abandonnée, et le nom de la version suffit à savoir où elle en est.

Une API Kubernetes ne disparaît jamais du jour au lendemain : elle traverse cinq états, et le nom de la version (v1alpha1, v1beta1, v1) suffit à savoir où elle en est. La colonne à surveiller dans le tableau ci-dessous est la durée typique : c'est elle qui vous dit combien de temps vous avez pour migrer une fois l'avertissement apparu. Le piège classique consiste à croire qu'une API beta est sûre parce qu'elle est activée par défaut, alors que sa fenêtre de suppression est deux fois plus courte que celle d'une API stable.

┌─────────┐ ┌─────────┐ ┌─────────┐ ┌────────────┐ ┌─────────┐
│ Alpha │ ──▶ │ Beta │ ──▶ │ Stable │ ──▶ │ Deprecated │ ──▶ │ Removed │
│ v1alpha1│ │ v1beta1 │ │ v1 │ │ v1 │ │ - │
└─────────┘ └─────────┘ └─────────┘ └────────────┘ └─────────┘
Expérimental Pré-stable Stable Obsolète Supprimé
PhaseCaractéristiquesDurée typique
Alpha (v1alpha1)Désactivé par défaut, peut changer ou disparaître sans préavisVariable
Beta (v1beta1)Activé par défaut, testé mais peut encore évoluer9+ mois minimum
Stable (v1)Garanti compatible, pas de changements cassantsIndéfini
DeprecatedToujours fonctionnel mais marqué obsolète, warnings dans les logs12+ mois
RemovedSupprimé, les manifests échouent-

La différence entre dépréciation et suppression se voit à l'exécution. Une API dépréciée applique la ressource et écrit un avertissement que personne ne lit dans les journaux de CI. Une API supprimée renvoie une erreur bloquante, et le message ci-dessous est trompeur : il parle de CRD alors que le vrai problème est un apiVersion que le cluster ne sert plus.

Quand une API est supprimée :

Fenêtre de terminal
kubectl apply -f deployment-old.yaml
error: resource mapping not found for name: "my-app" namespace: ""
from "deployment-old.yaml": no matches for kind "Deployment" in version "extensions/v1beta1"
ensure CRDs are installed first

Votre manifest devient inapplicable. Le cluster ne reconnaît plus cette combinaison apiVersion + kind.

Ces suppressions ont cassé des milliers de déploiements. Apprenez de l'histoire pour ne pas la répéter.

Les API extensions/v1beta1 et apps/v1beta1 ont été supprimées après des années de dépréciation. La migration était mécanique, un simple changement d'apiVersion dans quatre lignes de YAML, mais un détail a piégé beaucoup d'équipes : apps/v1 rend le champ spec.selector obligatoire et immuable, alors que extensions/v1beta1 le déduisait des labels du template. Un kubectl apply sur un Deployment existant échouait donc même après correction de l'apiVersion.

RessourceAncienne API (supprimée)Nouvelle API
Deploymentextensions/v1beta1apps/v1
DaemonSetextensions/v1beta1apps/v1
ReplicaSetextensions/v1beta1apps/v1
StatefulSetapps/v1beta1apps/v1

La vague 1.22 a été plus douloureuse que celle de 1.16 parce qu'elle ne se limitait pas à un changement d'apiVersion. L'Ingress en networking.k8s.io/v1 impose une structure différente pour le backend et un champ pathType obligatoire, ce qui casse les charts Helm dont les templates n'ont pas été adaptés. Notez la double ligne du tableau : les manifests écrits avant 1.14 pointaient sur extensions/v1beta1, ceux écrits ensuite sur networking.k8s.io/v1beta1, et les deux sont tombés dans la même version.

RessourceAncienne API (supprimée)Nouvelle API
Ingressextensions/v1beta1networking.k8s.io/v1
Ingressnetworking.k8s.io/v1beta1networking.k8s.io/v1
CertificateSigningRequestcertificates.k8s.io/v1beta1certificates.k8s.io/v1
ValidatingWebhookConfigurationadmissionregistration.k8s.io/v1beta1admissionregistration.k8s.io/v1

Deux des trois lignes du tableau se traitent par un remplacement d'apiVersion. La troisième, PodSecurityPolicy, n'a pas d'équivalent : c'est le seul cas de cette page où l'outillage automatique ne peut rien pour vous, parce que le modèle de sécurité change complètement. Une organisation qui avait construit une douzaine de PSP finement découpées par équipe a dû choisir entre les trois profils standards des Pod Security Standards et l'écriture de politiques dans un moteur d'admission tiers.

RessourceAncienne API (supprimée)Remplacement
PodSecurityPolicypolicy/v1beta1Pod Security Standards (PSS)
PodDisruptionBudgetpolicy/v1beta1policy/v1
EndpointSlicediscovery.k8s.io/v1beta1discovery.k8s.io/v1

Kubernetes 1.37, deux retraits invisibles aux outils

Section intitulée « Kubernetes 1.37, deux retraits invisibles aux outils »

Les trois exemples précédents sont des API supprimées, repérables en lisant les apiVersion de vos manifestes : c'est ce que font Pluto et kubent, vus plus bas. La 1.37 apporte deux changements d'une autre nature, que ces outils ne signalent pas, faute d'apiVersion modifié.

ChangementCe qui casse
Les Static Pods ne peuvent plus référencer de ressources de l'APIun Static Pod qui monte un Secret ou une ConfigMap est rejeté au chargement du fichier, et aucun Pod miroir n'apparaît
kubectl run --filename / -f est dépréciérien ne casse : le drapeau est ignoré, avec un avertissement

Le premier changement mérite qu'on s'y arrête, parce qu'il se diagnostique mal. Le kubelet ne fait pas démarrer le Pod puis échouer : il refuse le fichier de configuration lui-même. Conséquence directe, kubectl get pods ne montre rien, pas même un Pod en erreur, et le seul endroit où le refus est écrit est le journal du kubelet :

Fenêtre de terminal
journalctl -u kubelet | grep -i "static pod"
Sortie
E0914 14:10:16.293719 219 file.go:107] "Unable to process watch event"
err="can't process config file \"/etc/kubernetes/manifests/lab-staticpod.yaml\":
static pods may not reference secrets"

Le second est bénin, et la nuance compte pour ne pas chercher une panne qui n'existe pas. Le drapeau --filename ne fait rien, mais kubectl le dit désormais à voix haute :

Sortie
Flag --filename has been deprecated, it is ignored by kubectl run
and will be removed in a future release

Retenez-en la règle générale : une mise à niveau peut casser autre chose qu'un apiVersion. Un changement de comportement du kubelet ou un drapeau qui cesse d'être toléré ne laissent aucune trace dans vos YAML, et aucun analyseur de manifestes ne les verra.

Les quatre méthodes ci-dessous ne cherchent pas au même endroit, et c'est ce qui décide de celle qu'il vous faut. Deux d'entre elles interrogent le cluster, donc les objets réellement stockés : elles trouvent ce qui a été créé à la main ou par un opérateur, dont le YAML n'est nulle part. Les deux autres lisent des fichiers, donc votre dépôt Git : elles s'exécutent dans une pull request, avant tout contact avec un cluster.

Aucune ne remplace l'autre. Un manifeste corrigé dans Git n'efface pas l'objet obsolète déjà présent dans etcd, et inversement. En pratique, on combine une analyse de fichiers en intégration continue et un balayage du cluster avant chaque montée de version.

Depuis Kubernetes 1.19, kubectl affiche des warnings pour les API dépréciées :

Fenêtre de terminal
kubectl apply -f old-ingress.yaml
Warning: extensions/v1beta1 Ingress is deprecated in v1.14+, unavailable in v1.22+;
use networking.k8s.io/v1 Ingress
ingress.extensions/my-ingress created

Limitation : Vous ne voyez le warning qu'au moment de l'apply. Pas pratique pour auditer tous vos manifests.

Listez les versions d'API supportées par votre cluster :

Fenêtre de terminal
# Toutes les API disponibles
kubectl api-versions
# Vérifier si une API existe
kubectl api-versions | grep networking.k8s.io

Pour voir quelle version est préférée pour une ressource :

Fenêtre de terminal
kubectl api-resources | grep -i ingress

Pluto est un outil CLI dédié à la détection des API obsolètes. Contrairement aux deux méthodes précédentes, il travaille sur des fichiers, ce qui permet de l'exécuter avant tout contact avec un cluster, donc dans une pull request. Il embarque sa propre base de versions Kubernetes et sait aussi lire les releases Helm stockées dans le cluster.

  1. Installer Pluto

    Le projet publie un fichier checksums.txt à côté de chaque archive. Vérifiez l'empreinte avant d'extraire le binaire : c'est ce contrôle qui distingue une installation reproductible d'un téléchargement aveugle.

    Fenêtre de terminal
    brew install FairwindsOps/tap/pluto

    La vérification doit afficher pluto_5.24.3_linux_amd64.tar.gz: OK. Toute autre sortie signifie que l'archive est corrompue ou altérée, n'extrayez pas.

  2. Scanner un répertoire de manifests

    Fenêtre de terminal
    pluto detect-files -d ./manifests/

    Sortie :

    NAME KIND VERSION REPLACEMENT REMOVED DEPRECATED REPL AVAIL
    my-ingress Ingress extensions/v1beta1 networking.k8s.io/v1 true true true
    restricted PodSecurityPolicy policy/v1beta1 true true false

    La colonne REPL AVAIL décide de votre charge de travail : à true, un remplacement existe et la migration se résume à changer l'apiVersion. À false, comme pour la PodSecurityPolicy ci-dessus, il n'existe aucune ressource équivalente : il faut repenser la configuration.

  3. Scanner les releases Helm déployées

    Les charts déjà installés échappent aux scans de fichiers : le YAML rendu n'existe nulle part sur disque. Pluto va le lire dans les Secrets de release Helm.

    Fenêtre de terminal
    pluto detect-helm -owide
  4. Cibler une version Kubernetes spécifique

    Par défaut, Pluto compare aux versions de sa base interne. En fixant la version cible, vous obtenez la liste exacte de ce qui cassera lors de la montée de version prévue, sans bruit sur les API encore valides.

    Fenêtre de terminal
    pluto detect-files -d ./manifests/ --target-versions k8s=v1.29.0

kubent prend le problème par l'autre bout : au lieu de lire vos fichiers, il interroge le cluster et énumère les objets réellement stockés dans etcd. C'est le seul moyen de repérer les ressources créées à la main, par un opérateur ou par un ancien pipeline, dont le YAML n'est plus dans aucun dépôt Git.

Le projet ne publie pas de fichier de sommes de contrôle pour ses archives, mais il fournit une image de conteneur. L'épingler par digest garantit que vous exécutez exactement le binaire vérifié :

Fenêtre de terminal
# kubent 0.7.3, image épinglée par digest
docker run --rm \
-v "$HOME/.kube/config:/.kube/config:ro" \
ghcr.io/doitintl/kube-no-trouble@sha256:33016a690ca145963a0689cbe00a8499e1ba4d33c43629f9140d52e1ba698a2b \
--kubeconfig /.kube/config

Sur un poste où kubent est déjà installé, la commande se réduit à kubent. La sortie regroupe les objets par version de Kubernetes qui les supprimera :

>>> Deprecated APIs removed in 1.25 <<<
-----------------------------------------
KIND NAMESPACE NAME API_VERSION
PodSecurityPolicy <cluster> restricted policy/v1beta1

La migration se répartit en deux catégories, et savoir dans laquelle vous êtes évite de perdre du temps. Quand seul l'apiVersion change, l'outillage fait le travail. Quand la structure des champs change, comme le backend d'un Ingress qui passe de deux clés plates à un objet imbriqué, aucun convertisseur ne devine votre intention et la réécriture est manuelle.

La colonne REPL AVAIL de Pluto, vue plus haut, répond justement à cette question avant que vous ne commenciez.

Le plugin kubectl convert transforme les manifests vers une nouvelle API. Ce n'est pas une sous-commande native de kubectl : c'est un binaire séparé que la documentation Kubernetes distribue à part et que kubectl découvre grâce à son nom kubectl-convert. Le projet publie une empreinte SHA-256 à côté de chaque binaire ; le contrôle ci-dessous prend dix secondes et évite d'exécuter un fichier altéré en transit.

  1. Installer le plugin

    Fenêtre de terminal
    K8S_VERSION="$(curl -sSL https://dl.k8s.io/release/stable.txt)"
    BASE="https://dl.k8s.io/release/${K8S_VERSION}/bin/linux/amd64"
    curl -sSLO "${BASE}/kubectl-convert"
    curl -sSLO "${BASE}/kubectl-convert.sha256"
    echo "$(cat kubectl-convert.sha256) kubectl-convert" | sha256sum --check -
    sudo install -m 0755 kubectl-convert /usr/local/bin/kubectl-convert

    La commande de vérification doit afficher kubectl-convert: OK. Contrôlez ensuite que kubectl reconnaît le plugin avec kubectl convert --help.

  2. Convertir un manifest

    Fenêtre de terminal
    kubectl convert -f old-ingress.yaml --output-version networking.k8s.io/v1 > new-ingress.yaml
  3. Vérifier le résultat

    Le fichier converti peut nécessiter des ajustements manuels. Par exemple, l'Ingress v1 exige le champ pathType :

    # Avant (v1beta1) - pas de pathType
    - path: /api
    # Après (v1) - pathType obligatoire
    - path: /api
    pathType: Prefix # ou Exact, ImplementationSpecific

Dès qu'un champ change de forme, comme le backend d'un Ingress qui passe d'une paire de clés plates à un objet imbriqué, la conversion automatique atteint ses limites. Le processus ci-dessous ajoute deux garde-fous que kubectl convert n'offre pas : une sauvegarde de l'objet vivant avant modification, et une lecture du guide de migration officiel, qui documente les champs sans équivalent.

  1. Sauvegarder la ressource actuelle

    Fenêtre de terminal
    kubectl get ingress my-ingress -o yaml > backup-ingress.yaml
  2. Consulter la documentation de migration

    Kubernetes publie des guides de migration pour chaque version majeure :

  3. Adapter le manifest

    Exemple pour Ingress v1beta1 → v1 :

    # Avant
    apiVersion: networking.k8s.io/v1beta1
    kind: Ingress
    metadata:
    name: my-ingress
    spec:
    rules:
    - host: example.com
    http:
    paths:
    - path: /api
    backend:
    serviceName: api-service
    servicePort: 80
    # Après
    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
    name: my-ingress
    spec:
    rules:
    - host: example.com
    http:
    paths:
    - path: /api
    pathType: Prefix # NOUVEAU: obligatoire
    backend:
    service: # NOUVEAU: structure imbriquée
    name: api-service
    port:
    number: 80
  4. Appliquer et valider

    Fenêtre de terminal
    kubectl apply -f new-ingress.yaml
    kubectl describe ingress my-ingress

Un scan lancé à la main ne trouve une régression que le jour où quelqu'un pense à le relancer. L'automatiser déplace le contrôle au moment où la dette s'introduit, dans la pull request qui ajoute le manifeste, plutôt qu'au moment où elle coûte cher, pendant la montée de version.

Les deux exemples qui suivent partagent trois choix délibérés : la version de Pluto est figée et vérifiée par empreinte, pour qu'une mise à jour amont ne change pas le verdict sans prévenir ; --target-versions désigne la version que vous visez, pas celle que vous exécutez ; et le job échoue sur une API supprimée, sans quoi le rapport n'est qu'un fichier que personne n'ouvre.

Le déclencheur pull_request filtré sur manifests/** et charts/** évite de faire tourner le scan à chaque commit de documentation. Deux points de ce workflow méritent attention : la version de Pluto est figée avec vérification d'empreinte, pour qu'une mise à jour amont ne change pas silencieusement le verdict de la CI, et --target-versions désigne la version de cluster que vous visez, pas celle que vous exécutez aujourd'hui.

.github/workflows/api-deprecation.yaml
name: Check Kubernetes API deprecations
on:
pull_request:
paths:
- 'manifests/**'
- 'charts/**'
permissions: {}
jobs:
pluto:
runs-on: ubuntu-24.04
permissions:
contents: read
env:
PLUTO_VERSION: "5.24.3"
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- name: Install Pluto
run: |
base="https://github.com/FairwindsOps/pluto/releases/download/v${PLUTO_VERSION}"
curl -sSLO "${base}/pluto_${PLUTO_VERSION}_linux_amd64.tar.gz"
curl -sSLO "${base}/checksums.txt"
grep "pluto_${PLUTO_VERSION}_linux_amd64.tar.gz" checksums.txt | sha256sum --check -
tar xzf "pluto_${PLUTO_VERSION}_linux_amd64.tar.gz" pluto
sudo install -m 0755 pluto /usr/local/bin/pluto
- name: Check manifests
run: pluto detect-files -d ./manifests/ --target-versions k8s=v1.30.0
- name: Check Helm charts
run: |
for chart in ./charts/*/; do
helm template test "$chart" | pluto detect -
done

Pluto ne se contente pas d'afficher un rapport, il traduit la gravité dans son code de sortie. C'est ce qui permet de bloquer une pull request sans analyser la sortie texte. Un quatrième code existe en plus des trois du tableau : 4 signifie qu'une API supprimée n'a aucun remplacement disponible, le cas de la PodSecurityPolicy. Les options --ignore-deprecations, --ignore-removals et --ignore-unavailable-replacements neutralisent chacune le code correspondant.

CodeSignificationAction CI/CD
0Aucune API obsolèteContinuer
2API dépréciées trouvéesAvertir (optionnel)
3API supprimées trouvéesBloquer le merge
4API supprimée sans remplacementBloquer, et prévoir une réécriture

La distinction entre 2 et 3 ne dépend pas du manifeste mais de la version visée, et c'est ce qui rend --target-versions structurant. Le même Ingress en extensions/v1beta1 analysé contre deux cibles différentes donne :

Fenêtre de terminal
pluto detect-files -d ./manifests/ # code 3
pluto detect-files -d ./manifests/ --target-versions k8s=v1.21.0 # code 2

Dans le second cas, la colonne REMOVED passe à false : sur un cluster 1.21, cette API existe encore. Le manifeste n'a pas changé, votre échéance si.

Le piège, dans une étape GitHub Actions, est que le shell tourne avec set -e : si Pluto sort en 3, l'étape s'arrête avant d'atteindre la ligne qui lit $?. Il faut donc capturer le code dans la même commande.

- name: Check and fail on removed APIs
run: |
exit_code=0
pluto detect-files -d ./manifests/ --target-versions k8s=v1.30.0 || exit_code=$?
if [ "$exit_code" -ge 3 ]; then
echo "::error::API supprimées détectées, migration requise."
exit 1
fi
if [ "$exit_code" -eq 2 ]; then
echo "::warning::API dépréciées détectées."
fi

La logique est la même côté GitLab, avec une différence utile : le format de sortie markdown produit un rapport lisible que vous conservez en artefact. Le when: always est indispensable, sans lui l'artefact disparaît précisément dans le cas qui vous intéresse, celui où le job échoue parce que des API supprimées ont été trouvées.

.gitlab-ci.yml
api-deprecation-check:
stage: validate
image: alpine:3.22@sha256:14358309a308569c32bdc37e2e0e9694be33a9d99e68afb0f5ff33cc1f695dce
variables:
PLUTO_VERSION: "5.24.3"
before_script:
- apk add --no-cache curl tar
- base="https://github.com/FairwindsOps/pluto/releases/download/v${PLUTO_VERSION}"
- curl -sSLO "${base}/pluto_${PLUTO_VERSION}_linux_amd64.tar.gz"
- curl -sSLO "${base}/checksums.txt"
- grep "pluto_${PLUTO_VERSION}_linux_amd64.tar.gz" checksums.txt | sha256sum -c -
- tar xzf "pluto_${PLUTO_VERSION}_linux_amd64.tar.gz" pluto
- install -m 0755 pluto /usr/local/bin/pluto
script:
- pluto detect-files -d ./manifests/ --target-versions k8s=v1.30.0 -o markdown > deprecation-report.md
artifacts:
paths:
- deprecation-report.md
when: always
allow_failure: false

Deux rythmes cohabitent, et confondre les deux est l'erreur la plus commune. Le premier est ponctuel, lié à une montée de version précise, et son enjeu est l'ordre des opérations. Le second est continu, et son enjeu est d'empêcher la dette de se reconstituer entre deux montées.

L'ordre de ces cinq étapes n'est pas négociable, et c'est le point que les équipes inversent le plus souvent : on met à jour le cluster, puis on découvre les manifestes cassés. La migration précède la montée de version, parce qu'un manifeste écrit dans la nouvelle apiVersion fonctionne déjà sur l'ancien cluster, alors que l'inverse est faux.

  1. Scanner tous les manifests avec Pluto ou kubent
  2. Cibler la version cible : --target-versions k8s=v1.X.0
  3. Migrer et tester en environnement staging
  4. Mettre à jour les charts Helm si vous en utilisez
  5. Mettre à jour le cluster seulement après migration

Scanner uniquement au moment de l'upgrade transforme la montée de version en chantier. Le rythme ci-dessous répartit l'effort : le contrôle en pull request empêche l'arrivée de nouvelles dettes, et le scan mensuel ciblant la version actuelle plus deux vous donne l'avance nécessaire pour migrer sans urgence, puisque Kubernetes publie environ trois versions mineures par an.

FréquenceAction
À chaque PRScanner les manifests modifiés
MensuelScanner tout le repo avec la version K8s actuelle + 2
Avant upgradeRapport complet sur la version cible

Les outils embarquent une base de versions figée au moment de leur compilation : sur une version de Kubernetes récente, ils peuvent ignorer une dépréciation annoncée après leur dernière release. Les trois pages ci-dessous font autorité. La première explique les règles du jeu, la deuxième donne le tableau exhaustif API par API, et la troisième précise l'écart de version toléré entre kubectl, le plan de contrôle et les kubelets.

  • Les API Kubernetes suivent un cycle : alpha → beta → stable → deprecated → removed
  • Une API dépréciée fonctionne encore (avec warnings), une API supprimée casse vos déploiements
  • Kubernetes garantit 12 mois minimum entre dépréciation et suppression pour les API stables
  • Utilisez Pluto ou kubent pour détecter les API obsolètes dans vos manifests et votre cluster
  • kubectl convert migre les manifests simples, mais les changements structurels nécessitent une réécriture manuelle
  • Automatisez la détection dans votre CI/CD pour bloquer les PR avec des API supprimées
  • Scannez avant chaque upgrade de cluster avec la version cible

Sept questions pour vérifier que vous distinguez une API dépréciée d'une API supprimée, que vous savez quel outil sortir selon que la ressource vit dans un fichier ou dans le cluster, et que vous lisez correctement un code de sortie Pluto.

Contrôle de connaissances

Validez vos connaissances avec ce quiz interactif

7 questions
6 min.
70% 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 site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn