DevOps y monitorización

Elegir la política adecuada de recuperación para tareas cron omitidas

Cuando los servidores se reinician durante tareas programadas, los saltos silenciosos o las grandes acumulaciones pueden causar pérdida de datos o prohibiciones de API. Aprende a configurar systemd, Kubernetes y scripts personalizados para manejar correctament

Racks de servidores con luces de advertencia simbolizando tareas programadas omitidas
Ilustración creada para este artículo

Traducido automáticamente del original en inglés.

Un servidor que se reinició para aplicar parches del kernel a las 01:55 y volvió a estar disponible a las 02:10 provocó que una tarea crítica de copia de seguridad programada para las 02:00 desapareciera sin dejar rastro. Dado que los demonios cron estándar no revisan el pasado, el sistema omitió silenciosamente la ejecución, dejando a la organización sin copias de seguridad durante un día completo. Este incidente pone de manifiesto un modo de fallo común pero frecuentemente ignorado en la infraestructura autoalojada, donde el comportamiento predeterminado de las herramientas de programación no se alinea con los requisitos de continuidad del negocio.

Qué ocurrió

El problema principal radica en cómo los diferentes sistemas de programación interpretan los huecos temporales causados por tiempos de inactividad. En el incidente reportado, el demonio cron clásico buscaba tareas que coincidieran con el minuto actual. Como la máquina estaba offline a las 02:00, ese minuto específico nunca existió desde la perspectiva del demonio. En consecuencia, no se registró ningún error, no se disparó ninguna alerta y la siguiente ejecución exitosa no tuvo lugar hasta el día siguiente. Este comportamiento de "salto silencioso" es el predeterminado para muchas herramientas tradicionales de programación en Unix, lo cual resulta peligroso para tareas críticas como volcados de bases de datos o escaneos de seguridad.

Por el contrario, otro equipo experimentó el extremo opuesto. Su planificador estuvo caído durante seis horas y, al recuperarse, intentó "ponerse al día" lanzando simultáneamente 72 instancias de una tarea de sincronización de cinco minutos. Esta ráfaga de actividad saturó una API asociada, activando límites de tasa (rate limits) que interrumpieron el servicio durante el resto del día. Ambos equipos sufrieron porque no habían definido explícitamente una política de ejecución omitida para sus tareas. El comportamiento predeterminado del planificador, en lugar de una decisión de ingeniería deliberada, dictó el resultado.

Detalles clave

  • Tres políticas principales: Cuando se omite una hora programada, los sistemas generalmente optan por saltar la ejecución, ejecutarla una vez con retraso o intentar ejecutar cada franja horaria perdida.
  • Cron clásico: Omite por defecto las ejecuciones perdidas por completo, sin ofrecer mecanismos de recuperación ni registro del evento omitido.
  • Temporizadores systemd: El uso de Persistent=true permite que un temporizador se dispare una vez poco después del arranque si se perdió una ejecución, ideal para copias de seguridad diarias.
  • CronJobs de Kubernetes: Se pueden configurar con startingDeadlineSeconds para permitir inicios tardíos dentro de una ventana determinada, y con concurrencyPolicy: Forbid para evitar ejecuciones superpuestas.
  • Tareas por periodo: Tareas como la facturación requieren procesar cada intervalo perdido en secuencia, lo cual debe ser gestionado por la lógica de la propia tarea, no por el planificador.
  • La detección es crítica: Independientemente de la política elegida, los equipos deben implementar monitorización para detectar cuándo una tarea ha sido omitida o ejecutada significativamente tarde.

Contexto

Para comprender estos modos de fallo, ayuda distinguir entre sincronizaciones sin estado (stateless) y operaciones con estado (stateful). Una sincronización sin estado, como obtener datos públicos del tiempo, normalmente no necesita recuperar datos pasados; la siguiente ejecución simplemente obtendrá el estado actual. Para estas, saltar la ejecución suele ser aceptable. Sin embargo, las operaciones con estado, como las copias de seguridad o la facturación financiera, acumulan deuda cuando pierden un ciclo. Una copia de seguridad perdida significa que los datos están desprotegidos durante más tiempo, y un ciclo de facturación perdido implica fuga de ingresos.

El cron tradicional está diseñado para la simplicidad, no para la resiliencia. Asume que la máquina siempre está encendida. Alternativas modernas como los temporizadores systemd y anacron fueron creadas para manejar máquinas que entran en suspensión o se reinician con frecuencia. Anacron, por ejemplo, registra el momento de la última ejecución en disco y lanza las tareas si su periodo (diario, semanal) ha transcurrido desde la última ejecución. Comprender qué herramienta se ajusta a las necesidades de fiabilidad de tu carga de trabajo es esencial para un diseño robusto de la infraestructura.

Por qué importa

Para los equipos que ejecutan su propio software, el comportamiento predeterminado de su planificador representa un riesgo oculto. Si dependes de cron estándar para copias de seguridad diarias de base de datos, un simple reinicio durante ventanas de mantenimiento puede dejarte vulnerable durante 24 horas sin ninguna alerta. Este silencio es particularmente peligroso porque crea una falsa sensación de seguridad. Los equipos de operaciones pueden ver luces verdes en sus dashboards porque el demonio cron está funcionando, sin saber que la carga útil real del trabajo fue omitida.

Por el contrario, los mecanismos agresivos de recuperación pueden causar una denegación de servicio autoinfligida. Como se vio en el ejemplo de limitación de tasa de la API, reproducir ciegamente las tareas perdidas puede abrumar a los servicios downstream o agotar los recursos locales. Esto es especialmente relevante en arquitecturas de microservicios, donde la acumulación de trabajo pendiente de un servicio puede provocar fallos en cascada en todo el sistema. Definir explícitamente cómo maneja cada tarea los tiempos de inactividad previene estas sorpresas y asegura que las acciones de recuperación sean proporcionales a la importancia de la tarea.

Qué puedes hacer

  • Audita tus tareas cron: Identifica cuáles son críticas (copias de seguridad, facturación) y cuáles son efímeras (sincronizaciones, logs). Documenta el comportamiento deseado para cada una si se pierde una ejecución.
  • Migra tareas críticas a temporizadores systemd: Usa Persistent=true para tareas diarias o semanales para asegurar que se ejecuten una vez tras un reinicio si fueron omitidas.
  • Configura plazos en Kubernetes: Establece startingDeadlineSeconds en CronJobs para definir una ventana válida para la ejecución tardía, evitando reintentos indefinidos u omisiones totales.
  • Implementa lógica de recuperación idempotente: Para tareas por periodo como la facturación, escribe la tarea para calcular los intervalos faltantes desde un registro de base de datos en lugar de depender del planificador para disparar múltiples ejecuciones.
  • Añade monitorización de latido (heartbeat): Utiliza herramientas de monitorización externas para alertar si una tarea no ha informado éxito dentro de su periodo esperado más un margen de gracia.
  • Registra horarios programados vs. reales: Asegúrate de que tu aplicación distinga en los logs entre cuándo se suponía que debía ejecutarse una tarea y cuándo comenzó realmente, para ayudar a depurar problemas de latencia.

Más noticias

Todas las noticias