Intro
Mettre à niveau des CronJobs Kubernetes ou les migrer entre clusters est rarement un simple kubectl apply. Les opérations de niveau production exigent une séquence qui commence par observer l'état actuel, documente le plus petit changement réversible, et se termine par des commandes de vérification concrètes où le résultat attendu est connu à l'avance. Cet article est un guide pratique de cette séquence exacte.
Vous y trouverez un flux de travail complet couvrant l'inventaire des versions, les changements de manifeste depuis l'API dépréciée batch/v1beta1 vers l'API stable batch/v1, la configuration sûre, la validation, les modes de défaillance avec restauration, et une liste de contrôle opérationnelle. Chaque section inclut des commandes réelles, des extraits YAML d'exemple, des sorties attendues et des critères de décision, afin que vous puissiez adapter les étapes à votre propre environnement.
Le public visé est les développeurs, ingénieurs DevOps, SRE et équipes techniques de startups qui exécutent des tâches planifiées dans Kubernetes. L'accent est mis sur les CronJobs spécifiquement, mais les préoccupations environnantes (version du cluster, registre de conteneurs, pipelines CI/CD) sont incluses lorsqu'elles impactent directement la sécurité de la mise à niveau.
Avant d'appliquer quoi que ce soit à un cluster partagé, créez un namespace jetable pour vos tests à blanc. Cela limite le rayon d'impact et vous permet de tester le chemin de migration complet sans toucher aux charges de travail de production.
kubectl create namespace cronjob-migration-test
kubectl config set-context --current --namespace=cronjob-migration-test
Vous avez maintenant un terrain de jeu sûr pour suivre chaque exemple ci-dessous.
Inventaire des versions et de l'environnement
La première étape de toute mise à niveau ou migration de CronJob est de savoir exactement ce que vous exécutez. L'API CronJob de Kubernetes a évolué au fil du temps : batch/v1beta1 était la version originale de l'API, dépréciée depuis Kubernetes 1.21, et supprimée dans Kubernetes 1.25. L'API stable batch/v1 est disponible depuis Kubernetes 1.21. Votre chemin de migration dépend fortement de la version de cluster que vous exécutez et des objets CronJob que vous avez.
Visez une migration propre vers batch/v1. Voici le tableau des phases :
| Version du cluster | Statut de batch/v1beta1 | Statut de batch/v1 | Action de migration requise |
|---|---|---|---|
| < 1.21 | Disponible | Non disponible | Mettre à niveau le cluster d'abord, puis migrer les CronJobs |
| 1.21 - 1.24 | Dépréciée | Disponible | Migrer les CronJobs vers batch/v1 avant la mise à niveau du cluster vers 1.25 |
| >= 1.25 | Supprimée | Disponible | Tous les CronJobs doivent être en batch/v1 |
Si vous êtes sur Kubernetes 1.24 ou antérieur, vous pouvez toujours lire les CronJobs batch/v1beta1 avec le serveur d'API. À partir de 1.25, les requêtes à l'ancien point de terminaison échouent avec une erreur 404. Si votre outillage GitOps ou votre pipeline CI/CD référence encore l'ancienne API, ces manifestes échoueront à s'appliquer après la mise à niveau du cluster.
Vérifier la version du cluster et la disponibilité de l'API
D'abord, capturez la version du cluster. Cela détermine si l'ancienne API est encore servie.
kubectl version --short
# Exemple de sortie sur 1.24 :
# Client Version: v1.24.10
# Server Version: v1.24.10
Pour voir quelles versions d'API sont disponibles pour les CronJobs dans votre cluster, utilisez kubectl api-versions.
kubectl api-versions | grep batch
# Exemple de sortie sur 1.24 :
# batch/v1
# batch/v1beta1
# Sur 1.25+, seule batch/v1 apparaît.
Ne présumez pas de la disponibilité uniquement à partir de la version du cluster : une API peut être désactivée via des feature gates ou des drapeaux du serveur d'API. La commande api-versions est la vérité terrain.
Lister tous les CronJobs existants et leurs versions d'API
Maintenant, énumérez chaque CronJob dans tous les namespaces, y compris la version d'API stockée dans chaque objet. Utilisez kubectl get cronjobs avec une sortie large.
kubectl get cronjobs --all-namespaces -o wide
# Exemple de sortie :
# NAMESPACE NAME SCHEDULE SUSPEND ACTIVE LAST SCHEDULE AGE CONTAINERS IMAGES
# default db-backup 0 2 * * * False 0 3m 12d backup postgres:15
La sortie -o wide ne montre pas la version d'API. Pour la capturer, utilisez une requête JSON.
kubectl get cronjobs --all-namespaces -o jsonpath='{range .items[*]}{.metadata.namespace}{"/"}{.metadata.name}{" apiVersion="}{.apiVersion}{"\n"}{end}'
# Exemple de sortie :
# default/db-backup apiVersion=batch/v1beta1
# default/reports apiVersion=batch/v1
Écrivez cette sortie dans un fichier. Cela devient votre inventaire de migration :
kubectl get cronjobs --all-namespaces -o jsonpath='{range .items[*]}{.metadata.namespace}{"/"}{.metadata.name}{" apiVersion="}{.apiVersion}{"\n"}{end}' > cronjob-inventory.txt
cat cronjob-inventory.txt
Évaluer la santé spécifique des CronJobs
Avant de modifier quoi que ce soit, vérifiez que les CronJobs existants fonctionnent réellement. Décrivez-en un pour voir ses événements récents.
kubectl describe cronjob db-backup -n default
# Cherchez la section Events :
# Events:
# Type Reason Age From Message
# ---- ------ ---- ---- -------
# Normal SuccessfulCreate 3m cronjob-controller Created job db-backup-28463140
# Normal SawCompletedJob 2m cronjob-controller Saw completed job: db-backup-28463140, status: Complete
S'il n'y a pas d'événements, ou si les événements montrent des échecs, corrigez le problème sous-jacent avant de tenter une migration d'API. Migrer un CronJob cassé ne fait que changer sa version d'API, pas son comportement.
Capturer l'état actuel pour la restauration
Ayez toujours un moyen de revenir en arrière. Stockez les manifestes actuels dans un répertoire versionné :
mkdir -p cronjob-backup/pre-migration
kubectl get cronjob db-backup -n default -o yaml > cronjob-backup/pre-migration/db-backup.yaml
Si votre CronJob a été créé via Helm ou un autre outil, ne vous fiez pas uniquement à l'objet vivant ; conservez également les valeurs de chart ou les manifestes d'origine. Pour une restauration manuelle, appliquer le YAML sauvegardé est le chemin le plus rapide.
Chemin de configuration sûr
Une fois que vous avez l'inventaire et une sauvegarde, l'étape suivante est de définir la configuration cible. L'objectif principal est de migrer le manifeste CronJob de batch/v1beta1 vers batch/v1. Le CronJob batch/v1 est structurellement identique à la bêta, donc le changement est généralement limité au champ apiVersion. Cependant, certains clusters ont des règles de validation ou de défaut supplémentaires, donc un dry-run est recommandé.
Exemple : migration d'un manifeste CronJob
Voici un CronJob batch/v1beta1 typique :
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: reports
namespace: production
spec:
schedule: "0 1 * * *"
concurrencyPolicy: Forbid
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 1
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: report-generator
image: registry.example.com/reports:1.4.2
command: ["/usr/local/bin/generate-report"]
env:
- name: REPORT_BUCKET
value: "s3://reports-archive"
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
Pour migrer, changez apiVersion en batch/v1.
apiVersion: batch/v1
kind: CronJob
metadata:
name: reports
namespace: production
spec:
schedule: "0 1 * * *"
# ... tout le reste demeure identique
Utiliser kubectl apply avec --dry-run=client ou --dry-run=server
Avant d'appliquer au cluster vivant, validez le manifeste. --dry-run=client vérifie la syntaxe YAML et le schéma de base. --dry-run=server envoie la requête au serveur d'API sans la persister, ce qui est la validation la plus précise possible.
kubectl apply -f reports-cronjob.yaml --dry-run=server
# Sortie attendue (si valide) :
# cronjob.batch/reports created (server dry run)
# Si la version d'API est supprimée, vous obtenez :
# error: unable to recognize "reports-cronjob.yaml": no matches for kind "CronJob" in version "batch/v1beta1"
Si le dry-run serveur réussit, vous pouvez appliquer pour de vrai. S'il échoue, inspectez le message d'erreur et ajustez le manifeste en conséquence. Ne sautez jamais cette étape ; elle détecte instantanément les incompatibilités de version, les champs manquants et les valeurs invalides.
Définir explicitement concurrencyPolicy et les limites d'historique
Une mise à niveau ou migration de CronJob est un bon moment pour revoir la spec. Deux champs causent souvent des surprises en production :
concurrencyPolicy: Forbidempêche les exécutions qui se chevauchent. La valeur par défaut estAllow, ce qui peut conduire à des jobs dupliqués si une exécution déborde.successfulJobsHistoryLimitetfailedJobsHistoryLimitcontrôlent combien d'objets Job terminés sont conservés. Les valeurs par défaut sont 3 et 1, ce qui est généralement bien. Les mettre trop bas supprime un historique d'audit utile ; trop haut encombre le cluster.
Exemple de spec de CronJob renforcée :
spec:
schedule: "0 2 * * *"
concurrencyPolicy: Forbid
successfulJobsHistoryLimit: 5
failedJobsHistoryLimit: 3
startingDeadlineSeconds: 300
jobTemplate:
spec:
backoffLimit: 4
activeDeadlineSeconds: 3600
startingDeadlineSeconds: 300 signifie que si le contrôleur CronJob rate le démarrage planifié de plus de 5 minutes (par exemple, le contrôleur était en panne), il n'exécutera pas cette itération, évitant une rafale de jobs de rattrapage. backoffLimit et activeDeadlineSeconds bornent les propres tentatives du job et sa durée totale.
Vérifier avec un test local
Pour être encore plus prudent, appliquez le CronJob migré dans un namespace séparé d'abord et déclenchez manuellement un job pour le voir fonctionner de bout en bout.
kubectl create namespace cronjob-migration-test
kubectl apply -f reports-cronjob.yaml -n cronjob-migration-test
# Ensuite créez un job de test à partir du CronJob en utilisant kubectl create job --from
kubectl create job manual-reports-test --from=cronjob/reports -n cronjob-migration-test
# Surveillez l'exécution du job
kubectl get jobs -n cronjob-migration-test
# NAME COMPLETIONS DURATION AGE
# manual-reports-test 1/1 42s 42s
Si le job de test réussit, votre configuration CronJob est valide sous la nouvelle version d'API. S'il échoue, inspectez les journaux et événements pour corriger le problème avant de migrer le CronJob de production.
Vérification et diagnostics
Après avoir appliqué le CronJob migré, vous devez vérifier qu'il est correctement configuré et qu'il s'exécutera comme prévu. La vérification ne consiste pas seulement à vérifier que la ressource existe ; c'est confirmer que le calendrier est interprété correctement, que le contrôleur peut créer des jobs, et que ces jobs se terminent avec succès.
Vérifier l'objet CronJob
D'abord, confirmez que le CronJob utilise maintenant batch/v1.
kubectl get cronjob reports -n production -o jsonpath='{.apiVersion}{"\n"}'
# Attendu : batch/v1
Ensuite, examinez le calendrier calculé. Une erreur courante est de définir une chaîne de calendrier que Kubernetes interprète différemment de l'intention. Le contrôleur CronJob utilise le format cron standard à cinq champs (minute heure jour-du-mois mois jour-de-la-semaine). Utilisez kubectl describe pour voir le calendrier et l'heure de dernière planification.
kubectl describe cronjob reports -n production
# Extrait de sortie :
# Schedule: 0 1 * * *
# Concurrency Policy: Forbid
# Suspend: False
# Last Schedule Time: 2025-03-12T01:00:00Z
Si Suspend est True, le CronJob ne s'exécutera jamais. Aussi, vérifiez Last Schedule Time pour voir si le contrôleur a planifié activement des jobs. Si l'heure de dernière planification est ancienne et qu'aucun job n'existe, examinez les journaux du contrôleur.
Surveiller la création et la fin des jobs
Déclenchez manuellement un job à partir du CronJob pour simuler l'exécution normale. Cela valide le modèle de job sans attendre la prochaine heure planifiée.
kubectl create job reports-manual-test --from=cronjob/reports -n production
kubectl get jobs -n production
# NAME COMPLETIONS DURATION AGE
# reports-manual-test 1/1 35s 35s
Si le job ne se termine pas, inspectez les journaux du pod et les événements.
kubectl logs jobs/reports-manual-test -n production
# Si le pod est dans une boucle de crash, obtenez les journaux précédents :
kubectl logs jobs/reports-manual-test -n production --previous
Vérifier la santé du contrôleur CronJob
Le contrôleur CronJob fait partie du kube-controller-manager. S'il n'est pas en cours d'exécution ou n'est pas sain, aucun CronJob ne sera planifié. Vérifiez son statut en regardant les pods du gestionnaire de contrôleurs dans le namespace kube-system.
kubectl get pods -n kube-system | grep controller-manager
# Sortie attendue (varie selon le cluster) :
# kube-controller-manager-control-plane 1/1 Running 0 42d
Si le gestionnaire de contrôleurs est dans une boucle de crash, vérifiez ses journaux pour les erreurs liées à la synchronisation CronJob.
kubectl logs -n kube-system kube-controller-manager-control-plane | grep -i cronjob
Valider les fuseaux horaires et la sémantique de planification
Les CronJobs Kubernetes ne supportent pas CRON_TZ. Le calendrier est toujours évalué dans le fuseau horaire du contrôleur, qui est généralement UTC. Si vous avez besoin qu'un job s'exécute à une heure locale spécifique, convertissez vous-même l'heure en UTC. Par exemple, pour s'exécuter à 1h00 heure normale de l'Est (UTC-5), le calendrier devrait être 0 6 * en UTC. Soyez explicite dans votre documentation pour éviter toute confusion.
Modes de défaillance et récupération
Même avec une planification minutieuse, les mises à niveau et migrations peuvent échouer. Cette section couvre les modes de défaillance les plus courants pour la migration de CronJob et fournit des étapes de récupération concrètes.
Défaillance : version d'API supprimée avant la migration
Si vous essayez d'appliquer un CronJob batch/v1beta1 sur un cluster Kubernetes 1.25+, le serveur d'API le rejette.
kubectl apply -f old-cronjob.yaml
# Sortie d'erreur :
# error: unable to recognize "old-cronjob.yaml": no matches for kind "CronJob" in version "batch/v1beta1"
Récupération : Modifiez le manifeste pour utiliser apiVersion: batch/v1 et appliquez à nouveau. Si vous avez de nombreux manifestes, utilisez un rechercher-remplacer dans votre dépôt ou un outil comme kubectl convert (s'il est encore disponible) pour convertir en masse. N'essayez pas d'utiliser l'ancienne API en rétrogradant le cluster ; ce n'est pas un chemin supporté.
Défaillance : le modèle de job référence une ConfigMap ou un Secret manquant
L'application du CronJob réussit, mais lorsqu'un job est créé, le pod échoue avec CreateContainerConfigError ou InvalidImageName.
kubectl describe pod reports-manual-test-abcde -n production
# Events:
# Warning Failed 10s (x2 over 30s) kubelet Error: configmap "report-config" not found
Récupération : Recréez la ConfigMap ou le Secret manquant dans le namespace cible. Assurez-vous que le namespace du CronJob correspond à l'endroit où vivent les objets de configuration. Si vous avez migré un CronJob vers un nouveau cluster, vous devez migrer également ses dépendances (ConfigMaps, Secrets, ServiceAccounts, PersistentVolumeClaims, etc.).
Défaillance : permissions ServiceAccount incorrectes
Après une migration inter-cluster, le ServiceAccount référencé dans le CronJob peut ne pas exister ou manquer de permissions RBAC. Le pod démarre mais le job échoue avec des erreurs d'autorisation.
kubectl logs jobs/reports-manual-test -n production
# Exemple d'erreur :
# Error from server (Forbidden): configmaps is forbidden: User "system:serviceaccount:production:reports-sa" cannot list resource "configmaps" in API group "" in the namespace "production"
Récupération : Créez le ServiceAccount et les liaisons Role ou ClusterRole nécessaires dans le nouveau cluster. Utilisez kubectl auth can-i --list --as=system:serviceaccount:production:reports-sa -n production pour auditer les permissions.
Défaillance : l'évaluation du calendrier conduit à des exécutions inattendues
Après la migration, le CronJob s'exécute à des heures inattendues. Les causes courantes incluent la confusion des fuseaux horaires ou une chaîne de calendrier qui était précédemment définie et se comporte maintenant différemment en raison des mises à niveau du contrôleur.
Récupération : Revoyez la chaîne de calendrier dans le manifeste et calculez les prochaines exécutions à l'aide d'un outil comme crontab.guru ou un analyseur cron local. Si le calendrier est incorrect, corrigez le manifeste et appliquez à nouveau. Si le calendrier est correct mais que les exécutions sont toujours décalées, vérifiez le fuseau horaire du contrôleur et ajustez le calendrier en conséquence.
Défaillance : chevauchements ou problèmes de concurrence
Si concurrencyPolicy est réglé sur Allow (par défaut) et que les jobs prennent plus de temps que l'intervalle de planification, plusieurs jobs peuvent s'exécuter simultanément, provoquant une contention des ressources ou une corruption des données.
Récupération : Réglez concurrencyPolicy: Forbid pour empêcher les exécutions qui se chevauchent. Si vous devez autoriser un certain chevauchement mais pas illimité, utilisez Replace pour tuer le job existant avant d'en démarrer un nouveau. Appliquez le changement et surveillez la prochaine exécution planifiée.
Procédure de restauration
Si une migration cause des problèmes imprévus (par exemple, un bogue dans la nouvelle image, ou un job se comporte différemment sous la nouvelle API), vous devez restaurer. Parce que les specs de CronJob batch/v1 et batch/v1beta1 sont identiques dans la plupart des champs, restaurer la version d'API est trivial : réappliquez le manifeste de sauvegarde enregistré (qui a l'ancienne apiVersion) si le cluster le supporte encore. Pour les clusters qui ne servent plus l'ancienne API, vous ne pouvez pas revenir à batch/v1beta1 ; vous devez plutôt corriger le problème tout en restant sur batch/v1.
Étapes de restauration :
- Suspendez le CronJob immédiatement pour arrêter la création de nouveaux jobs.
kubectl patch cronjob reports -n production -p '{"spec":{"suspend":true}}'
- Revenez à un manifeste précédent de votre répertoire de sauvegarde.
kubectl apply -f cronjob-backup/pre-migration/db-backup.yaml
- Vérifiez la restauration en contrôlant la version d'API et le calendrier.
kubectl get cronjob db-backup -n production -o jsonpath='{.apiVersion}{"\n"}'
- Réactivez si la restauration est efficace et que vous voulez reprendre le fonctionnement normal.
kubectl patch cronjob db-backup -n production -p '{"spec":{"suspend":false}}'
Ayez toujours un plan de restauration écrit avant de commencer la migration. En plein incident, vous n'aurez pas le temps de vous souvenir des commandes exactes.
Liste de contrôle opérationnelle
Utilisez la liste de contrôle suivante pour exécuter une mise à niveau/migration de CronJob en toute sécurité. Chaque élément inclut la commande et le résultat attendu.
| Étape | Action | Commande / Critère | Sortie / Signal attendu |
|---|---|---|---|
| 1 | Enregistrer la version du cluster | kubectl version --short | Server Version >= 1.21 pour batch/v1 |
| 2 | Confirmer la disponibilité de l'API | kubectl api-versions | grep batch | batch/v1 et batch/v1beta1 sur 1.21-1.24 ; seulement batch/v1 sur 1.25+ |
| 3 | Inventorier tous les CronJobs | kubectl get cronjobs --all-namespaces -o wide | Liste de tous les CronJobs avec versions d'API |
| 4 | Sauvegarder les manifestes CronJob existants | kubectl get cronjob <name> -n <ns> -o yaml > backup.yaml | Fichier YAML sauvegardé pour restauration |
| 5 | Mettre à jour apiVersion en batch/v1 dans le manifeste | Modifier YAML | apiVersion: batch/v1 |
| 6 | Appliquer en dry-run sur le cluster cible | kubectl apply -f cronjob.yaml --dry-run=server | cronjob.batch/<name> created (server dry run) |
| 7 | Appliquer en production | kubectl apply -f cronjob.yaml | cronjob.batch/<name> configured |
| 8 | Vérifier la version d'API | kubectl get cronjob <name> -o jsonpath='{.apiVersion}' | batch/v1 |
| 9 | Déclencher un job de test manuel | kubectl create job <name>-test --from=cronjob/<name> | Job créé et se termine avec succès |
| 10 | Vérifier l'exécution planifiée | Attendre la prochaine planification ; kubectl get jobs -n <ns> montre un nouveau job | Le job s'exécute à l'heure prévue |
| 11 | Surveiller les échecs | kubectl get jobs -n <ns> --watch | Aucun job échoué au-delà de backoffLimit |
| 12 | Documenter le chemin de restauration | Enregistrer les commandes de restauration dans le runbook | Prêt à exécuter si nécessaire |
Après avoir terminé la liste de contrôle, faites une revue post-migration : comparez la nouvelle version d'API, le calendrier, la politique de concurrence et les limites de ressources avec les valeurs pré-migration pour vous assurer que rien n'a été modifié involontairement.
Conclusion
Les mises à niveau et migrations de CronJobs Kubernetes sont un processus contrôlé en plusieurs étapes, pas une seule commande. La clé du succès est d'observer avant de changer, de sauvegarder avant d'appliquer, de faire un dry-run avant de valider, et de vérifier après coup. L'API stable batch/v1 est disponible depuis Kubernetes 1.21, et la migration vers celle-ci est simple si vous suivez les étapes décrites : inventorier vos CronJobs, mettre à jour l'apiVersion, valider avec des dry-runs serveur, tester avec un job manuel, et avoir un plan de restauration.
Comme prochaine étape, choisissez un CronJob à faible risque dans un namespace non-production et passez par le processus complet de bout en bout. Enregistrez l'état actuel, mettez à jour le manifeste, appliquez, déclenchez un job de test, et vérifiez le résultat. Ensuite, documentez les commandes exactes que vous avez utilisées dans un runbook pour votre équipe. Ce n'est qu'après avoir confiance dans le processus que vous devriez migrer les CronJobs de production.
Un flux de travail de mise à niveau fiable rend les échecs visibles, protège les valeurs sensibles comme les secrets et les identifiants, limite les changements à la ressource prévue, et définit la vérification de récupération avant qu'un incident ne force la décision. En suivant ce guide, vous pouvez migrer vos CronJobs Kubernetes en toute sécurité et avec confiance.