Introduction
Les CronJobs Kubernetes sont le mécanisme privilégié pour exécuter des charges de travail basées sur le temps telles que les sauvegardes de bases de données, la génération de rapports, le préchauffage de caches ou les tâches de nettoyage. Un CronJob se comporte un peu comme le démon cron classique d'Unix, mais au lieu d'exécuter un processus sur un seul hôte, il crée des Jobs Kubernetes selon un calendrier répétitif. Chaque Job, à son tour, crée un ou plusieurs Pods qui effectuent le travail réel. Cette superposition offre une flexibilité de planification ainsi que des reprises, un parallélisme et une gestion des ressources natifs de Kubernetes.
Pour les opérateurs, le véritable défi n'est pas d'écrire le YAML du CronJob ; c'est de savoir quelles commandes exécuter lorsqu'un calendrier a dérivé, qu'un Job a échoué silencieusement, qu'un Pod est en boucle de crash ou qu'un quota de ressources est épuisé. Ce guide se concentre sur l'ensemble des commandes pratiques dont vous avez besoin pour inspecter, configurer, déboguer et récupérer des CronJobs. Chaque section comprend des commandes concrètes, des résultats attendus et des signaux d'échec, pour que vous puissiez passer de l'observation à la résolution sans suppositions.
Tout au long de ce guide, nous insistons sur la sécurité opérationnelle : observez avant de modifier, limitez le rayon d'impact de toute modification, utilisez des espaces réservés plutôt que des secrets lors des tests, vérifiez le résultat et documentez les chemins de récupération avant d'en avoir besoin. Que vous soyez un développeur responsable d'un seul microservice ou un consultant DevOps gérant un cluster partagé, ces commandes vous aideront à maintenir des charges planifiées saines et prévisibles.
Inventaire de version et d'environnement
Avant de pouvoir exploiter de manière fiable les CronJobs, vous devez connaître la version de Kubernetes que vous ciblez et la version de l'API de la ressource CronJob. Les CronJobs sont stables (apiVersion : batch/v1) depuis Kubernetes 1.21. Si vous utilisez un cluster plus ancien, vous devrez peut-être utiliser batch/v1beta1, qui a été déprécié en 1.21 et supprimé en 1.25. La commande pour vérifier la version de votre cluster est :
kubectl version --short
Le résultat attendu inclut à la fois les versions client et serveur, telles que :
Client Version: v1.28.2
Server Version: v1.27.3
Si votre version de serveur est inférieure à 1.21, vous devez utiliser batch/v1beta1 dans vos manifestes de CronJob, et certaines fonctionnalités comme le champ timeZone (introduit en 1.24) ne seront pas disponibles.
Ensuite, confirmez que le contrôleur CronJob est en cours d'exécution. Le contrôleur fait partie du kube-controller-manager, qui s'exécute généralement comme un Pod statique sur les nœuds du plan de contrôle. Pour le voir :
kubectl get pods -n kube-system | grep controller-manager
Vous devriez voir un Pod nommé quelque chose comme kube-controller-manager-<nom-du-nœud> avec l'état Running. S'il n'est pas en cours d'exécution, la planification des CronJobs ne fonctionnera pas et aucun Job ne sera créé.
Prérequis pour utiliser les CronJobs :
- Un cluster Kubernetes en cours d'exécution avec des autorisations RBAC suffisantes pour créer, lister et décrire les CronJobs dans votre espace de noms. Utilisez
kubectl auth can-i create cronjobs --namespace <votre-namespace>pour vérifier. - Une image de conteneur exécutable qui se termine avec le statut 0 en cas de succès. Les images qui s'exécutent indéfiniment entraîneront le blocage des Jobs jusqu'à ce que activeDeadlineSeconds soit défini.
- Des quotas de ressources adéquats. Un CronJob qui demande plus de CPU ou de mémoire que ce que permet l'espace de noms ne pourra pas planifier ses Pods.
Une bonne observation de départ consiste à lister tous les CronJobs dans votre espace de noms actuel et leur état de base :
kubectl get cronjobs
Exemple de sortie :
NAME SCHEDULE SUSPEND ACTIVE LAST SCHEDULE AGE
backup-db 0 2 * * * False 0 2h 3d
report-gen */15 * * * * False 1 15m 10d
Pour un examen plus approfondi d'un CronJob spécifique, utilisez describe :
kubectl describe cronjob backup-db
Cela affiche la planification, la politique de concurrence, la dernière exécution réussie et tout événement récent, tel qu'un échec de création de Job en raison d'un dépassement de quota. Avant d'apporter une modification, capturez l'état actuel avec un horodatage :
date -u && kubectl get cronjob backup-db -o yaml > backup-db-before.yaml
Stockez cet instantané dans un endroit sûr. Si la modification ultérieure cause des problèmes, vous pouvez restaurer avec kubectl apply -f backup-db-before.yaml. Évitez de copier des secrets ou des identifiants dans ces instantanés ; s'ils apparaissent, masquez-les avant de les enregistrer.
Chemin de configuration sûr
Les modifications de configuration d'un CronJob doivent suivre un chemin clair : observez la configuration actuelle, testez une copie localement, appliquez le plus petit diff, vérifiez et documentez le retour en arrière. Ne modifiez jamais un CronJob en direct directement avec kubectl edit sauf en cas de véritable urgence ; utilisez plutôt un manifeste versionné dans le contrôle de source.
Voici un manifeste CronJob minimal sûr à utiliser comme point de départ :
apiVersion: batch/v1
kind: CronJob
metadata:
name: hello-world
namespace: default
spec:
schedule: "*/5 * * * *"
concurrencyPolicy: Forbid
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 1
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: hello
image: busybox:1.36
command: ["/bin/sh", "-c", "echo Hello Kubernetes; sleep 10"]
resources:
requests:
cpu: "10m"
memory: "20Mi"
limits:
cpu: "100m"
memory: "50Mi"
Champs clés et leurs valeurs sûres :
- schedule : Utilisez la syntaxe cron standard avec cinq champs. Faites attention aux fuseaux horaires ; à partir de Kubernetes 1.24, vous pouvez définir
timeZone: "America/New_York"pour éviter toute confusion lorsque les nœuds fonctionnent en UTC. - concurrencyPolicy : Définissez-la toujours explicitement. Forbid empêche les exécutions qui se chevauchent si un Job dure plus longtemps que l'intervalle. Replace signifie qu'un nouveau Job remplace un Job en cours, et Allow autorise des exécutions simultanées. Choisissez en fonction de la sécurité de la charge de travail ; Forbid est généralement la valeur par défaut sûre pour les Jobs idempotents.
- startingDeadlineSeconds : Si elle est définie, le contrôleur CronJob tentera de démarrer un Job dans ce nombre de secondes après l'heure planifiée. Si la fenêtre est dépassée, le Job est ignoré. Utile pour les clusters ayant des fenêtres de maintenance.
- successfulJobsHistoryLimit et failedJobsHistoryLimit : Gardez-les bas (par exemple, 3 et 1) pour éviter d'accumuler des objets Job et Pod terminés qui consomment du stockage etcd.
- restartPolicy : Doit être OnFailure ou Never pour les Jobs (pas Always). OnFailure permet des reprises dans le même Job ; Never crée un nouveau Pod à chaque tentative.
Pour tester des modifications sans affecter la production, créez une copie du CronJob dans un espace de noms séparé ou avec un nom différent :
kubectl get cronjob hello-world -o yaml | sed 's/name: hello-world/name: hello-world-test/' | kubectl apply -f -
Ou, si vous utilisez kustomize, maintenez une base et un overlay pour les tests. Après avoir appliqué le CronJob de test, déclenchez manuellement une exécution immédiate pour valider la spécification du Job sans attendre la planification :
kubectl create job --from=cronjob/hello-world-test manual-test-001
Ensuite, inspectez le Job et le Pod résultants :
kubectl get jobs -l job=manual-test-001
kubectl get pods -l job-name=manual-test-001
kubectl logs -l job-name=manual-test-001
Si les journaux montrent la sortie attendue et que le Pod se termine, vous pouvez promouvoir la modification vers le CronJob de production. Enregistrez toujours la modification exacte et la commande de vérification dans votre runbook.
Un exemple de séquence pratique : si vous devez changer la planification de 5 minutes à 10 minutes et mettre à jour l'image du conteneur, faites-le en deux patchs séparés. D'abord, modifiez la planification avec une seule commande kubectl patch :
kubectl patch cronjob hello-world -p '{"spec":{"schedule":"*/10 * * * *"}}'
Vérifiez la planification avec kubectl get cronjob hello-world -o jsonpath='{.spec.schedule}'. Ensuite, modifiez l'image dans une étape séparée après avoir confirmé que la planification fonctionne toujours. Cela limite le rayon d'impact si une modification provoque un comportement inattendu.
Vérification et diagnostics
Une fois qu'un CronJob est déployé ou modifié, vous devez vérifier qu'il s'exécute réellement comme prévu. Les commandes de base pour la vérification sont kubectl get, kubectl describe et kubectl logs, mais vous devez savoir quels libellés et champs examiner car les CronJobs créent des Jobs qui créent des Pods, et chaque niveau a son propre statut.
Commencez par le statut du CronJob :
kubectl get cronjob hello-world -o wide
Regardez les colonnes LAST SCHEDULE et ACTIVE. Si LAST SCHEDULE est vide et ACTIVE est 0 pendant une longue période, le contrôleur peut être suspendu ou la planification peut être invalide. Vérifiez avec :
kubectl get cronjob hello-world -o jsonpath='{.spec.suspend} {.spec.schedule} {.status}'
Sortie attendue : false /5 * {"lastScheduleTime":"2025-03-21T10:00:00Z","active":[]}. Si suspend est true, le CronJob est en pause ; reprenez-le avec kubectl patch cronjob hello-world -p '{"spec":{"suspend":false}}'.
Pour voir tous les Jobs créés par ce CronJob, utilisez le sélecteur de libellé que Kubernetes ajoute automatiquement : job-name est unique par Job, mais vous pouvez lister par le propriétaire CronJob avec :
kubectl get jobs -l job-name
Cependant, une meilleure approche consiste à utiliser le sélecteur de champ pour les références de propriétaire :
kubectl get jobs -o json | jq '.items[] | select(.metadata.ownerReferences[0].name=="hello-world") | .metadata.name'
Ou, si vous avez la CLI Kubernetes kubectl avec le modèle de nom de Job, vous pouvez lister les Jobs récents triés par horodatage de création :
kubectl get jobs --sort-by=.metadata.creationTimestamp | grep hello-world
Pour un Job spécifique, inspectez ses Pods et leurs statuts :
kubectl describe job hello-world-1650000000
Cela affiche des événements comme Created pod: hello-world-1650000000-abcde et Completed. Si le Pod échoue, vous verrez Back-off restarting failed container.
Pour déboguer un Pod en échec, obtenez ses journaux :
kubectl logs <nom-du-pod>
Si le conteneur a redémarré et que vous voulez les journaux de l'instance précédente, utilisez --previous :
kubectl logs <nom-du-pod> --previous
Un échec courant est un code de sortie non nul dû à une erreur de script. Pour voir le code de sortie exact :
kubectl get pod <nom-du-pod> -o jsonpath='{.status.containerStatuses[*].state.terminated.exitCode}'
Pour les problèmes de performance ou de planification, utilisez kubectl describe pod <nom-du-pod> pour vérifier les événements avec des messages comme failed to schedule ou insufficient cpu.
Si le CronJob est censé créer un Job mais que rien n'apparaît, vérifiez la décision du contrôleur en regardant les événements du CronJob :
kubectl describe cronjob hello-world
Les événements peuvent afficher Cannot determine if job needs to be started: too many missed start times si le CronJob a été suspendu pendant une longue période ou si le contrôleur était hors service. Dans ce cas, vous devrez peut-être déclencher manuellement un Job avec kubectl create job --from=cronjob/hello-world manual-$(date +%s) puis enquêter sur la raison pour laquelle le contrôleur ne l'a pas exécuté automatiquement.
Pour une surveillance continue, mettez en place une veille simple sur le statut du CronJob :
kubectl get cronjobs --watch
Cela affichera les modifications au fur et à mesure, ce qui est utile lors d'un déploiement.
Modes de défaillance et récupération
Les CronJobs échouent pour diverses raisons, et chaque échec laisse des indices spécifiques dans le statut de l'objet Kubernetes et les événements. Les modes de défaillance les plus courants et comment s'en remettre sont :
- Job non créé à l'heure planifiée
- Pourquoi : CronJob suspendu, contrôleur hors service, planification invalide ou trop de temps de démarrage manqués.
- Comment détecter :
kubectl get cronjobne montre pas de LAST SCHEDULE récent, etkubectl describe cronjobaffiche "too many missed start times" ou aucun événement. - Récupération : Vérifiez l'état de suspension (
kubectl get cronjob <nom> -o jsonpath='{.spec.suspend}'), vérifiez que le Pod du contrôleur est en cours d'exécution, validez la planification avec un analyseur cron en ligne, et si nécessaire, supprimez le CronJob et réappliquez-le. Ensuite, déclenchez manuellement un Job pour rattraper si approprié.
- Job créé mais Pod échoue immédiatement avec CrashLoopBackOff
- Pourquoi : La commande du conteneur se termine par un code non nul, des dépendances manquantes ou une erreur de configuration.
- Comment détecter :
kubectl get podsmontre l'état CrashLoopBackOff ;kubectl describe podaffiche "Back-off restarting failed container" ;kubectl logs <pod> --previousmontre l'erreur. - Récupération : Corrigez la commande ou l'environnement, puis soit mettez à jour le modèle du CronJob et attendez la prochaine exécution planifiée, soit créez manuellement un Job à partir du CronJob mis à jour pour vérifier. Ensuite, nettoyez les anciens Pods en échec.
- Le Pod s'exécute mais se termine avec succès alors que la tâche réelle échoue
- Pourquoi : Le script à l'intérieur du conteneur ne propage pas les erreurs ; il peut avaler les codes de sortie ou exécuter une commande qui échoue silencieusement.
- Comment détecter : Le Job se termine avec succès mais la sortie attendue est absente ou les données sont corrompues. Vérifiez les journaux de l'application et le code de sortie.
- Récupération : Modifiez la commande du conteneur pour utiliser
set -edans les scripts shell, ou assurez-vous que le programme se termine avec un code non nul en cas d'échec. Utilisez une vérification de santé ou une étape de vérification post-exécution dans le Job. Si possible, exécutez la commande manuellement dans un Pod de test.
- Épuisement des ressources ou dépassement de quota
- Pourquoi : Le CronJob demande plus de CPU/mémoire que le quota de l'espace de noms ne le permet, ou le cluster est surchargé.
- Comment détecter :
kubectl describe cronjobaffiche "failed to create job: pods is forbidden: exceeded quota" ;kubectl get podsmontre Pending. - Récupération : Ajustez les demandes et limites de ressources, augmentez le quota de l'espace de noms, ou planifiez le CronJob pendant les heures creuses. Envisagez également d'utiliser des classes de priorité si disponibles.
- Exécutions qui se chevauchent causant une corruption de données
- Pourquoi : concurrencyPolicy réglée sur Allow et les Jobs prennent plus de temps que l'intervalle, entraînant des exécutions simultanées qui peuvent entrer en conflit.
- Comment détecter : Voir plusieurs Jobs actifs pour le même CronJob (
kubectl get jobsavec ACTIVE > 1), ou des anomalies de données. - Récupération : Réglez concurrencyPolicy sur Forbid ou Replace, reconcevez la charge de travail pour qu'elle soit idempotente, ou augmentez l'intervalle. Si une exécution corrompue s'est produite, restaurez à partir de la sauvegarde et invoquez manuellement un Job de nettoyage.
- Version d'API dépréciée casse l'application
- Pourquoi : Le cluster a été mis à niveau et batch/v1beta1 a été supprimé.
- Comment détecter :
kubectl apply -f cronjob.yamlrenvoie l'erreurno matches for kind "CronJob". - Récupération : Mettez à jour le manifeste vers apiVersion : batch/v1 et réappliquez. Vérifiez les autres dépréciations d'API avec
kubectl api-versions.
Pour chaque échec, documentez la commande exacte qui a montré le problème et la commande qui a confirmé la récupération. Une entrée de runbook simple pourrait ressembler à :
- Symptôme : Le CronJob hello-world ne s'exécute pas,
kubectl get cronjobmontre SUSPEND=True. - Action : Exécutez
kubectl patch cronjob hello-world -p '{"spec":{"suspend":false}}'. - Vérification :
kubectl get cronjob hello-worldmontre SUSPEND=False et après la prochaine planification,kubectl get jobsmontre un nouveau Job.
Liste de contrôle des opérations
Utilisez cette liste de contrôle avant, pendant et après toute modification d'un CronJob pour garantir sécurité et cohérence. Chaque élément comprend une commande concrète et un résultat attendu. Le propriétaire responsable de chaque élément doit être une personne nommée, généralement l'ingénieur responsable du service ou l'opérateur de garde. Revisitez cette liste lors d'une revue hebdomadaire des opérations pour mettre à jour les procédures modifiées.
| Étape | Commande / Action | Résultat attendu | Propriétaire | Fréquence |
|---|---|---|---|---|
| 1. Vérifier la version du cluster | kubectl version --short | Serveur >= 1.21 pour batch/v1 | Responsable DevOps | Avant tout travail sur CronJob |
| 2. Vérifier le contrôleur | kubectl get pods -n kube-system | grep controller-manager | Pod Running | Administrateur du cluster | Hebdomadaire |
| 3. Lister les CronJobs | kubectl get cronjobs -n <ns> | Noms, planifications, état de suspension | Propriétaire du service | Quotidien |
| 4. Inspecter un CronJob | kubectl describe cronjob <nom> -n <ns> | Événements, dernière planification, politique | Propriétaire du service | Avant et après modification |
| 5. Capturer l'état actuel | kubectl get cronjob <nom> -o yaml > backup.yaml | Fichier sauvegardé, aucun secret | Opérateur | Avant toute modification |
| 6. Apporter une modification ciblée | kubectl patch ... ou kubectl apply -f updated.yaml | Modification appliquée avec succès | Propriétaire du service | Au besoin |
| 7. Vérifier la modification | kubectl get cronjob <nom> -o jsonpath='{.spec...}' | Reflète la nouvelle valeur | Opérateur | Immédiatement après |
| 8. Vérifier la création du Job | kubectl get jobs -n <ns> après l'heure planifiée | Un nouveau Job apparaît | Opérateur | Prochaine planification |
| 9. Vérifier le statut du Pod | kubectl get pods -l job-name=<job> | Completed ou Running | Opérateur | Après la création du Job |
| 10. Vérifier les journaux | kubectl logs <nom-du-pod> | Sortie attendue, aucune erreur | Propriétaire du service | Après la fin |
| 11. Surveiller les échecs récurrents | kubectl get jobs --field-selector status.successful=0 | Aucun échec inattendu | Équipe de surveillance | Quotidien |
| 12. Nettoyer les anciens Jobs | Définir des limites d'historique, ou manuellement kubectl delete job <ancien> | Historique limité conservé | Propriétaire du service | Hebdomadaire |
En cas d'urgence, comme un CronJob déclenchant une charge de travail incontrôlable, l'action immédiate est de le suspendre :
kubectl patch cronjob <nom> -p '{"spec":{"suspend":true}}'
Cela arrête la création future de Jobs sans supprimer la définition du CronJob. Vous pouvez ensuite enquêter en toute sécurité et supprimer les Jobs actifs si nécessaire : kubectl delete jobs --all -n <ns> supprimerait tous les Jobs, mais soyez prudent et utilisez des sélecteurs de libellés pour cibler uniquement ceux du CronJob problématique.
Pièges courants et comment les éviter
Même les utilisateurs expérimentés de Kubernetes tombent dans des pièges avec les CronJobs. Voici les pièges les plus courants, pourquoi ils surviennent et comment les éviter ou s'en remettre.
Piège 1 : Ignorer les fuseaux horaires Les planifications Kubernetes utilisent le fuseau horaire du kube-controller-manager, qui est généralement UTC. Si vous voulez qu'un Job s'exécute à 2 heures du matin heure locale mais le planifiez comme 0 2 *, il s'exécutera à 2 heures UTC. À partir de Kubernetes 1.24, vous pouvez définir spec.timeZone pour éviter toute confusion. Testez toujours la planification avec kubectl get cronjob -o yaml et vérifiez le dernier temps de planification par rapport à votre horloge locale.
Piège 2 : Ne pas définir concurrencyPolicy Si vous omettez concurrencyPolicy, il prend par défaut Allow, ce qui peut conduire à des Jobs qui se chevauchent et corrompent les données si le Job n'est pas idempotent. Par exemple, un Job de sauvegarde qui s'exécute toutes les heures mais prend 90 minutes aura des sauvegardes qui se chevauchent, verrouillant potentiellement la base de données. Définissez concurrencyPolicy: Forbid sauf si vous avez absolument besoin d'exécutions simultanées.
Piège 3 : Laisser successfulJobsHistoryLimit trop élevé Par défaut, successfulJobsHistoryLimit est 3 et failedJobsHistoryLimit est 1, mais si vous avez des centaines de CronJobs, les objets Job et Pod terminés peuvent consommer d'énormes quantités de stockage etcd et encombrer kubectl get jobs. Définissez ces limites à des nombres faibles ou zéro si vous n'avez pas besoin de l'historique.
Piège 4 : Mal comprendre restartPolicy Le modèle de Pod du Job doit définir restartPolicy sur OnFailure ou Never. Si vous le définissez sur Always (la valeur par défaut pour les Deployments), le contrôleur de Job le rejettera avec une erreur. Spécifiez-le toujours explicitement.
Piège 5 : Utiliser une image de conteneur qui ne se termine pas Si votre conteneur exécute un processus de longue durée (comme un serveur web) au lieu d'une tâche de courte durée, le Job ne se terminera jamais et finira par atteindre activeDeadlineSeconds s'il est défini, sinon il restera bloqué indéfiniment. Assurez-vous que votre conteneur exécute une commande finie.
Piège 6 : Modifier un CronJob en place sans test Utiliser kubectl edit pour modifier directement une planification ou une image est risqué car vous pouvez introduire une faute de frappe ou oublier d'enregistrer la modification. Gardez toujours les manifestes dans le contrôle de version et appliquez avec kubectl apply. Pour des expériences rapides, créez une copie dans un espace de noms de test.
Piège 7 : Ne pas tenir compte des Jobs manqués pendant la maintenance Si vous suspendez un CronJob pour une fenêtre de maintenance puis le reprenez, vous risquez de manquer les Jobs qui auraient dû s'exécuter pendant la suspension si startingDeadlineSeconds n'est pas défini. Demandez-vous si vous devez déclencher manuellement des Jobs de rattrapage.
Conclusion
Les CronJobs Kubernetes sont puissants mais exigent des opérations disciplinées. Les commandes et modèles de ce guide vous donnent une base solide pour inspecter, configurer, vérifier et récupérer des Jobs planifiés en toute sécurité. Rappelez-vous les principes fondamentaux : observez avant de modifier, faites de petites modifications réversibles, protégez les données sensibles et vérifiez toujours le résultat.
Commencez par une petite modification sûre : listez tous vos CronJobs (kubectl get cronjobs), choisissez-en un avec une charge de travail non critique, capturez son YAML actuel et testez une modification de planification dans un espace de noms séparé. Exécutez les commandes de vérification de la section 4 et documentez les résultats. Au fur et à mesure que vous gagnez en confiance, étendez à des configurations plus complexes comme les fuseaux horaires et les politiques de concurrence.
En suivant la liste de contrôle opérationnelle et en étant conscient des pièges courants, vous pouvez maintenir vos charges planifiées saines, éviter les échecs inattendus et récupérer rapidement en cas de problème.