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.
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.
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.
| Mode d’échec | Symptômes | Récupération |
|---|---|---|
| ImagePullBackOff | Pod bloqué en Pending ou ImagePullBackOff, événements montrent Failed to pull image | Vérifiez le nom/l’étiquette de l’image, les identifiants du registre, l’accès réseau. Utilisez kubectl describe pod pour les détails. |
| CrashLoopBackOff | Le pod redémarre de manière répétée, code de sortie non nul | Inspectez les journaux (kubectl logs <pod>), corrigez la commande ou les variables d’environnement. |
| DeadlineExceeded | L’état du Job affiche Failed avec la raison DeadlineExceeded | Augmentez activeDeadlineSeconds ou optimisez la charge de travail. |
| ResourceQuotaExceeded | Le Job ne peut pas créer de pods, les événements affichent exceeded quota | Ajustez les demandes de ressources ou augmentez le quota de l’espace de noms. |
| Evicted | Pods expulsés à cause de la pression sur le nœud | Augmentez les ressources du nœud ou réduisez les demandes de ressources des pods. |
| BackoffLimitExceeded | Le Job a échoué après backoffLimit relances | Enquêtez sur la cause racine, augmentez backoffLimit si c’est transitoire. |
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.yamlpour le récupérer. - Réappliquez le manifeste avec
kubectl apply. Notez que certains champs du Job sont immuables (par exemple,template,backoffLimit,completions,parallelismaprè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 devpour tous les Jobs de l’espace de noms, ou définissezttlSecondsAfterFinishedsur 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, paslatest). restartPolicyest défini surNeverouOnFailure(jamaisAlways).- Les demandes et limites de ressources sont définies pour chaque conteneur.
backoffLimitetactiveDeadlineSecondssont 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=serverdans 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).
completionsetparallelismcorrespondent à 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.