Retour au cours

infra / kubernetes

Jobs et CronJobs

Leçon 171 exercice

Explication

Ce que vous allez apprendre

  • Comprendre la différence entre une charge permanente (Deployment) et une tâche ponctuelle (Job)
  • Configurer les réessais automatiques d'un Job avec backoffLimit
  • Limiter le temps maximal d'exécution d'un Job avec activeDeadlineSeconds
  • Paralléliser une file de travail avec completions et parallelism
  • Planifier des tâches récurrentes avec un CronJob, et éviter le piège du chevauchement

Dans quel contexte ?

Une équipe doit exporter chaque nuit un rapport de ventes, un traitement qui dure normalement 10 minutes. Un jour, la base de données est anormalement lente et l'export du lundi prend 3 heures, jusqu'à empiéter sur l'heure de déclenchement de l'export du mardi. Sans protection, les deux exports tournent alors simultanément, se marchent potentiellement dessus et corrompent le rapport final. Cette leçon montre comment un CronJob bien configuré, avec concurrencyPolicy, évite précisément ce genre de chevauchement silencieux.

Toutes les charges ne sont pas permanentes

D'abord, tout ce qui a été vu jusqu'ici, Deployment, StatefulSet, sert à faire tourner des applications qui doivent rester actives en permanence.

Un besoin de nature complètement différente

Mais beaucoup de tâches réelles ont une nature différente : une migration de base de données, un traitement d'images en lot, un export de rapport nocturne. Ces tâches doivent s'exécuter, se terminer, puis s'arrêter.

L'objet conçu pour ce cas

C'est exactement ce que le Job est conçu pour gérer, contrairement à un Deployment qui redémarrerait indéfiniment un conteneur qui se termine.

ChampRôle
backoffLimitNombre maximal de réessais en cas d'échec
activeDeadlineSecondsDurée maximale avant d'arrêter un Job bloqué
completions / parallelismNombre total de tâches réparties sur plusieurs exécutions simultanées
concurrencyPolicy (CronJob)Interdit ou autorise le chevauchement de deux exécutions planifiées

Réessayer intelligemment en cas d'échec

Un Job ne se contente pas de lancer une tâche une fois : il peut réessayer automatiquement en cas d'échec, jusqu'à un nombre limite de tentatives, le backoffLimit.

Éviter qu'une tâche bloquée tourne indéfiniment

Il peut aussi imposer une limite de temps globale, activeDeadlineSeconds, pour éviter qu'une tâche bloquée tourne indéfiniment sans jamais échouer proprement.

Prérequis

Cette leçon suppose les Pods et leurs conteneurs déjà bien compris : un Job orchestre simplement leur cycle de vie différemment d'un Deployment, en visant une complétion plutôt qu'une disponibilité permanente.

Traiter une file de travail en parallèle

Un Job peut aussi traiter une file de travail en parallèle, en répartissant un nombre total de tâches (completions) sur plusieurs exécutions simultanées (parallelism), utile pour accélérer un traitement par lots.

Planifier des tâches récurrentes

Une fois le Job compris, le CronJob ajoute une couche de planification par-dessus : au lieu de le lancer manuellement, on définit un calendrier avec la syntaxe cron classique.

Ce que fait Kubernetes à chaque échéance

Kubernetes crée alors automatiquement un nouveau Job à chaque échéance, typiquement pour une sauvegarde nocturne ou un nettoyage périodique.

Piège fréquent

Sans concurrencyPolicy définie, deux exécutions d'un même CronJob peuvent se chevaucher si la première dure plus longtemps que prévu — un risque réel pour des tâches comme des sauvegardes ou des exports, qui peuvent se corrompre mutuellement si elles tournent en même temps.

Le piège du chevauchement

Si une exécution planifiée prend plus de temps que prévu, et que la suivante démarre entretemps, deux exécutions peuvent se marcher dessus, comme deux sauvegardes lancées en même temps.

La protection à connaître

La concurrencyPolicy permet d'interdire explicitement ce chevauchement, un détail facile à négliger mais qui peut avoir de vraies conséquences en production.

La leçon suivante s'attaque à un besoin différent : comprendre ce qui se passe réellement dans le cluster grâce aux logs et aux métriques.

Commandes & code

Jobs & CronJobs

Un Job exécute une tâche jusqu'à complétion (pas un service permanent) ; un CronJob planifie des Jobs récurrents.

yaml
# job.yaml - migration de base de données exécutée une fois
apiVersion: batch/v1
kind: Job
metadata:
  name: db-migrate
spec:
  backoffLimit: 3          # nombre de tentatives en cas d'échec
  activeDeadlineSeconds: 300 # timeout global du Job
  template:
    spec:
      restartPolicy: Never   # OnFailure ou Never, jamais "Always" pour un Job
      containers:
        - name: migrate
          image: myorg/api:1.4.2
          command: ["npm", "run", "migrate"]
yaml
# job-parallel.yaml - traitement parallèle d'une file de tâches
apiVersion: batch/v1
kind: Job
metadata:
  name: batch-resize-images
spec:
  completions: 20            # nombre total de Pods qui doivent terminer avec succès
  parallelism: 4              # nombre de Pods exécutés EN MÊME TEMPS
  backoffLimit: 6
  template:
    spec:
      restartPolicy: OnFailure
      containers:
        - name: resize-worker
          image: myorg/image-worker:2.1.0
          command: ["node", "resize-worker.js"]
yaml
# cronjob.yaml - sauvegarde nocturne planifiée
apiVersion: batch/v1
kind: CronJob
metadata:
  name: nightly-db-backup
spec:
  schedule: "0 2 * * *"                # 02h00 chaque nuit (syntaxe cron classique, UTC)
  concurrencyPolicy: Forbid              # empêche le chevauchement si le précédent tourne encore
  successfulJobsHistoryLimit: 5
  failedJobsHistoryLimit: 3
  startingDeadlineSeconds: 120
  jobTemplate:
    spec:
      backoffLimit: 2
      template:
        spec:
          restartPolicy: OnFailure
          containers:
            - name: backup
              image: myorg/db-backup-tool:1.0
              command: ["/scripts/backup.sh"]
              env:
                - name: DB_PASSWORD
                  valueFrom:
                    secretKeyRef:
                      name: db-credentials
                      key: password
bash
kubectl apply -f cronjob.yaml
kubectl get cronjobs
kubectl get jobs --watch

# Déclencher manuellement une exécution immédiate (utile pour tester le CronJob)
kubectl create job manual-backup --from=cronjob/nightly-db-backup

kubectl logs job/db-migrate

Résumé

  • restartPolicy d'un Job est Never ou OnFailure, jamais Always.
  • parallelism/completions gèrent le traitement par lots en parallèle.
  • concurrencyPolicy: Forbid évite deux exécutions simultanées d'un même CronJob.
  • kubectl create job --from=cronjob/... permet de tester une exécution hors planning.

Exercices pratiques

1 disponible
1

Mission : empêcher deux sauvegardes nocturnes de se marcher dessus

Objectif : Corriger un CronJob de sauvegarde pour empêcher tout chevauchement d'exécutions, et le tester sans attendre l'échéance planifiée.

Contexte

Le CronJob nightly-db-backup exporte chaque nuit un rapport de ventes en 10 minutes normalement. Une nuit, la base de données est anormalement lente et l'export dure 3 heures, jusqu'à empiéter sur l'échéance suivante. Rien dans la configuration actuelle n'empêche alors les deux exports de tourner en même temps et de corrompre le rapport final : concurrencyPolicy n'a jamais été définie sur ce CronJob.

Résoudre l’exercice →