Programación y Cron
Los scripts de Python se convierten en trabajos fiables cuando se programan con cron del sistema, temporizadores de systemd o programadores dentro del proceso como APScheduler. El script en sí debe ser idempotente, registrarse y salir con códigos significativos.
Receta
# crontab -e
0 2 * * * cd /opt/jobs && /opt/jobs/.venv/bin/python backup.py >>/var/log/backup.log 2>&1from apscheduler.schedulers.blocking import BlockingScheduler
sched = BlockingScheduler()
sched.add_job(lambda: print("tick"), "interval", minutes=5)
sched.start()Cuándo recurrir a esto:
- ETL nocturno y generación de informes
- Comprobaciones de rotación de certificados y registros
- Consultas a APIs cuando los webhooks no están disponibles
- Agente de desarrollo/larga duración con APScheduler en un solo proceso
Ejemplo funcional
Script compatible con cron con archivo de bloqueo, registro y alternativa APScheduler para desarrollo.
import fcntl
import logging
import sys
from contextlib import contextmanager
from pathlib import Path
LOCK = Path("/tmp/daily-sync.lock")
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(message)s")
log = logging.getLogger("daily-sync")
@contextmanager
def single_instance(lock_path: Path):
lock_path.parent.mkdir(parents=True, exist_ok=True)
with lock_path.open("w") as f:
try:
fcntl.flock(f.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB)
except BlockingIOError:
log.warning("otra instancia en ejecución")
raise SystemExit(0)
yield
def job() -> int:
log.info("inicio de sincronización")
# ... trabajo ...
log.info("sincronización completada")
return 0
def main() -> int:
with single_instance(LOCK):
return job()
if __name__ == "__main__":
raise SystemExit(main())Lo que esto demuestra:
- El bloqueo de archivos evita invocaciones de cron superpuestas cuando el trabajo excede el intervalo
- Salida
0al omitir la ejecución duplicada; cron no alerta falsamente - Las rutas absolutas al python del venv en crontab evitan el intérprete incorrecto
Inmersión Profunda
Opciones del Programador
| Herramienta | Modelo |
|---|---|
| cron | Nivel del SO, ejecución única por programación |
| temporizador de systemd | Nivel del SO con ganchos de dependencia |
| APScheduler | Dentro del proceso, Bloqueante/En segundo plano |
biblioteca schedule | Bucles de intervalo simples |
Higiene de Cron
- Establecer
PATHycda la raíz del proyecto - Usar la ruta absoluta de python del venv
- Redirigir stdout/stderr a registros rotados
- Monitorizar la marca de tiempo del último éxito externamente
Notas de Python
from apscheduler.schedulers.background import BackgroundScheduler
scheduler = BackgroundScheduler()
scheduler.add_job(send_heartbeat, "cron", hour=3)
scheduler.start() # no bloqueante en aplicaciones web - apagar al salirErrores Comunes
- Rutas relativas en cron - archivo no encontrado. Solución:
cdal directorio de la aplicación o rutas absolutas en todas partes. - Ejecuciones superpuestas - doble procesamiento. Solución: flock o bloqueo distribuido (Redis/DynamoDB).
- Fallo silencioso de cron - registro vacío significa éxito incluso si falla antes de registrarse. Solución: envolver try/except de nivel superior, enviar correo electrónico en caso de salida no cero.
- APScheduler en Gunicorn con múltiples trabajadores - trabajos duplicados. Solución: solo el programador en un proceso dedicado o usar cron del SO.
- Sorpresas con la zona horaria - cron usa la TZ del servidor. Solución: documentar UTC vs local; usar
TZ=en crontab si es necesario.
Alternativas
| Alternativa | Usar Cuando | No Usar Cuando |
|---|---|---|
| Celery beat | Existe una cola de tareas distribuida | Cron simple en un solo host es suficiente |
| Airflow/Prefect | Dependencias de DAG complejas | Un script nocturno |
| CloudWatch Events | Programación nativa de AWS | Cargas de trabajo solo en premisa |
Preguntas Frecuentes
¿cron vs APScheduler?
cron para lotes de producción en servidores; APScheduler para demonios de desarrollo o tareas infrecuentes incrustadas en procesos de larga duración.
¿Cómo pruebo los scripts de cron?
Ejecutar manualmente con el mismo entorno que crontab; pytest la función job() sin el programador.
¿Qué usuario debe usar cron?
Cuenta de servicio dedicada con permisos mínimos, no root a menos que sea absolutamente necesario.
¿Cómo recibo alertas sobre ejecuciones perdidas?
Monitor de latidos externo (Dead Man's Snitch, healthchecks.io) que espera un ping después del trabajo.
¿Puedo programar código asíncrono?
APScheduler 3.x admite ejecutores asíncronos en algunas configuraciones; cron invoca un punto de entrada síncrono; use asyncio.run(main()) dentro.
¿Cómo interactúa la rotación de registros?
Cron se añade al archivo de registro; configure logrotate en esa ruta por separado de los manejadores de registro de Python.
¿Ventajas del temporizador de systemd?
Ordenación de dependencias, integración con journald y pruebas de unidad más fáciles en Linux moderno.
¿Cuánto tiempo deben ejecutarse los trabajos?
Establezca el intervalo de cron > tiempo de ejecución p99; agregue un bloqueo para manejar los desbordamientos con gracia.
¿Problemas con el horario de verano?
Prefiera programaciones UTC para sistemas globales; documente los trabajos en hora local alrededor de las transiciones del horario de verano.
¿Cómo se relaciona esto con los scripts robustos?
Los scripts programados necesitan idempotencia, ejecución en seco y códigos de salida explícitos; consulte la página de Scripts Robustos.
Relacionado
- Scripts Robustos - bloqueos y códigos de salida
- Conceptos Básicos de Scripting - punto de entrada CLI
- Trabajar con APIs y Webhooks - alternativa de sondeo
- Mejores Prácticas de Scripting del Sistema - operaciones desatendidas
- Prefect y Dagster - orquestación de flujos de trabajo a escala
Versiones de la pila: Esta página se escribió para Python 3.14.0 (estable 3.14, mantenimiento 3.13), FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+ y uv 0.6+.