
Los Job de Kubernetes ejecutan tareas finitas, como migraciones, respaldos o generación de informes. Un CronJob agrega una programación recurrente y crea un nuevo Job en cada horario previsto.
A diferencia de un Deployment, estas cargas deben finalizar. Por eso no utilizan kubectl rollout ni tienen un rollback automático: la recuperación consiste en suspender nuevas ejecuciones, corregir la plantilla y crear un nuevo Job.
Qué vas a implementar
La guía configura un CronJob que:
- Se ejecuta diariamente a las 03:00 de Argentina.
- Evita ejecuciones programadas superpuestas.
- Limita reintentos y tiempo máximo de ejecución.
- Conserva temporalmente Jobs terminados para diagnóstico.
- Permanece suspendido hasta superar una prueba manual.
Un CronJob crea Jobs, y cada Job administra uno o más Pods hasta completar la tarea o alcanzar una condición de fallo. Incluso una tarea configurada con una sola ejecución podría iniciarse más de una vez en situaciones excepcionales; por eso su lógica debe ser idempotente. La documentación oficial describe este comportamiento.
Requisitos previos
- Un clúster Kubernetes operativo.
kubectlconfigurado con el contexto correcto.- Permisos para administrar namespaces, Jobs y CronJobs.
- Kubernetes 1.27 o posterior para utilizar
.spec.timeZone. - Una imagen de contenedor que finalice con código
0al completar correctamente. - Un entorno de ensayo y un mecanismo externo de respaldo cuando la tarea modifique datos.
Comprueba el contexto antes de continuar:
kubectl config current-context
kubectl version --client
kubectl cluster-infoCrea un namespace aislado:
kubectl create namespace donweb-lab \
--dry-run=client -o yaml | kubectl apply -f -Verifica los permisos:
kubectl auth can-i create jobs.batch -n donweb-lab
kubectl auth can-i create cronjobs.batch -n donweb-labAmbas consultas deben responder yes.
Configurar el CronJob
Guarda lo siguiente como cronjob.yaml:
apiVersion: batch/v1
kind: CronJob
metadata:
name: mantenimiento-diario
namespace: donweb-lab
spec:
schedule: "0 3 * * *"
timeZone: "America/Argentina/Buenos_Aires"
suspend: true
concurrencyPolicy: Forbid
startingDeadlineSeconds: 600
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 3
jobTemplate:
spec:
backoffLimit: 2
activeDeadlineSeconds: 300
ttlSecondsAfterFinished: 86400
template:
metadata:
labels:
app.kubernetes.io/name: mantenimiento-diario
spec:
restartPolicy: Never
securityContext:
seccompProfile:
type: RuntimeDefault
containers:
- name: mantenimiento
image: busybox:1.36.1
imagePullPolicy: IfNotPresent
command: ["/bin/sh", "-c"]
args:
- |
set -eu
echo "Inicio de la tarea"
date -u
sleep 5
echo "Tarea completada"
resources:
requests:
cpu: 10m
memory: 16Mi
limits:
cpu: 100m
memory: 64Mi
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 65534
capabilities:
drop: ["ALL"]El comando es deliberadamente inocuo. En una implementación real, reemplázalo por una imagen propia, revisada y preferentemente fijada mediante digest.
Controles importantes
suspend: trueevita ejecuciones programadas durante la validación.concurrencyPolicy: Forbidomite una nueva ejecución si la anterior continúa activa. Esta política solo coordina Jobs creados por el mismo CronJob.startingDeadlineSeconds: 600permite iniciar una ejecución con hasta diez minutos de retraso; después se omite.backoffLimit: 2limita los reintentos de Pods fallidos.activeDeadlineSeconds: 300detiene el Job si supera cinco minutos, aunque todavía queden reintentos.ttlSecondsAfterFinished: 86400permite limpiar el Job y sus Pods 24 horas después de finalizar.
Kubernetes admite Allow, Forbid y Replace como políticas de concurrencia. Además, un plazo inferior a diez segundos puede ser demasiado corto debido a la frecuencia de comprobación del controlador. Consulta las opciones y limitaciones de CronJob.
Insertar conceptualmente la Imagen 2 aquí.
Validar antes de aplicar
Primero solicita al API Server que valide el manifiesto sin persistirlo:
kubectl apply --dry-run=server -f cronjob.yamlLuego revisa las diferencias:
kubectl diff -f cronjob.yamlkubectl diff devuelve código 1 cuando encuentra diferencias y un valor mayor cuando ocurre un error. No agregues || true, porque ocultaría fallos reales.
Aplica el recurso todavía suspendido:
kubectl apply -f cronjob.yaml
kubectl get cronjob mantenimiento-diario -n donweb-labConfirma expresamente la programación, zona horaria y suspensión:
kubectl get cronjob mantenimiento-diario -n donweb-lab \
-o jsonpath='{.spec.schedule}{" | "}{.spec.timeZone}{" | suspend="}{.spec.suspend}{"\n"}'La salida esperada es:
0 3 * * * | America/Argentina/Buenos_Aires | suspend=trueNo incluyas TZ= ni CRON_TZ= dentro de .spec.schedule: Kubernetes rechaza ese formato. En versiones 1.27 o posteriores debe utilizarse .spec.timeZone; sin este campo, el horario se interpreta según la zona del kube-controller-manager. Referencia oficial de zonas horarias.
Ejecutar una prueba manual
Crea un Job independiente desde la plantilla del CronJob:
JOB="mantenimiento-prueba-$(date -u +%Y%m%d%H%M%S)"
kubectl create job "$JOB" \
--from=cronjob/mantenimiento-diario \
-n donweb-labEste comando está soportado específicamente para crear un Job desde un CronJob. Referencia de kubectl create job.
Espera a que finalice:
kubectl wait --for=condition=complete \
"job/$JOB" \
-n donweb-lab \
--timeout=6mComprueba estado, Pods y salida:
kubectl get job "$JOB" -n donweb-lab
kubectl get pods -n donweb-lab -l job-name="$JOB"
kubectl logs "job/$JOB" -n donweb-lab
kubectl describe job "$JOB" -n donweb-lab
kubectl get events -n donweb-lab \
--sort-by=.metadata.creationTimestampEl Job debe mostrar Complete, el Pod debe aparecer como Completed y los logs deben incluir Tarea completada.
Insertar conceptualmente la Imagen 3 aquí.
Habilitar la programación
Después de validar la tarea manual:
kubectl patch cronjob mantenimiento-diario \
-n donweb-lab \
--type=merge \
-p '{"spec":{"suspend":false}}'Comprueba el estado:
kubectl get cronjob mantenimiento-diario -n donweb-lab
kubectl describe cronjob mantenimiento-diario -n donweb-labTras el siguiente horario previsto, revisa los Jobs creados:
kubectl get jobs -n donweb-lab \
--sort-by=.metadata.creationTimestampTen presente que editar un CronJob solo cambia los Jobs futuros. Los Jobs y Pods que ya comenzaron conservan su configuración anterior.
Cómo detener o revertir el cambio
Un CronJob no admite kubectl rollout undo. Para impedir nuevas ejecuciones:
kubectl patch cronjob mantenimiento-diario \
-n donweb-lab \
--type=merge \
-p '{"spec":{"suspend":true}}'La suspensión no detiene Jobs activos. Revísalos antes de decidir si deben cancelarse:
kubectl get jobs -n donweb-lab
kubectl get pods -n donweb-labEliminar un Job activo termina sus Pods y puede dejar una operación incompleta:
kubectl delete job NOMBRE_DEL_JOB -n donweb-labPara revertir una modificación de configuración, restaura la versión anterior de cronjob.yaml, valídala y vuelve a aplicar:
kubectl apply --dry-run=server -f cronjob.yaml
kubectl diff -f cronjob.yaml
kubectl apply -f cronjob.yamlSeguridad y confiabilidad
- Diseña la tarea para tolerar ejecuciones duplicadas y reinicios.
- Usa transacciones, claves de idempotencia o bloqueos con vencimiento cuando modifiques datos.
- No almacenes contraseñas ni tokens en
args, variables literales o imágenes. - Usa
Secret, un gestor externo de secretos y unaServiceAccountcon mínimo privilegio. - Establece solicitudes y límites de recursos para evitar competencia descontrolada.
- Fija imágenes mediante digest en producción.
- Conserva logs fuera del Pod si deben sobrevivir a la limpieza automática.
- Prueba restauración, reintentos y fallos parciales antes de programar respaldos reales.
Problemas frecuentes
El CronJob no crea Jobs
Comprueba .spec.suspend, .spec.schedule, .spec.timeZone, los eventos y el reloj del control plane. Evita valores de startingDeadlineSeconds inferiores a diez segundos.
El Job falla y vuelve a intentarse
Revisa kubectl describe job, los eventos y los logs del Pod fallido. backoffLimit cuenta fallos; no corrige errores de imagen, permisos ni configuración.

Aparecen ejecuciones duplicadas
La programación de CronJobs es aproximada y puede producir más de una ejecución en circunstancias excepcionales. Forbid reduce solapamientos del mismo CronJob, pero la tarea sigue necesitando idempotencia.
El Job queda activo demasiado tiempo
Comprueba que el proceso responda a la terminación y define activeDeadlineSeconds. Si alcanza el plazo, Kubernetes marca el Job como fallido con motivo DeadlineExceeded.
Los Jobs terminados desaparecen
Revisa successfulJobsHistoryLimit, failedJobsHistoryLimit y ttlSecondsAfterFinished. El TTL puede eliminarlos antes de que el límite histórico resulte relevante.