Intro
Cette version française explique Kubernetes CronJobs configuration mistakes with practical examples avec le même objectif pratique que l article source : aider le lecteur à comprendre le contexte, les décisions à prendre et les points à vérifier avant de passer à l action.
Les Kubernetes CronJobs sont parfaits pour des tâches récurrentes comme des exports nocturnes, la rotation de logs ou des nettoyages périodiques. Pourtant, de petites erreurs de configuration peuvent provoquer des exécutions qui se chevauchent, des horaires manqués, un trop-plein d'objets d'historique, ou encore des Jobs qui ne démarrent jamais. Ce guide pratique propose des manières sûres de configurer, vérifier et dépanner les CronJobs, avec des exemples concrets en YAML et des commandes kubectl. Vous apprendrez à inventorier votre environnement, choisir des valeurs par défaut prudentes, déployer des changements sans surprise, valider le comportement, récupérer après un échec et appliquer une checklist pour rendre les opérations répétables.
Inventaire de version et d'environnement
Avant tout changement, capturez une vue minimale mais complète de l'environnement. La version et la topologie déterminent les champs disponibles et le comportement du contrôleur.
Commandes
- Vérifier les versions client et serveur :
kubectl version --short
- Lister les groupes d'API CronJob pour confirmer le support :
kubectl api-resources | grep -i cronjob
- Inventorier les espaces de noms et les CronJobs existants :
kubectl get cronjobs.batch -A
- Inspecter en profondeur un CronJob précis :
kubectl get cronjob my-cron -n myns -o yaml
Ce qu'il faut capturer (exemples construits ; à vérifier sur votre cluster) :
| Élément à capturer | Commande d'exemple | Valeur d'exemple |
|---|---|---|
| Version serveur Kubernetes | kubectl version --short | Server Version: v1.28.3 |
| API CronJob | kubectl api-resources | batch/v1 CronJob |
| Support du fuseau horaire | kubectl explain cronjob.spec.timeZone | Champ présent en v1.28 |
| Namespace et nom | n/a | myns/my-cron |
| Santé du contrôleur | kubectl get deploy -n kube-system | cronjob controller available |
Notes
- batch/v1 CronJob est largement disponible dans les clusters modernes. Si votre cluster est très ancien, confirmez si vous êtes en batch/v1beta1 et planifiez une migration.
- Le champ timeZone est disponible dans les versions récentes. S'il n'apparaît pas dans kubectl explain, ne l'utilisez pas.
Chemin de configuration sûr
Cette section couvre des erreurs fréquentes de configuration et des choix sûrs, avec du YAML prêt à copier et adapter.
Erreur 1 : Autoriser par défaut des exécutions qui se chevauchent
Symptôme : une nouvelle exécution démarre alors que la précédente est encore active, entraînant travail en double ou contention.
Pourquoi : concurrencyPolicy vaut Allow par défaut.
Choix sûr : préférez Forbid pour les tâches idempotentes qui ne doivent pas se chevaucher. N'utilisez Replace que si la nouvelle exécution doit préempter l'ancienne.
Mauvais exemple (chevauchements probables) :
apiVersion: batch/v1
kind: CronJob
metadata:
name: overlap-prone
spec:
schedule: "* * * * *" # chaque minute
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: worker
image: busybox:1.36
command: ["/bin/sh","-c","echo starting; sleep 90; echo done"]
Correctif :
spec:
concurrencyPolicy: Forbid
schedule: "* * * * *"
jobTemplate:
spec:
backoffLimit: 1
template:
spec:
restartPolicy: OnFailure
containers:
- name: worker
image: busybox:1.36
command: ["/bin/sh","-c","echo starting; sleep 90; echo done"]
Résultat attendu : lorsqu'une minute passe alors que l'exécution précédente dort toujours, la nouvelle exécution est ignorée. Vérifiez avec :
kubectl get jobs -l cronjob-name=overlap-prone
Erreur 2 : restartPolicy invalide dans le template Job
Symptôme : le Job ne programme aucun Pod ; vous voyez des erreurs de validation, par exemple restartPolicy: Always n'est pas autorisé pour les Jobs.
Choix sûr : définissez restartPolicy sur OnFailure (le plus courant) ou Never.
Extrait correctif :
spec:
jobTemplate:
spec:
template:
spec:
restartPolicy: OnFailure # pas Always
Erreur 3 : startingDeadlineSeconds trop petit ⇒ horaires manqués
Symptôme : le CronJob ne crée pas de Job après une brève panne du plan de contrôle, une lenteur du registre d'images ou un décalage d'horloge.
Pourquoi : startingDeadlineSeconds limite la fenêtre pendant laquelle les exécutions manquées sont démarrées. Trop petit ⇒ abandon trop rapide.
Choix sûr : fixez une fenêtre raisonnable, par ex. quelques minutes pour des fréquences élevées, jusqu'à une heure pour les tâches horaires/quotidiennes.
Exemple pour un job horaire :
spec:
schedule: "0 * * * *"
startingDeadlineSeconds: 3600 # rattrapage possible sur 1h
Erreur 4 : Encoder le fuseau horaire dans la crontab ou dépendre de l'heure des nœuds
Symptôme : déclenchements à la mauvaise heure locale ou dérive lors du DST.
Choix sûr :
- Si votre cluster le supporte, utilisez
spec.timeZonepour fixer explicitement le fuseau horaire du job. - Sinon, programmez en UTC et convertissez les heures métier en UTC dans l'expression.
Exemple avec timeZone :
spec:
schedule: "0 2 * * *"
timeZone: "America/New_York" # à n'utiliser que si supporté par votre version
Erreur 5 : Rétention d'historique excessive ou insuffisante
Symptôme : bruit API dû à des centaines d'anciens Jobs, ou au contraire pas assez d'historique pour dépanner.
Choix sûr : ajustez par CronJob.
Exemple :
spec:
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 5
jobTemplate:
spec:
ttlSecondsAfterFinished: 86400 # optionnel si le TTL controller est activé
Erreur 6 : Reprises illimitées sur Pods en échec
Symptôme : Pods qui retentent indéfiniment et se chevauchent avec les déclenchements suivants.
Choix sûr : limitez les reprises et la durée d'exécution.
Exemple :
spec:
jobTemplate:
spec:
backoffLimit: 2
activeDeadlineSeconds: 1200 # 20 minutes de plafond par job
template:
spec:
restartPolicy: OnFailure
Erreur 7 : ServiceAccount ou permissions manquantes
Symptôme : Pods démarrent mais échouent avec des erreurs RBAC lorsqu'ils doivent appeler l'API ou accéder à des secrets.
Choix sûr : assignez un ServiceAccount minimal et un Role/RoleBinding dans le même namespace.
Exemple :
apiVersion: v1
kind: ServiceAccount
metadata:
name: cron-sa
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: cron-reader
rules:
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get","list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: cron-reader-binding
subjects:
- kind: ServiceAccount
name: cron-sa
roleRef:
kind: Role
name: cron-reader
apiGroup: rbac.authorization.k8s.io
---
apiVersion: batch/v1
kind: CronJob
metadata:
name: rbac-aware
spec:
schedule: "*/15 * * * *"
concurrencyPolicy: Forbid
jobTemplate:
spec:
template:
spec:
serviceAccountName: cron-sa
restartPolicy: OnFailure
containers:
- name: worker
image: busybox:1.36
command: ["/bin/sh","-c","kubectl get cm || echo no kubectl here"]
Erreur 8 : Toujours tirer l'image (Always) pour des images stables
Symptôme : pulls inutiles augmentant la latence ou échouant si le registre est indisponible.
Choix sûr : si vous pointez vers un digest ou un tag immuable, utilisez IfNotPresent.
containers:
- name: worker
image: myrepo/tool@sha256:...
imagePullPolicy: IfNotPresent
Habitudes de déploiement plus sûres
- Exécution de test : utilisez
kubectl create job --from=cronjob/<name> <test-name>pour simuler immédiatement une exécution planifiée. - Validation en dry-run :
kubectl apply --dry-run=server -f cron.yamlpour détecter les erreurs de schéma. - Changement progressif : introduisez un nouveau CronJob suffixé
-v2, faites-le tourner en parallèle avecsuspend=falseet mettez l'ancien ensuspend=truepour une fenêtre contrôlée, puis finalisez le basculement. - Suspension temporaire : mettez un CronJob en pause si nécessaire :
kubectl patch cronjob my-cron -p '{"spec":{"suspend":true}}'
Vérification et diagnostics
Contrôles de base
- Santé du contrôleur et activité de planification :
kubectl get cronjob my-cron -n myns
kubectl describe cronjob my-cron -n myns
- Lister les Jobs créés par un CronJob :
kubectl get jobs -n myns -l cronjob-name=my-cron
- Inspecter les événements et logs du Pod du dernier Job :
JOB=$(kubectl get jobs -n myns -l cronjob-name=my-cron -o jsonpath='{.items[-1:].metadata.name}')
kubectl get pods -n myns -l job-name=$JOB
POD=$(kubectl get pods -n myns -l job-name=$JOB -o jsonpath='{.items[0].metadata.name}')
kubectl logs -n myns $POD
- Valider la crontab et les champs :
kubectl apply --dry-run=server -f cron.yaml
kubectl explain cronjob.spec
Tableau récapitulatif (erreurs, symptômes, vérifs, correctifs)
| Erreur | Ce que vous voyez | Vérifier avec | Correctif recommandé |
|---|---|---|---|
| Exécutions qui se chevauchent (Allow) | Plusieurs Jobs actifs | kubectl get jobs -l cronjob-name=NAME | concurrencyPolicy: Forbid ou Replace |
| restartPolicy invalide | 0 Pod pour le Job | kubectl apply --dry-run=server -f | OnFailure ou Never |
| startingDeadlineSeconds trop petit | Pas de rattrapage | kubectl describe cronjob | Augmenter la fenêtre |
| timeZone non supporté | Champ ignoré / apply échoue | kubectl explain cronjob.spec.timeZone | Utiliser l'UTC ou mettre à niveau |
| Historique excessif | Centaines de Jobs | kubectl get jobs -l cronjob-name=NAME | Réduire les limites d'historique |
| Reprises non bornées | Longues erreurs, chevauchement | kubectl describe job | backoffLimit + activeDeadlineSeconds |
| RBAC manquant | Forbidden dans les logs | kubectl logs; kubectl auth can-i | ServiceAccount + Role minimal |
Résultats attendus d'un CronJob sain
kubectl get cronjobaffiche LAST SCHEDULE avec un horodatage récent conforme à votre stratégie horaire.kubectl get jobsmontre un Job réussi par période attendue, ou un en cours. Les statuts passent de 0/1 à 1/1.kubectl logsdu dernier Pod contient la sortie prévue, sans boucle de crash.
Déclenchement manuel et rattrapage
Pour tester sans attendre l'horaire :
- Créer un job ponctuel depuis le template du CronJob :
kubectl create job --from=cronjob/my-cron my-cron-adhoctest -n myns
- Étiqueter et tracer ce job ad hoc :
kubectl label job my-cron-adhoctest purpose=adhoc -n myns
- Supprimer le job ad hoc après validation :
kubectl delete job my-cron-adhoctest -n myns
Modes de panne et reprise
Contrôleur ou API indisponibles
- Symptôme : aucun nouveau Job aux heures prévues. LAST SCHEDULE n'avance plus.
- Cause : perturbation du plan de contrôle ou contrôleur CronJob à l'arrêt.
- Reprise : après rétablissement, assurez-vous que
startingDeadlineSecondsest suffisant pour rattraper. Vérifiez la santé du déploiement du contrôleur.
Décalage d'horloge et confusion de fuseau
- Symptôme : dérive par rapport au temps métier ou au DST.
- Reprise : préférez l'UTC ou utilisez
spec.timeZonesi supporté. Comparez LAST SCHEDULE à une horloge UTC de référence.
Erreurs de pull d'image
- Symptôme :
ImagePullBackOffsur les Pods. - Reprise : vérifiez imagePullSecrets et disponibilité du registre ; envisagez
IfNotPresentpour des images immuables. Relancez avec un job ad hoc.
Échecs RBAC
- Symptôme : erreurs Forbidden dans les logs lors d'accès API.
- Reprise : attachez un ServiceAccount minimal, un Role et un RoleBinding. Testez avec :
kubectl auth can-i get configmaps --as=system: serviceaccount: myns: cron-sa -n myns
Crash loops ou dépassements de délais
- Symptôme : Pods redémarrent ou dépassent la fenêtre d'exécution.
- Reprise : fixez
backoffLimit, ajoutezactiveDeadlineSeconds, et assurez-vous que la tâche est idempotente. Inspectez les logs du conteneur.
Encombrement de l'historique
- Symptôme : grand nombre de Jobs terminés ralentissant les listes.
- Reprise : baissez
successfulJobsHistoryLimitetfailedJobsHistoryLimit; activezttlSecondsAfterFinishedsi le contrôleur TTL est disponible.
Arrêt d'urgence et reprise
- Suspendre temporairement :
kubectl patch cronjob my-cron -n myns -p '{"spec":{"suspend":true}}'
- Reprendre :
kubectl patch cronjob my-cron -n myns -p '{"spec":{"suspend":false}}'
Stratégie de rollback
- Conservez un manifeste de référence pour chaque CronJob. Pour revenir en arrière, réappliquez explicitement le fichier précédent :
kubectl apply -f cronjob-prev.yaml
- Si un champ impose une recréation (rare pour les CronJobs), suspendez l'objet actuel, créez le remplaçant avec un nom distinct, validez une exécution de test, puis supprimez l'ancien.
Vérifications après reprise
- LAST SCHEDULE avance de nouveau.
- Un seul Job par période lorsque
concurrencyPolicyvautForbid. - L'historique conserve assez de contexte pour l'analyse sans excès.
Checklist d'exploitation
Inventaire et prérequis
- [ ]
kubectl version --shortcapturé (client et serveur) - [ ] Groupe d'API CronJob confirmé (
batch/v1) - [ ] Présence de
timeZonevérifiée viakubectl explain - [ ] Namespace et noms consignés
Sécurité de la configuration
- [ ]
concurrencyPolicy=Forbid(ouReplaceavec justification) - [ ]
restartPolicy=OnFailureouNever - [ ]
startingDeadlineSecondsdimensionné à la tolérance - [ ]
successfulJobsHistoryLimitetfailedJobsHistoryLimitajustés - [ ]
backoffLimitetactiveDeadlineSecondsraisonnables - [ ]
serviceAccountNamedéfini si besoin API/secret, RBAC minimal - [ ] Référence d'image immuable,
imagePullPolicyapproprié - [ ]
suspendpositionné pour les fenêtres de pause/rollout
Étapes de validation
- [ ]
kubectl apply --dry-run=serverpasse - [ ]
kubectl describe cronjobmontre la prochaine échéance attendue - [ ] Job ad hoc créé et terminé pour les nouveautés/risques
Observation et diagnostic
- [ ] LAST SCHEDULE se met à jour après changement
- [ ] Nombre prévu de Jobs actifs par période uniquement
- [ ] Logs des Pods inspectés et cohérents
Récupération et rollback
- [ ] Manifeste connu-bon disponible pour rollback immédiat
- [ ] Capacité à suspendre/reprendre vérifiée
- [ ] Nettoyage des Jobs ad hoc ou en échec effectué
Signaux et actions (exemples)
| Signal | Interprétation | Action suivante |
|---|---|---|
| LAST SCHEDULE n'avance pas | Problème de contrôleur/horaire | Vérifier la santé du contrôleur ; revoir startingDeadlineSeconds |
| Nombreux Jobs concurrents | Chevauchement autorisé | concurrencyPolicy: Forbid ou Replace |
| ImagePullBackOff | Registre/policy de pull | Vérifier secrets ; IfNotPresent pour tags immuables |
| Forbidden dans les logs | Manque RBAC | ServiceAccount ; Role/Binding minimal |
| Jobs sans fin | Reprises non bornées | backoffLimit + activeDeadlineSeconds |
Conclusion
Les CronJobs sont fiables lorsqu'ils sont configurés avec méthode. L'approche la plus sûre consiste à inventorier les versions et champs disponibles, empêcher les chevauchements par défaut, borner les reprises et les délais, et régler la rétention pour conserver un historique utile sans l'encombrement. Validez chaque changement avec le dry-run côté serveur et un job ponctuel avant d'activer l'horaire, et conservez un plan de rollback simple en gardant des manifestes connus-bons. Avec ces pratiques et la checklist ci-dessus, vous réduisez les surprises opérationnelles et gardez des tâches récurrentes prévisibles.