Introduction
Les CronJobs Kubernetes excellent pour les tâches récurrentes: synchronisation de données, génération de rapports, warming de cache et nettoyage. Ce guide montre comment automatiser des CronJobs avec CI/CD à l'aide d'exemples concrets en YAML, Python, Bash et GitLab CI. Vous apprendrez à:
- Construire et tester l'image du job
- Valider les manifestes avant déploiement
- Déployer en sécurité en suspendant d'abord puis en lançant un test à la demande
- Ajouter des smoke tests et des garde-fous
- Revenir en arrière rapidement en cas d'échec
- Diagnostiquer les pannes courantes du pipeline
Pourquoi cela fonctionne: l'approche par l'exemple accélère le passage de l'intention à l'automatisation opérationnelle, tandis qu'une séparation claire des étapes réduit le rework et favorise l'itération. Ce guide couvre de bout en bout Kubernetes CronJobs CI/CD. Nous mettons en pratique la Kubernetes CronJobs automation, clarifions le Kubernetes CronJobs deployment, mettons en place un Kubernetes CronJobs pipeline traçable et finissons par un Kubernetes CronJobs rollback rapide et vérifiable.
Vue d'ensemble du workflow
Le flux de travail suivant est simple, sûr et prêt pour la production:
- Écrire le code du job
- Définir le manifeste CronJob
- Ajouter des étapes CI/CD (build, validate, deploy, smoke test, promote, rollback)
- Appliquer des contrôles de déploiement sûrs
- Activer la planification après vérification
1) Écrire le code du job
Exemple de job Python avec codes de sortie explicites et logs lisibles:
# app/main.py
import logging, os, sys, time
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s")
def validate_env():
required = ["EXAMPLE_PARAM"]
missing = [k for k in required if not os.getenv(k)]
if missing:
logging.error("Missing required env: %s", ",".join(missing))
return False
return True
def run_job():
logging.info("job start")
# Simule du travail
time.sleep(2)
# Ajoutez ici votre logique
logging.info("job done")
return 0
if __name__ == "__main__":
if not validate_env():
sys.exit(2)
code = run_job()
sys.exit(code)
Entrypoint avec gestion stricte des erreurs:
# app/entrypoint.sh
#!/usr/bin/env bash
set -Eeuo pipefail
trap 'echo "[ERROR] line $LINENO status $?" >&2' ERR
python /app/main.py
Dockerfile minimal:
# Dockerfile
FROM python:3.11-slim
WORKDIR /app
COPY app/requirements.txt /app/
RUN pip install --no-cache-dir -r requirements.txt || true
COPY app/ /app/
RUN useradd -u 10001 appuser && chown -R appuser /app
USER 10001
ENTRYPOINT ["bash","/app/entrypoint.sh"]
2) Définir le manifeste CronJob
Des valeurs par défaut prudentes réduisent l'impact d'un incident et facilitent le debug.
# k8s/cronjob.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
name: report-cron
namespace: staging
spec:
schedule: "*/15 * * * *" # toutes les 15 minutes
# timeZone: "UTC" # décommentez si supporté par le cluster
concurrencyPolicy: Forbid # éviter les exécutions qui se chevauchent
startingDeadlineSeconds: 300 # ne pas démarrer si trop en retard
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 2
suspend: true # déployer suspendu; d'abord un smoke test
jobTemplate:
spec:
backoffLimit: 1
activeDeadlineSeconds: 600 # limite dure de durée d'exécution
template:
spec:
restartPolicy: Never
containers:
- name: report
image: registry.example.com/report-cron:${IMAGE_TAG}
imagePullPolicy: IfNotPresent
env:
- name: EXAMPLE_PARAM
value: nightly
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "512Mi"
imagePullSecrets:
- name: regcred
Notes:
- Utilisez des tags d'image immuables (ex: SHA de commit). Évitez latest.
- Déployez avec
suspend: truepour les premiers déploiements; activez après un smoke test concluant. - Pour les secrets, montez depuis un Secret ou suivez votre pratique standard.
3) Ajouter des étapes CI/CD
Exemple GitLab CI pragmatique avec build, validate, deploy en staging, smoke test, promotion en prod et rollback.
# .gitlab-ci.yml
stages: [test, build, validate, deploy_staging, smoke_test, deploy_prod, rollback]
variables:
APP_NAME: "report-cron"
IMAGE_REGISTRY: "registry.example.com"
IMAGE_TAG: "$CI_COMMIT_SHA"
KUBE_NAMESPACE_STAGING: "staging"
KUBE_NAMESPACE_PROD: "prod"
# Fournissez DOCKER_AUTH, KUBECONFIG et kubectl dans vos runners
unit-test:
stage: test
image: python:3.11-slim
script:
- pip install -r app/requirements.txt || true
- python -m py_compile app/main.py
build-image:
stage: build
image: docker:24
services: ["docker:24-dind"]
script:
- echo "$DOCKER_AUTH" | docker login -u "$DOCKER_USER" --password-stdin $IMAGE_REGISTRY
- docker build -t $IMAGE_REGISTRY/$APP_NAME:$IMAGE_TAG .
- docker push $IMAGE_REGISTRY/$APP_NAME:$IMAGE_TAG
needs: ["unit-test"]
validate-manifest:
stage: validate
image: bitnami/kubectl:1.30
script:
- sed "s/\${IMAGE_TAG}/$IMAGE_TAG/g" k8s/cronjob.yaml > k8s/rendered.yaml
- kubectl apply --dry-run=client -f k8s/rendered.yaml
needs: ["build-image"]
deploy-staging:
stage: deploy_staging
image: bitnami/kubectl:1.30
script:
- kubectl config use-context "$KUBE_CONTEXT_STAGING"
- kubectl -n $KUBE_NAMESPACE_STAGING apply -f k8s/rendered.yaml
- kubectl -n $KUBE_NAMESPACE_STAGING annotate cronjob/$APP_NAME ci-commit=$CI_COMMIT_SHA --overwrite
needs: ["validate-manifest"]
environment:
name: staging
smoke-test-staging:
stage: smoke_test
image: bitnami/kubectl:1.30
script:
- kubectl config use-context "$KUBE_CONTEXT_STAGING"
- export SMOKE_JOB=${APP_NAME}-smoke-$(date +%s)
- kubectl -n $KUBE_NAMESPACE_STAGING create job $SMOKE_JOB --from=cronjob/$APP_NAME
- kubectl -n $KUBE_NAMESPACE_STAGING wait --for=condition=complete job/$SMOKE_JOB --timeout=10m || (kubectl -n $KUBE_NAMESPACE_STAGING logs job/$SMOKE_JOB; exit 1)
- kubectl -n $KUBE_NAMESPACE_STAGING logs job/$SMOKE_JOB
- kubectl -n $KUBE_NAMESPACE_STAGING delete job/$SMOKE_JOB --ignore-not-found
- echo "Smoke test passed"
needs: ["deploy-staging"]
promote-prod:
stage: deploy_prod
image: bitnami/kubectl:1.30
script:
- kubectl config use-context "$KUBE_CONTEXT_PROD"
- sed "s/namespace: staging/namespace: $KUBE_NAMESPACE_PROD/g" k8s/rendered.yaml > k8s/prod.yaml
- kubectl -n $KUBE_NAMESPACE_PROD apply -f k8s/prod.yaml
- kubectl -n $KUBE_NAMESPACE_PROD annotate cronjob/$APP_NAME ci-commit=$CI_COMMIT_SHA --overwrite
- # Facultatif: unsuspend après un smoke test en prod
needs: ["smoke-test-staging"]
environment:
name: production
rollback:
stage: rollback
image: bitnami/kubectl:1.30
when: manual
variables:
ROLLBACK_TAG: "" # définir lors du déclenchement
script:
- test -n "$ROLLBACK_TAG" || (echo "Set ROLLBACK_TAG" && exit 1)
- kubectl config use-context "$KUBE_CONTEXT_PROD"
- kubectl -n $KUBE_NAMESPACE_PROD patch cronjob $APP_NAME --type='json' \
-p='[{"op":"replace","path":"/spec/jobTemplate/spec/template/spec/containers/0/image","value":"'$IMAGE_REGISTRY'/'$APP_NAME':'$ROLLBACK_TAG'"}]'
- # Vérifier via un Job one-off avant d'unsuspend la planification
- export SMOKE_JOB=${APP_NAME}-rb-$(date +%s)
- kubectl -n $KUBE_NAMESPACE_PROD create job $SMOKE_JOB --from=cronjob/$APP_NAME
- kubectl -n $KUBE_NAMESPACE_PROD wait --for=condition=complete job/$SMOKE_JOB --timeout=10m || (kubectl -n $KUBE_NAMESPACE_PROD logs job/$SMOKE_JOB; exit 1)
- kubectl -n $KUBE_NAMESPACE_PROD logs job/$SMOKE_JOB
- kubectl -n $KUBE_NAMESPACE_PROD delete job/$SMOKE_JOB --ignore-not-found
4) Contrôles de déploiement sûrs
- Dry-run des manifestes avant
apply. - Déployer les CronJobs suspendus dans les nouveaux environnements.
- Faire un smoke test avec un Job ponctuel créé depuis le template du CronJob.
- Garder les exécutions courtes avec
activeDeadlineSecondset éviter les chevauchements avecconcurrencyPolicy: Forbid. - Utiliser des tags immuables et annoter avec le SHA de commit pour la traçabilité.
5) Activer la planification après vérification
Quand les smoke tests passent, enlever la suspension:
kubectl -n staging patch cronjob report-cron -p '{"spec": {"suspend": false}}'
Répétez en production après promotion et validation.
Plan pilote local
Commencez par un seul CronJob et gardez un périmètre restreint.
Objectifs du pilote:
- Un CronJob dans un namespace bac à sable
- Build, validation, déploiement suspendu et smoke test réussi
- Mesuré par un code de sortie 0 et des logs attendus
Étapes:
- Choisir un job à faible risque (ex: rapport sans effets externes).
- Containeriser et ajouter des codes de sortie stricts et des logs (comme ci-dessus).
- Rédiger un manifeste CronJob avec des valeurs sûres et
suspend: true. - Lancer des vérifications locales:
- Linter Python, tests rapides
- Rendre le manifeste avec le SHA de commit
kubectl apply --dry-run=client -f k8s/rendered.yaml
- Déployer dans un namespace bac à sable.
- Créer un Job ponctuel depuis le CronJob et attendre la complétion.
- Inspecter les logs et le statut de sortie; corriger si nécessaire.
- Documenter les critères de succès puis seulement unsuspend la planification.
Pourquoi ce plan: restreint, mesurable et inspectable localement, il apporte de la valeur rapidement sans risquer la production.
Modèles pratiques de rollback
Les CronJobs ne supportent pas kubectl rollout undo. Gardez un rollback simple:
- Tags immuables: construisez des images taguées avec le SHA de commit; conservez le dernier état sain.
- Revenir au dernier tag sain via un patch, puis vérifier avec un Job ponctuel.
- Ou bien revert du changement Git qui met à jour le tag d'image et redéploiement.
Exemple de patch:
kubectl -n prod patch cronjob report-cron --type='json' \
-p='[{"op":"replace","path":"/spec/jobTemplate/spec/template/spec/containers/0/image","value":"registry.example.com/report-cron: abc123"}]'
Pannes courantes du pipeline et correctifs
- ImagePullBackOff: credentials du registre manquants ou mauvais tag d'image.
- Correctif: vérifier
imagePullSecretset l'usage de tags immuables; confirmer que le push a réussi. - CrashLoopBackOff ou sortie non nulle: bug applicatif ou variables d'environnement absentes.
- Correctif: ajouter les variables requises; renforcer la gestion d'erreurs dans l'entrypoint; inspecter les logs.
- Le Job dépasse la limite de temps:
- Correctif: définir
activeDeadlineSecondset revoir la charge; découper en tâches plus petites si nécessaire. - Chevauchements d'exécutions:
- Correctif:
concurrencyPolicy: Forbidet un espacement de planification raisonnable. - Démarrages manqués quand le cluster est chargé:
- Correctif:
startingDeadlineSecondset éventuellement augmenter les ressources. - CronJob suspendu qui ne s'exécute jamais:
- Correctif: unsuspend après un smoke test concluant.
- Erreurs RBAC ou de contexte dans la CI:
- Correctif: vérifier le context
kubectl, le namespace et les permissions pourapply,create jobetpatch. - Confusion de fuseau horaire du cron:
- Correctif: planifier en UTC ou valider le comportement du cluster; clarifier dans la documentation.
Conclusion
Automatiser des CronJobs Kubernetes avec CI/CD devient simple si vous séparez les préoccupations et intégrez des garde-fous:
- Construire et tester l'image avec des codes de sortie explicites
- Valider les manifestes en dry-run
- Déployer suspendu puis valider via un Job ponctuel
- Promouvoir avec des tags immuables et des annotations claires
- Prévoir un rollback rapide en patchant le tag d'image
Démarrez avec un CronJob dans un bac à sable, rendez le succès mesurable, puis étendez seulement quand votre pilote est vert et observable.