
Un Job exécute une tâche jusqu'à sa complétion : migration de base de données, traitement batch, génération de rapport. Un CronJob crée des objets Job selon un planning ; ce sont ensuite ces Jobs qui exécutent les Pods nécessaires.
Contrairement aux Pods gérés par un Deployment qui tournent en continu, les Jobs s'arrêtent une fois leur travail terminé. Kubernetes garantit que la tâche sera complétée, même en cas d'échec temporaire.
Prérequis : un cluster Kubernetes avec kubectl configuré.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Créer un Job pour une tâche ponctuelle
- Configurer le comportement en cas d'échec (backoffLimit, activeDeadlineSeconds)
- Paralléliser l'exécution avec completions et parallelism
- Planifier des tâches récurrentes avec CronJob
- Maîtriser les paramètres de production (concurrencyPolicy, suspend, timeZone)
- Appliquer les bonnes pratiques pour le nettoyage automatique
Job ou Deployment ?
Section intitulée « Job ou Deployment ? »Le choix se joue sur une seule question : la charge a-t-elle une fin ? Un Deployment redémarre indéfiniment ses Pods, c'est exactement ce qu'il faut pour un service web mais c'est catastrophique pour un script de migration, qui serait relancé en boucle après chaque succès. Le tableau ci-dessous vous aide à trancher avant d'écrire le moindre YAML.
| Ressource | Comportement | Cas d'usage |
|---|---|---|
| Deployment | Maintient N Pods en continu | Applications web, API, services |
| Job | Exécute jusqu'à complétion, puis s'arrête | Migration, backup, traitement batch |
| CronJob | Crée un Job selon un planning | Tâches planifiées, maintenance |
Créer votre premier Job
Section intitulée « Créer votre premier Job »Un Job crée un Pod, exécute la tâche, et se termine une fois le travail fait :
apiVersion: batch/v1kind: Jobmetadata: name: hello-jobspec: template: spec: containers: - name: hello image: busybox command: ["echo", "Hello from Kubernetes Job"] restartPolicy: Neverkubectl apply -f hello-job.yamlAttendez la fin de l'exécution :
kubectl wait --for=condition=complete job/hello-job --timeout=60sVérifiez le statut :
kubectl get jobsNAME STATUS COMPLETIONS DURATION AGEhello-job Complete 1/1 4s 10sObserver et inspecter un Job
Section intitulée « Observer et inspecter un Job »Un Job terminé ne dit rien de lui-même : c'est le Pod qu'il a créé qui porte la sortie du programme. Trois commandes suffisent pour tout savoir : kubectl logs pour la sortie, kubectl describe pour le compteur de tentatives et les événements, kubectl get pods pour retrouver les Pods même après la fin du Job. Gardez en tête que ces informations disparaissent avec le Job si vous avez activé un nettoyage automatique.
Voir les logs
Section intitulée « Voir les logs »kubectl logs job/<nom> sélectionne automatiquement un Pod du Job, vous n'avez pas besoin de connaître son nom généré.
kubectl logs job/hello-jobHello from Kubernetes JobDétails du Job
Section intitulée « Détails du Job »kubectl describe affiche la configuration effective et surtout la liste des événements, où apparaissent les échecs de planification et les redémarrages.
kubectl describe job hello-jobInformations clés :
- Completions : nombre d'exécutions réussies requises
- Parallelism : nombre de Pods exécutés en parallèle
- Active / Succeeded / Failed : état des Pods
Lister les Pods créés par un Job
Section intitulée « Lister les Pods créés par un Job »Kubernetes appose le label job-name sur chaque Pod créé, ce qui permet de tous les retrouver, y compris ceux qui ont échoué avant une nouvelle tentative.
kubectl get pods -l job-name=hello-jobAnatomie d'un Job
Section intitulée « Anatomie d'un Job »Le manifeste minimal vu plus haut fonctionne, mais il laisse Kubernetes appliquer ses valeurs par défaut : six tentatives, aucune limite de durée, aucun nettoyage. L'exemple ci-dessous montre les quatre champs qui transforment un Job jetable en tâche exploitable en production. Tous se déclarent au niveau du spec du Job, pas dans le template du Pod, une confusion fréquente qui fait rejeter le manifeste par l'API.
apiVersion: batch/v1kind: Jobmetadata: name: backup-databasespec: backoffLimit: 3 # Tentatives max en cas d'échec activeDeadlineSeconds: 300 # Timeout global (5 min) ttlSecondsAfterFinished: 3600 # Nettoyage auto après 1h template: spec: containers: - name: backup image: postgres:16 command: ["pg_dump", "-h", "db-host", "-U", "admin", "mydb"] restartPolicy: Never| Champ | Description | Valeur par défaut |
|---|---|---|
backoffLimit | Nombre de tentatives avant abandon | 6 (sauf Indexed Jobs) |
activeDeadlineSeconds | Durée max d'exécution du Job | Illimitée |
ttlSecondsAfterFinished | Nettoyage automatique après N secondes | Non activé |
completions | Nombre d'exécutions réussies requises | 1 |
parallelism | Nombre de Pods simultanés | 1 |
Comportement en cas d'échec
Section intitulée « Comportement en cas d'échec »Un Job qui échoue ne s'arrête pas tout de suite : Kubernetes relance le travail jusqu'à épuisement du quota de tentatives. Trois réglages encadrent ce comportement, et ils répondent à des questions différentes : combien de fois réessayer (backoffLimit), pendant combien de temps au maximum (activeDeadlineSeconds), et comment le Pod lui-même est relancé (restartPolicy). Sans eux, une tâche cassée peut consommer des ressources pendant des heures avant que quelqu'un ne s'en aperçoive.
backoffLimit : limiter les tentatives
Section intitulée « backoffLimit : limiter les tentatives »Si un Pod échoue, Kubernetes le relance avec un délai exponentiel (10s, 20s, 40s...). backoffLimit stoppe les tentatives :
spec: backoffLimit: 3 # Abandon après 3 échecsLe Job passe en statut Failed avec la raison BackoffLimitExceeded.
Pour la plupart des Jobs, backoffLimit vaut 6 par défaut. Les Indexed Jobs disposent aussi d'options avancées comme backoffLimitPerIndex.
activeDeadlineSeconds : timeout global
Section intitulée « activeDeadlineSeconds : timeout global »Empêche un Job de tourner indéfiniment :
spec: activeDeadlineSeconds: 300 # 5 minutes maxSi le temps est dépassé, tous les Pods sont arrêtés et le Job passe en Failed avec la raison DeadlineExceeded.
restartPolicy : comportement du Pod
Section intitulée « restartPolicy : comportement du Pod »Ce champ décide de ce qui est recréé après un échec : un Pod entièrement neuf, ou le même conteneur relancé dans le Pod existant. La différence compte dès que la tâche écrit dans un volume emptyDir, car ce volume est perdu avec le Pod.
| Valeur | Comportement |
|---|---|
Never | Kubernetes crée un nouveau Pod à chaque tentative |
OnFailure | Le même Pod redémarre (garde les volumes locaux) |
Jobs parallèles
Section intitulée « Jobs parallèles »Deux champs pilotent le parallélisme : completions fixe le nombre total d'exécutions réussies attendues, parallelism le nombre de Pods qui tournent en même temps. Kubernetes se charge de maintenir le second jusqu'à atteindre le premier. Attention, les Pods d'un même Job reçoivent tous la même commande : sans mécanisme de partage du travail, ils traiteraient les mêmes données.
apiVersion: batch/v1kind: Jobmetadata: name: parallel-jobspec: completions: 10 # 10 exécutions au total parallelism: 3 # 3 Pods en parallèle template: spec: containers: - name: worker image: busybox command: ["sh", "-c", "echo 'Processing...' && sleep 5"] restartPolicy: NeverKubernetes lance 3 Pods en parallèle, puis en lance de nouveaux dès qu'un se termine, jusqu'à atteindre 10 complétions.
Jobs indexés
Section intitulée « Jobs indexés »En mode Indexed, chaque Pod reçoit un index de complétion unique. Kubernetes l'expose via l'annotation batch.kubernetes.io/job-completion-index, et la variable d'environnement JOB_COMPLETION_INDEX est disponible dans le conteneur :
spec: completions: 5 parallelism: 5 completionMode: Indexed template: spec: containers: - name: worker image: busybox command: ["sh", "-c", "echo Processing chunk $JOB_COMPLETION_INDEX"]Idéal pour traiter des partitions de données (chunk 0, chunk 1...).
Nettoyage automatique avec TTL
Section intitulée « Nettoyage automatique avec TTL »Sans nettoyage, les Jobs terminés s'accumulent. ttlSecondsAfterFinished les supprime automatiquement :
spec: ttlSecondsAfterFinished: 3600 # Supprimé 1h après complétionCronJobs : planifier des tâches récurrentes
Section intitulée « CronJobs : planifier des tâches récurrentes »Un CronJob crée automatiquement des Jobs selon un planning. Il ne lance jamais de Pod directement : à chaque échéance, il fabrique un nouvel objet Job, qui crée à son tour les Pods. C'est pour cette raison que le manifeste contient un jobTemplate qui enveloppe le template de Pod, avec deux niveaux de spec imbriqués.
apiVersion: batch/v1kind: CronJobmetadata: name: daily-backupspec: schedule: "0 2 * * *" # Tous les jours à 2h du matin timeZone: "Europe/Paris" # Fuseau horaire explicite successfulJobsHistoryLimit: 3 # Garde les 3 derniers Jobs réussis failedJobsHistoryLimit: 1 # Garde le dernier Job échoué jobTemplate: spec: template: spec: containers: - name: backup image: postgres:16 command: ["pg_dump", "-h", "db-host", "-U", "admin", "mydb"] restartPolicy: NeverSyntaxe cron
Section intitulée « Syntaxe cron »Le champ schedule reprend la syntaxe cron classique à cinq champs, séparés par des espaces, de la minute vers le jour de la semaine. Une expression invalide est rejetée à la création de l'objet, mais une expression valide qui ne correspond pas à votre intention passera sans avertissement.
| Expression | Signification |
|---|---|
0 * * * * | Chaque heure à :00 |
0 2 * * * | Tous les jours à 2h |
0 0 * * 0 | Chaque dimanche à minuit |
*/15 * * * * | Toutes les 15 minutes |
0 9-17 * * 1-5 | Chaque heure de 9h à 17h, lundi à vendredi |
Observer les CronJobs
Section intitulée « Observer les CronJobs »La sortie de kubectl get cronjobs condense l'essentiel : le planning appliqué, le fuseau retenu, l'état de suspension, le nombre de Jobs actifs et la date de la dernière exécution déclenchée.
kubectl get cronjobsNAME SCHEDULE TIMEZONE SUSPEND ACTIVE LAST SCHEDULE AGEdaily-backup 0 2 * * * Europe/Paris False 0 12h 3dParamètres CronJob de production
Section intitulée « Paramètres CronJob de production »Un CronJob qui fonctionne en lab peut poser problème en production dès que la tâche dure plus longtemps que l'intervalle prévu, ou que le cluster était indisponible à l'heure dite. Quatre champs répondent à ces situations : concurrencyPolicy pour les chevauchements, startingDeadlineSeconds pour les exécutions manquées, suspend pour la mise en pause, timeZone pour l'heure locale. Les valeurs par défaut privilégient la souplesse plutôt que la sécurité, à vous de les resserrer.
concurrencyPolicy : gérer les chevauchements
Section intitulée « concurrencyPolicy : gérer les chevauchements »Ce champ répond à une question précise : que faire si l'exécution précédente n'est pas terminée quand la suivante doit démarrer ?
| Valeur | Comportement |
|---|---|
Allow | Autorise plusieurs Jobs simultanés (par défaut) |
Forbid | Skip l'exécution si un Job est en cours |
Replace | Arrête l'ancien Job et en démarre un nouveau |
spec: concurrencyPolicy: Forbid # Recommandé pour les backupsstartingDeadlineSeconds : délai de démarrage
Section intitulée « startingDeadlineSeconds : délai de démarrage »Si le cluster était indisponible à l'heure prévue, combien de temps après peut-on encore démarrer ?
spec: startingDeadlineSeconds: 3600 # 1h de délai maxSi startingDeadlineSeconds est défini, Kubernetes saute une exécution qui a dépassé ce délai. Si ce champ n'est pas défini, les occurrences n'ont pas de date limite de démarrage.
suspend : mettre en pause
Section intitulée « suspend : mettre en pause »Arrêter temporairement les exécutions sans supprimer le CronJob :
kubectl patch cronjob daily-backup -p '{"spec":{"suspend":true}}'Réactiver :
kubectl patch cronjob daily-backup -p '{"spec":{"suspend":false}}'timeZone : fuseau horaire explicite
Section intitulée « timeZone : fuseau horaire explicite »Depuis Kubernetes 1.27, vous pouvez spécifier le fuseau horaire :
spec: timeZone: "Europe/Paris"Sans timeZone, le planning utilise le fuseau horaire du kube-controller-manager, souvent UTC dans de nombreux clusters.
Déclencher manuellement un CronJob
Section intitulée « Déclencher manuellement un CronJob »Attendre 2 h du matin pour vérifier qu'un backup fonctionne n'a aucun sens. L'option --from crée un Job indépendant à partir du jobTemplate du CronJob : le planning n'est pas modifié, et le Job manuel porte le nom que vous lui donnez, ce qui évite de le confondre avec les exécutions automatiques.
kubectl create job --from=cronjob/daily-backup manual-backupSuivez l'exécution :
kubectl wait --for=condition=complete job/manual-backup --timeout=300skubectl logs job/manual-backupCas réels
Section intitulée « Cas réels »Les trois manifestes qui suivent reprennent les champs déjà vus, mais avec des valeurs choisies pour l'usage visé. Comparez-les : une migration se veut non rejouable et rapide, un backup se veut fiable et historisé, une purge se veut discrète. Le même objet Kubernetes se configure très différemment selon la conséquence d'une exécution en double.
Migration de base de données
Section intitulée « Migration de base de données »Une migration rejouée peut corrompre les données : le backoffLimit est ramené au strict minimum, et l'activeDeadlineSeconds évite qu'un verrou de base laisse le Job en cours indéfiniment. Les identifiants arrivent par envFrom depuis un Secret, jamais en clair dans le manifeste.
apiVersion: batch/v1kind: Jobmetadata: name: db-migrationspec: backoffLimit: 1 # Pas de retry sur migration activeDeadlineSeconds: 600 # 10 min max ttlSecondsAfterFinished: 86400 template: spec: containers: - name: migrate image: myapp:latest command: ["./manage.py", "migrate", "--no-input"] envFrom: - secretRef: name: db-credentials restartPolicy: NeverSauvegarde planifiée
Section intitulée « Sauvegarde planifiée »Deux backups qui écrivent en même temps dans le même volume produisent une archive inutilisable : concurrencyPolicy: Forbid interdit ce cas. L'historique conservé (sept Jobs réussis) permet de retrouver la trace d'un échec passé, et le dump est écrit dans un PersistentVolumeClaim, pas dans le conteneur, qui disparaît avec le Pod.
apiVersion: batch/v1kind: CronJobmetadata: name: postgres-backupspec: schedule: "0 3 * * *" timeZone: "Europe/Paris" concurrencyPolicy: Forbid successfulJobsHistoryLimit: 7 # 1 semaine d'historique failedJobsHistoryLimit: 3 jobTemplate: spec: template: spec: containers: - name: backup image: postgres:16@sha256:33f923b05f64ca54ac4401c01126a6b92afe839a0aa0a52bc5aeb5cc958e5f20 command: - /bin/sh - -c - pg_dump -h $DB_HOST -U $DB_USER $DB_NAME | gzip > /backup/$(date +%Y%m%d).sql.gz envFrom: - secretRef: name: db-credentials volumeMounts: - name: backup-volume mountPath: /backup volumes: - name: backup-volume persistentVolumeClaim: claimName: backup-pvc restartPolicy: NeverPurge de données anciennes
Section intitulée « Purge de données anciennes »Une purge hebdomadaire tolère un décalage, elle est donc programmée le dimanche à 4 h, hors des créneaux de charge. Notez la position du ttlSecondsAfterFinished : il est placé dans le jobTemplate, car c'est bien le Job créé qui doit être nettoyé, pas le CronJob lui-même.
apiVersion: batch/v1kind: CronJobmetadata: name: data-cleanupspec: schedule: "0 4 * * 0" # Chaque dimanche à 4h timeZone: "Europe/Paris" jobTemplate: spec: ttlSecondsAfterFinished: 86400 template: spec: containers: - name: cleanup image: myapp:latest command: ["./cleanup.sh", "--older-than", "90d"] restartPolicy: NeverDebug : problèmes courants
Section intitulée « Debug : problèmes courants »Quatre situations reviennent en permanence avec les Jobs, et elles se diagnostiquent toutes de la même manière : d'abord les logs du Pod, ensuite les événements du Job ou du CronJob. Le réflexe qui fait gagner du temps est de vérifier que le Pod existe encore : si un TTL l'a supprimé, ses logs sont définitivement perdus et il faut relancer la tâche pour observer.
-
Le Job reste en "Running" indéfiniment
Vérifiez les logs du Pod :
Fenêtre de terminal kubectl logs job/my-jobSolution : ajoutez
activeDeadlineSecondspour forcer un timeout. -
Le Job échoue avec BackoffLimitExceeded
Analysez les événements :
Fenêtre de terminal kubectl describe job my-jobVérifiez les logs des Pods échoués :
Fenêtre de terminal kubectl logs -l job-name=my-job --all-containers -
Le CronJob ne s'exécute pas
Vérifiez l'expression cron et le fuseau horaire :
Fenêtre de terminal kubectl get cronjob my-cronjobkubectl describe cronjob my-cronjob | grep "Last Schedule" -
CronJob suspendu accidentellement
Fenêtre de terminal kubectl get cronjob my-cronjob -o jsonpath='{.spec.suspend}'Si
true, réactivez aveckubectl patch.
Erreurs fréquentes
Section intitulée « Erreurs fréquentes »Les deux premières lignes du tableau correspondent aux raisons d'échec que Kubernetes inscrit lui-même dans le statut du Job, vous les retrouverez telles quelles dans kubectl describe.
| Erreur | Cause | Solution |
|---|---|---|
BackoffLimitExceeded | Trop d'échecs | Analyser les logs, corriger le code |
DeadlineExceeded | Job trop long | Augmenter activeDeadlineSeconds ou optimiser |
| Job ne démarre pas | Expression cron invalide | Vérifier la syntaxe et le timeZone |
| Jobs qui s'accumulent | Pas de TTL | Ajouter ttlSecondsAfterFinished |
Contrôle de connaissances
Section intitulée « Contrôle de connaissances »Ce quiz porte sur les points qui coûtent le plus cher quand ils sont mal compris : la restartPolicy autorisée, le rôle du backoffLimit et l'effet de concurrencyPolicy sur les CronJobs.
Contrôle de connaissances
Validez vos connaissances avec ce quiz interactif
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
Vérification
(0/0)Profil de compétences
Quoi faire maintenant
Ressources pour progresser
Des indices pour retenter votre chance ?
Nouveau quiz complet avec des questions aléatoires
Retravailler uniquement les questions ratées
Retour à la liste des certifications
À retenir
Section intitulée « À retenir »- Un Job exécute une tâche jusqu'à complétion, un CronJob la planifie
- backoffLimit limite les tentatives, activeDeadlineSeconds impose un timeout
- ttlSecondsAfterFinished nettoie automatiquement les Jobs terminés
- parallelism et completions permettent le traitement parallèle
- concurrencyPolicy: Forbid évite les exécutions simultanées de CronJobs
- timeZone garantit des exécutions à l'heure locale
- suspend permet de mettre un CronJob en pause sans le supprimer
- Testez avec
kubectl create job --from=cronjob/...avant la mise en prod
Pour aller plus loin
Section intitulée « Pour aller plus loin »- Débugger une application : Les techniques pour inspecter un Job qui échoue avant d'avoir produit le moindre log.
- Requests et Limits : Le dimensionnement des Jobs, souvent oubliés quand un quota de namespace est atteint.
- RBAC Kubernetes : Les permissions du ServiceAccount qu'utilise un CronJob pour agir sur le cluster.