## Introduction

Les Jobs Kubernetes sont les piliers du traitement par lots, des tâches ponctuelles et des opérations de maintenance. Ils garantissent qu'un nombre spécifié de pods se terminent avec succès, en gérant les relances et le cycle de vie des pods. Mais un Job n'est fiable que si sa configuration est correcte. Un restartPolicy manquant, un backoffLimit illimité ou une limite de ressources absente peuvent transformer une simple tâche en échec silencieux ou en monstre gourmand en ressources.

Dans ce guide, nous passerons en revue les erreurs de configuration les plus courantes des Jobs Kubernetes, comment valider vos manifestes de Job avant de les appliquer, et comment récupérer en toute sécurité lorsque les choses tournent mal. Chaque section comprend des extraits YAML concrets, des commandes kubectl et des sorties attendues pour que vous puissiez appliquer ces pratiques immédiatement.

## Inventaire de la version et de l’environnement

Avant de créer ou de déboguer des Jobs, vous devez connaître votre environnement Kubernetes. Cela évite les surprises liées aux API obsolètes, aux contraintes de ressources ou aux problèmes de permissions.

### 1. Vérifier la version de Kubernetes

Utilisez kubectl version pour voir les versions du client et du serveur. Les Jobs sont stables dans batch/v1 depuis Kubernetes 1.21, mais des fonctionnalités comme podFailurePolicy et managedBy nécessitent des versions plus récentes.

kubectl version --short
Client Version: v1.27.3
Server Version: v1.27.3 
 Si votre serveur est antérieur à 1.21, vous devez utiliser batch/v1beta1 pour les Jobs, mais cette API est supprimée en 1.25. Prévoyez une mise à niveau.

### 2. Vérifier les dépréciations d’API

Depuis Kubernetes 1.25, l’API batch/v1beta1 de CronJob est supprimée. Utilisez toujours batch/v1 pour les Jobs et les CronJobs. Vous pouvez vérifier les avertissements lors de l’application des ressources en utilisant kubectl apply --dry-run=client ou en interrogeant le serveur d’API avec kubectl api-resources .

### 3. Vérifier les quotas de ressources

Si votre espace de noms a un ResourceQuota, il peut limiter le nombre de Jobs, de pods, ou de CPU/mémoire. Vérifiez avec :

kubectl describe resourcequota -n dev
Name: compute-quota
Namespace: dev
Resource Used Hard
-------- ---- ----
requests.cpu 500m 2
requests.memory 1Gi 4Gi
limits.cpu 1 4
limits.memory 2Gi 8Gi
pods 3 10
jobs.batch 1 5 
 Si vous dépassez ces limites, les pods de votre Job ne seront pas planifiés.

### 4. Évaluer la capacité des nœuds

Assurez-vous que les nœuds de votre cluster disposent de suffisamment de CPU et de mémoire allouables pour les pods du Job plus les frais généraux. Utilisez :

kubectl describe nodes | grep -A 5 "Allocated resources"
Allocated resources:
 (Total limits may be over 100 percent, i.e., overcommitted.)
 Resource Requests Limits
 -------- -------- ------
 cpu 1200m (60%) 0 (0%)
 memory 900Mi (45%) 0 (0%) 

### 5. Confirmer les permissions RBAC

 Vous avez besoin au minimum des verbes create , get , list , watch et delete sur les Jobs et les pods, et get sur les logs. Pour un accès minimal, créez un rôle :

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
 namespace: dev
 name: job-manager
rules:
- apiGroups: ["batch"]
 resources: ["jobs"]
 verbs: ["create", "get", "list", "watch", "delete", "update"]
- apiGroups: [""]
 resources: ["pods", "pods/log"]
 verbs: ["get", "list", "watch"] 
 Liez ce rôle à votre utilisateur ou compte de service.

<style>
.eno-quiz-widget{margin:2rem 0;padding:1.5rem;border-radius:12px;background:var(--eno-surface-lowest,#f7f7f8);border:1px solid var(--eno-border-soft,#e2e2e6);font-family:var(--eno-font-body,Inter,sans-serif)}
.eno-quiz-widget .eno-quiz-kicker{font-family:var(--eno-font-label,"JetBrains Mono",monospace);font-size:.75rem;letter-spacing:.05em;text-transform:uppercase;color:var(--eno-text-muted,#5f5f68);margin:0 0 .5rem}
.eno-quiz-widget .eno-quiz-question{font-family:var(--eno-font-heading,"Hanken Grotesk",sans-serif);font-size:1.0625rem;font-weight:600;margin:0 0 1rem;color:var(--eno-text-strong,#1a1a1f)}
.eno-quiz-widget .eno-quiz-options{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:.5rem}
.eno-quiz-widget .eno-quiz-option{display:block;width:100%;min-height:44px;text-align:left;padding:.625rem .875rem;border-radius:8px;border:1.5px solid var(--eno-border-soft,#e2e2e6);background:#fff;font-size:.9375rem;cursor:pointer;transition:border-color var(--eno-motion-base,180ms ease-out),background var(--eno-motion-base,180ms ease-out)}
.eno-quiz-widget .eno-quiz-option:hover{border-color:var(--eno-primary,#0059bb)}
.eno-quiz-widget .eno-quiz-option:focus-visible{outline:none;box-shadow:0 0 0 3px rgb(0 89 187 / 0.15)}
.eno-quiz-widget .eno-quiz-option[aria-pressed="true"]{border-color:var(--eno-primary,#0059bb);background:rgb(0 89 187 / 0.06)}
.eno-quiz-widget .eno-quiz-option[data-correct="true"].eno-quiz-revealed{border-color:var(--eno-success,#17803a);background:rgb(23 128 58 / 0.08)}
.eno-quiz-widget .eno-quiz-option[data-correct="false"].eno-quiz-revealed.eno-quiz-was-selected{border-color:var(--eno-error,#ba1a1a);background:var(--eno-error-soft,#ffdad6)}
.eno-quiz-widget .eno-quiz-option-icon{display:inline-block;width:1.1em;margin-right:.4em;font-weight:700}
.eno-quiz-widget .eno-quiz-submit{margin-top:1rem;min-height:44px;padding:.5rem 1.25rem;border-radius:8px;border:none;background:var(--eno-primary,#0059bb);color:#fff;font-weight:600;font-size:.9375rem;cursor:pointer;transition:opacity var(--eno-motion-base,180ms ease-out)}
.eno-quiz-widget .eno-quiz-submit:disabled{opacity:.5;cursor:not-allowed}
.eno-quiz-widget .eno-quiz-submit:focus-visible{outline:none;box-shadow:0 0 0 3px rgb(0 89 187 / 0.15)}
.eno-quiz-widget .eno-quiz-explanation{margin-top:1rem;padding:.875rem 1rem;border-radius:8px;font-size:.9375rem;line-height:1.5;display:none}
.eno-quiz-widget .eno-quiz-explanation.eno-quiz-visible{display:block}
.eno-quiz-widget .eno-quiz-explanation.eno-quiz-correct{background:rgb(23 128 58 / 0.08);color:var(--eno-success,#17803a)}
.eno-quiz-widget .eno-quiz-explanation.eno-quiz-incorrect{background:var(--eno-error-soft,#ffdad6);color:var(--eno-error,#ba1a1a)}
@media (prefers-reduced-motion: reduce){.eno-quiz-widget *{transition:none!important}}
</style><div class="eno-quiz-widget" role="group" aria-label="Question rapide">
Question rapide 1 sur 2

Quelles valeurs de restartPolicy sont appropriées pour un Job Kubernetes ?

- Always
- OnFailure ou Never
- Always ou OnFailure
- Never uniquement

<button type="button" class="eno-quiz-submit" disabled>Valider</button>
<div class="eno-quiz-explanation">Comme indiqué dans la référence, un Job n&#39;est approprié que pour les Pods dont restartPolicy vaut OnFailure ou Never. La valeur par défaut est Always, ce qui ne convient pas aux Jobs.</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
  document.querySelectorAll('.eno-quiz-widget:not([data-eno-quiz-bound])').forEach(function (widget) {
    widget.setAttribute('data-eno-quiz-bound', '1');
    var options = Array.prototype.slice.call(widget.querySelectorAll('.eno-quiz-option'));
    var submitBtn = widget.querySelector('.eno-quiz-submit');
    var explanation = widget.querySelector('.eno-quiz-explanation');
    var selected = null;
    options.forEach(function (opt) {
      opt.addEventListener('click', function () {
        if (widget.hasAttribute('data-eno-quiz-answered')) return;
        options.forEach(function (o) { o.setAttribute('aria-pressed', 'false'); });
        opt.setAttribute('aria-pressed', 'true');
        selected = opt;
        submitBtn.disabled = false;
      });
    });
    submitBtn.addEventListener('click', function () {
      if (!selected || widget.hasAttribute('data-eno-quiz-answered')) return;
      widget.setAttribute('data-eno-quiz-answered', '1');
      submitBtn.disabled = true;
      var correct = selected.getAttribute('data-correct') === 'true';
      options.forEach(function (o) {
        o.classList.add('eno-quiz-revealed');
        if (o === selected) o.classList.add('eno-quiz-was-selected');
        var icon = o.querySelector('.eno-quiz-option-icon');
        if (o.getAttribute('data-correct') === 'true') icon.textContent = '\u2713';
        else if (o === selected) icon.textContent = '\u2717';
      });
      explanation.classList.add('eno-quiz-visible', correct ? 'eno-quiz-correct' : 'eno-quiz-incorrect');
    });
  });
});
</script>

## Chemin de configuration sécurisé

Une approche sûre pour déployer des Jobs consiste à commencer par un manifeste minimal, à valider avec un dry-run, à appliquer dans un espace de noms de développement, puis à ajouter des limites de ressources, des délais et des relances. Cela évite un rayon d’impact accidentel.

### Étape 1 : Créer un manifeste de Job de base

Créez un fichier job.yaml avec un Job simple qui affiche un message et se termine.

apiVersion: batch/v1
kind: Job
metadata:
 name: hello-job
spec:
 template:
 spec:
 containers:
 - name: hello
 image: busybox:1.36
 command: ["sh", "-c", "echo Hello Kubernetes! && sleep 5"]
 restartPolicy: Never
 backoffLimit: 4 
 Remarquez que restartPolicy est défini sur Never ; c’est obligatoire pour les Jobs (autrement OnFailure ). L’étiquette de l’image est épinglée à 1.36 , pas latest .

### Étape 2 : Validation par dry-run

Utilisez kubectl apply --dry-run=client pour valider la syntaxe et la justesse des champs de l’API sans réellement créer le Job.

kubectl apply --dry-run=client -f job.yaml
job.batch/hello-job created (dry run) 
 Pour une validation plus poussée qui atteint le serveur d’API sans persister, utilisez --dry-run=server (Kubernetes 1.18+). Cela vérifie les webhooks d’admission et les quotas.

kubectl apply --dry-run=server -f job.yaml
job.batch/hello-job created (server dry run) 

### Étape 3 : Appliquer dans un espace de noms de développement

 Créez un espace de noms dédié aux tests, par exemple dev , et appliquez le manifeste.

kubectl create namespace dev
kubectl apply -f job.yaml -n dev
job.batch/hello-job created 

### Étape 4 : Définir les demandes et limites de ressources

 N’exécutez jamais un Job sans demandes et limites de ressources. Cela empêche un pod de consommer toutes les ressources d’un nœud et assure une planification prévisible.

Ajoutez ce qui suit à la spécification du conteneur :

resources:
 requests:
 cpu: "100m"
 memory: "50Mi"
 limits:
 cpu: "200m"
 memory: "100Mi" 
 Extrait complet du Job :

apiVersion: batch/v1
kind: Job
metadata:
 name: hello-job
spec:
 template:
 spec:
 containers:
 - name: hello
 image: busybox:1.36
 command: ["sh", "-c", "echo Hello Kubernetes! && sleep 5"]
 resources:
 requests:
 cpu: "100m"
 memory: "50Mi"
 limits:
 cpu: "200m"
 memory: "100Mi"
 restartPolicy: Never
 backoffLimit: 4 

### Étape 5 : Configurer les délais et les relances

 Contrôlez la durée d’exécution d’un Job et le nombre de relances de pods échoués.

- backoffLimit : nombre de relances avant de marquer le Job comme échoué. La valeur par défaut est 6.

- activeDeadlineSeconds : durée maximale pendant laquelle le Job peut s’exécuter, y compris toutes les relances de pods. Une fois dépassée, le Job est terminé et marqué comme échoué.

- completions : nombre de fins de pods réussies requis pour que le Job soit considéré comme terminé.

- parallelism : nombre de pods pouvant s’exécuter simultanément.

Exemple :

spec:
 backoffLimit: 2
 activeDeadlineSeconds: 100
 completions: 5
 parallelism: 2 
 Avec cette configuration, le Job crée 5 pods au total, en exécutant 2 à la fois. Chaque pod a jusqu’à 2 relances, et tout le Job doit se terminer en 100 secondes. Si activeDeadlineSeconds est dépassé, tous les pods sont tués et le Job échoue avec la raison DeadlineExceeded .

### Manifeste de Job sécurisé complet

En combinant toutes les étapes :

apiVersion: batch/v1
kind: Job
metadata:
 name: hello-job
 namespace: dev
spec:
 backoffLimit: 2
 activeDeadlineSeconds: 100
 completions: 5
 parallelism: 2
 template:
 spec:
 containers:
 - name: hello
 image: busybox:1.36
 command: ["sh", "-c", "echo Hello Kubernetes! && sleep 5"]
 resources:
 requests:
 cpu: "100m"
 memory: "50Mi"
 limits:
 cpu: "200m"
 memory: "100Mi"
 restartPolicy: Never 

## Vérification et diagnostics

 Après avoir appliqué le Job, vérifiez son état, inspectez les journaux et surveillez les événements pour garantir une réussite.

### Vérifier l’état du Job

Utilisez kubectl get jobs pour voir l’état d’achèvement.

kubectl get jobs -n dev
NAME COMPLETIONS DURATION AGE
hello-job 5/5 45s 2m 
 Si COMPLETIONS affiche 0/5 , le Job est toujours en cours ou bloqué.

### Décrire le Job

kubectl describe job fournit des détails sur les états des pods, les événements et les conditions.

kubectl describe job hello-job -n dev
Name: hello-job
Namespace: dev
Selector: controller-uid=1234abcd-...
Labels: controller-uid=1234abcd-...,job-name=hello-job
Annotations: <none>
Parallelism: 2
Completions: 5
Start Time: Mon, 12 Aug 2024 10:00:00 +0000
Pods Statuses: 2 Active / 3 Succeeded / 0 Failed
Pod Template:
 Labels: controller-uid=1234abcd-...,job-name=hello-job
 Containers:
 hello:
 Image: busybox:1.36
 Port: <none>
 Command: ["sh", "-c", "echo Hello Kubernetes! && sleep 5"]
 Limits:
 cpu: 200m
 memory: 100Mi
 Requests:
 cpu: 100m
 memory: 50Mi
Events:
 Type Reason Age From Message
 ---- ------ ---- ---- -------
 Normal SuccessfulCreate 2m job-controller Created pod: hello-job-abcde
 Normal SuccessfulCreate 2m job-controller Created pod: hello-job-fghij
 Normal SuccessfulCreate 1m job-controller Created pod: hello-job-klmno
 Normal SuccessfulCreate 1m job-controller Created pod: hello-job-pqrst
 Normal SuccessfulCreate 1m job-controller Created pod: hello-job-uvwxy 
 Recherchez les événements FailedCreate si les pods ne peuvent pas être créés (par exemple, quota dépassé).

### Afficher les journaux des pods

D’abord, listez les pods associés au Job en utilisant le sélecteur d’étiquette job-name .

kubectl get pods -l job-name=hello-job -n dev
NAME READY STATUS RESTARTS AGE
hello-job-abcde 0/1 Completed 0 2m
hello-job-fghij 0/1 Completed 0 2m
hello-job-klmno 0/1 Completed 0 1m
hello-job-pqrst 0/1 Completed 0 1m
hello-job-uvwxy 0/1 Completed 0 1m 
 Puis affichez les journaux d’un pod :

kubectl logs hello-job-abcde -n dev
Hello Kubernetes! 
 Pour les pods échoués, les journaux révèlent souvent l’erreur.

### Surveiller les événements

Les événements fournissent une chronologie de ce qui est arrivé au Job et à ses pods.

kubectl get events --field-selector involvedObject.name=hello-job-abcde -n dev
LAST SEEN TYPE REASON OBJECT MESSAGE
2m Normal Scheduled pod/hello-job-abcde Successfully assigned dev/hello-job-abcde to node-1
2m Normal Pulled pod/hello-job-abcde Container image "busybox:1.36" already present on machine
2m Normal Created pod/hello-job-abcde Created container hello
2m Normal Started pod/hello-job-abcde Started container hello 
 Si un pod échoue, cherchez des événements Warning tels que BackOff , Failed ou Unhealthy .

<style>
.eno-quiz-widget{margin:2rem 0;padding:1.5rem;border-radius:12px;background:var(--eno-surface-lowest,#f7f7f8);border:1px solid var(--eno-border-soft,#e2e2e6);font-family:var(--eno-font-body,Inter,sans-serif)}
.eno-quiz-widget .eno-quiz-kicker{font-family:var(--eno-font-label,"JetBrains Mono",monospace);font-size:.75rem;letter-spacing:.05em;text-transform:uppercase;color:var(--eno-text-muted,#5f5f68);margin:0 0 .5rem}
.eno-quiz-widget .eno-quiz-question{font-family:var(--eno-font-heading,"Hanken Grotesk",sans-serif);font-size:1.0625rem;font-weight:600;margin:0 0 1rem;color:var(--eno-text-strong,#1a1a1f)}
.eno-quiz-widget .eno-quiz-options{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:.5rem}
.eno-quiz-widget .eno-quiz-option{display:block;width:100%;min-height:44px;text-align:left;padding:.625rem .875rem;border-radius:8px;border:1.5px solid var(--eno-border-soft,#e2e2e6);background:#fff;font-size:.9375rem;cursor:pointer;transition:border-color var(--eno-motion-base,180ms ease-out),background var(--eno-motion-base,180ms ease-out)}
.eno-quiz-widget .eno-quiz-option:hover{border-color:var(--eno-primary,#0059bb)}
.eno-quiz-widget .eno-quiz-option:focus-visible{outline:none;box-shadow:0 0 0 3px rgb(0 89 187 / 0.15)}
.eno-quiz-widget .eno-quiz-option[aria-pressed="true"]{border-color:var(--eno-primary,#0059bb);background:rgb(0 89 187 / 0.06)}
.eno-quiz-widget .eno-quiz-option[data-correct="true"].eno-quiz-revealed{border-color:var(--eno-success,#17803a);background:rgb(23 128 58 / 0.08)}
.eno-quiz-widget .eno-quiz-option[data-correct="false"].eno-quiz-revealed.eno-quiz-was-selected{border-color:var(--eno-error,#ba1a1a);background:var(--eno-error-soft,#ffdad6)}
.eno-quiz-widget .eno-quiz-option-icon{display:inline-block;width:1.1em;margin-right:.4em;font-weight:700}
.eno-quiz-widget .eno-quiz-submit{margin-top:1rem;min-height:44px;padding:.5rem 1.25rem;border-radius:8px;border:none;background:var(--eno-primary,#0059bb);color:#fff;font-weight:600;font-size:.9375rem;cursor:pointer;transition:opacity var(--eno-motion-base,180ms ease-out)}
.eno-quiz-widget .eno-quiz-submit:disabled{opacity:.5;cursor:not-allowed}
.eno-quiz-widget .eno-quiz-submit:focus-visible{outline:none;box-shadow:0 0 0 3px rgb(0 89 187 / 0.15)}
.eno-quiz-widget .eno-quiz-explanation{margin-top:1rem;padding:.875rem 1rem;border-radius:8px;font-size:.9375rem;line-height:1.5;display:none}
.eno-quiz-widget .eno-quiz-explanation.eno-quiz-visible{display:block}
.eno-quiz-widget .eno-quiz-explanation.eno-quiz-correct{background:rgb(23 128 58 / 0.08);color:var(--eno-success,#17803a)}
.eno-quiz-widget .eno-quiz-explanation.eno-quiz-incorrect{background:var(--eno-error-soft,#ffdad6);color:var(--eno-error,#ba1a1a)}
@media (prefers-reduced-motion: reduce){.eno-quiz-widget *{transition:none!important}}
</style><div class="eno-quiz-widget" role="group" aria-label="Question rapide">
Question rapide 2 sur 2

Que fait le contrôleur de Job lorsqu'il voit un nouveau Job ?

- Il exécute les conteneurs lui-même.
- Il demande au serveur d'API de créer ou supprimer des Pods.
- Il planifie directement les Pods sur les Nodes.
- Il met à jour le Job pour le marquer immédiatement terminé.

<button type="button" class="eno-quiz-submit" disabled>Valider</button>
<div class="eno-quiz-explanation">Le contrôleur de Job n&#39;exécute aucun Pod ni conteneur lui-même. Au lieu de cela, il demande au serveur d&#39;API de créer ou supprimer des Pods. D&#39;autres composants agissent sur ces nouvelles informations pour planifier et exécuter les Pods.</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
  document.querySelectorAll('.eno-quiz-widget:not([data-eno-quiz-bound])').forEach(function (widget) {
    widget.setAttribute('data-eno-quiz-bound', '1');
    var options = Array.prototype.slice.call(widget.querySelectorAll('.eno-quiz-option'));
    var submitBtn = widget.querySelector('.eno-quiz-submit');
    var explanation = widget.querySelector('.eno-quiz-explanation');
    var selected = null;
    options.forEach(function (opt) {
      opt.addEventListener('click', function () {
        if (widget.hasAttribute('data-eno-quiz-answered')) return;
        options.forEach(function (o) { o.setAttribute('aria-pressed', 'false'); });
        opt.setAttribute('aria-pressed', 'true');
        selected = opt;
        submitBtn.disabled = false;
      });
    });
    submitBtn.addEventListener('click', function () {
      if (!selected || widget.hasAttribute('data-eno-quiz-answered')) return;
      widget.setAttribute('data-eno-quiz-answered', '1');
      submitBtn.disabled = true;
      var correct = selected.getAttribute('data-correct') === 'true';
      options.forEach(function (o) {
        o.classList.add('eno-quiz-revealed');
        if (o === selected) o.classList.add('eno-quiz-was-selected');
        var icon = o.querySelector('.eno-quiz-option-icon');
        if (o.getAttribute('data-correct') === 'true') icon.textContent = '\u2713';
        else if (o === selected) icon.textContent = '\u2717';
      });
      explanation.classList.add('eno-quiz-visible', correct ? 'eno-quiz-correct' : 'eno-quiz-incorrect');
    });
  });
});
</script>

## Modes d’échec et récupération

Les Jobs peuvent échouer pour de nombreuses raisons. Voici les modes d’échec courants, les symptômes et les étapes de récupération.

<div class="my-stack-md overflow-x-auto">
<table class="min-w-[42rem] border-collapse text-left">
<thead><tr><th scope="col" class="border border-outline-variant bg-surface-container-low px-4 py-3 text-left font-label-md font-semibold text-on-surface">Mode d’échec</th><th scope="col" class="border border-outline-variant bg-surface-container-low px-4 py-3 text-left font-label-md font-semibold text-on-surface">Symptômes</th><th scope="col" class="border border-outline-variant bg-surface-container-low px-4 py-3 text-left font-label-md font-semibold text-on-surface">Récupération</th></tr></thead>
<tbody><tr><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant"><strong>ImagePullBackOff</strong></td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Pod bloqué en <code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded">Pending</code> ou <code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded">ImagePullBackOff</code>, événements montrent <code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded">Failed to pull image</code></td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Vérifiez le nom/l’étiquette de l’image, les identifiants du registre, l’accès réseau. Utilisez <code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded">kubectl describe pod</code> pour les détails.</td></tr>
<tr><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant"><strong>CrashLoopBackOff</strong></td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Le pod redémarre de manière répétée, code de sortie non nul</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Inspectez les journaux (<code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded">kubectl logs &lt;pod&gt;</code>), corrigez la commande ou les variables d’environnement.</td></tr>
<tr><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant"><strong>DeadlineExceeded</strong></td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">L’état du Job affiche <code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded">Failed</code> avec la raison <code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded">DeadlineExceeded</code></td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Augmentez <code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded">activeDeadlineSeconds</code> ou optimisez la charge de travail.</td></tr>
<tr><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant"><strong>ResourceQuotaExceeded</strong></td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Le Job ne peut pas créer de pods, les événements affichent <code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded">exceeded quota</code></td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Ajustez les demandes de ressources ou augmentez le quota de l’espace de noms.</td></tr>
<tr><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant"><strong>Evicted</strong></td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Pods expulsés à cause de la pression sur le nœud</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Augmentez les ressources du nœud ou réduisez les demandes de ressources des pods.</td></tr>
<tr><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant"><strong>BackoffLimitExceeded</strong></td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Le Job a échoué après <code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded">backoffLimit</code> relances</td><td class="border border-outline-variant px-4 py-3 align-top text-body-md text-on-surface-variant">Enquêtez sur la cause racine, augmentez <code class="font-mono text-[0.9em] bg-surface-container px-1 py-0.5 rounded">backoffLimit</code> si c’est transitoire.</td></tr></tbody>
</table>
</div>

### Exemples de récupération détaillés

### ImagePullBackOff

Supposons que votre Job utilise image: myapp:latest mais que le registre exige une authentification. Le pod affichera ImagePullBackOff .

Vérifiez le pod :

kubectl describe pod myjob-xxxxx -n dev
Events:
 Type Reason Age From Message
 ---- ------ ---- ---- -------
 Normal Scheduled 5m default-scheduler Successfully assigned dev/myjob-xxxxx to node-1
 Warning Failed 5m kubelet Failed to pull image "myapp:latest": rpc error: code = Unknown desc = Error response from daemon: pull access denied for myapp, repository does not exist or may require 'docker login'
 Warning Failed 5m kubelet Error: ErrImagePull
 Normal BackOff 4m kubelet Back-off pulling image "myapp:latest"
 Warning Failed 4m kubelet Error: ImagePullBackOff 
 Récupération :

- Vérifiez le nom et l’étiquette de l’image, par exemple myregistry.com/myapp:1.2.3 .

- Si elle est privée, créez un Secret avec les identifiants du registre et ajoutez imagePullSecrets à la spécification du pod.

- Réappliquez le manifeste corrigé.

### CrashLoopBackOff

Un pod qui se termine avec un code non nul de façon répétée entrera en CrashLoopBackOff .

kubectl get pods -n dev
NAME READY STATUS RESTARTS AGE
myjob-xxxxx 0/1 CrashLoopBackOff 5 3m 
 Inspectez les journaux :

kubectl logs myjob-xxxxx -n dev --previous
Error: cannot open config file 
 Récupération :

- Corrigez la commande ou l’environnement pour fournir la configuration manquante.

- Mettez à jour le manifeste du Job et réappliquez.

- Si le Job est toujours en cours, supprimez le pod pour en déclencher un nouveau (sous réserve de backoffLimit ).

### DeadlineExceeded

Si votre Job dépasse activeDeadlineSeconds , tous les pods sont terminés et le Job échoue.

kubectl get job myjob -n dev
NAME COMPLETIONS DURATION AGE
myjob 0/1 100s 2m

kubectl describe job myjob -n dev
...
Conditions:
 Type Status Reason Message
 ---- ------ ------ -------
 Failed True DeadlineExceeded Job was active longer than specified deadline 
 Récupération :

- Si la charge de travail nécessite légitimement plus de temps, augmentez activeDeadlineSeconds .

- Si elle s’est bloquée, enquêtez sur la cause racine via les journaux.

- Supprimez le Job échoué et créez-en un nouveau avec les paramètres corrigés.

### Étapes de retour en arrière et de récupération

Lorsqu’un Job échoue à cause d’une mauvaise configuration, suivez ces étapes :

- Modifiez le manifeste du Job pour corriger le problème. Si vous avez le fichier d’origine, modifiez-le ; sinon, utilisez kubectl get job <name> -o yaml > job.yaml pour le récupérer.

- Réappliquez le manifeste avec kubectl apply . Notez que certains champs du Job sont immuables (par exemple, template , backoffLimit , completions , parallelism après la création dans les anciennes versions). Si vous devez modifier des champs immuables, vous devez supprimer et recréer le Job.

- Si des champs immuables changent , supprimez l’ancien Job et créez-en un nouveau :

kubectl delete job hello-job -n dev
kubectl apply -f corrected-job.yaml -n dev 

- Nettoyez les Jobs terminés pour éviter l’encombrement et les fuites de ressources. Utilisez kubectl delete job --all -n dev pour tous les Jobs de l’espace de noms, ou définissez ttlSecondsAfterFinished sur le Job pour le supprimer automatiquement après une période (Kubernetes 1.21+).

 spec:
 ttlSecondsAfterFinished: 3600 # supprimer 1 heure après la fin 

## Liste de contrôle opérationnelle

 Avant de promouvoir un Job en production, assurez-vous que ces éléments sont en place. Cette liste couvre les points de mauvaise configuration les plus critiques.

- L’étiquette de l’image du conteneur est épinglée (par exemple, busybox:1.36 , pas latest ).

- restartPolicy est défini sur Never ou OnFailure (jamais Always ).

- Les demandes et limites de ressources sont définies pour chaque conteneur.

- backoffLimit et activeDeadlineSeconds sont définis en fonction du temps d’exécution prévu et de la tolérance aux relances.

- Le manifeste est validé avec kubectl apply --dry-run=server dans un espace de noms de développement.

- Les journaux sont accessibles via votre pile de journalisation et surveillés pour les erreurs.

- Le plan de retour en arrière est documenté et testé (par exemple, vous pouvez redéployer depuis le contrôle de version).

- Le Job est idempotent ou sûr à réexécuter (pas d’effets secondaires en cas de double exécution).

- completions et parallelism correspondent à la charge de travail (pas accidentellement laissés aux valeurs par défaut qui sur- ou sous-approvisionnent).

- Les quotas de l’espace de noms et la capacité des nœuds sont suffisants pour le parallélisme de pointe.

- Les permissions RBAC sont minimales mais suffisantes pour l’automatisation.

- Le Job a des étiquettes appropriées pour l’identification et la surveillance (par exemple, app: billing-export ).

Utilisez cette liste comme porte d’entrée dans votre pipeline CI/CD ou comme document de revue pour les déploiements manuels.

## Conclusion

Les Jobs Kubernetes sont puissants, mais les erreurs de configuration sont courantes et peuvent entraîner des échecs silencieux, des relances infinies ou l’épuisement des ressources. En suivant un chemin de configuration sécurisé, en validant avec des dry-runs, en définissant des limites de ressources et des délais, et en surveillant les événements, vous pouvez exécuter des Jobs de manière fiable en production.

N’oubliez pas de commencer par un pilote étroit, de valider localement et d’établir des habitudes de surveillance et de retour en arrière. Révisez régulièrement vos configurations de Jobs pour éviter toute dérive. Avec ces pratiques, vos charges de travail par lots s’exécuteront sans heurts, et en cas d’échec, vous saurez exactement comment diagnostiquer et récupérer.