Timers de Systemd como Alternativa a Cron: Programación Moderna de Tareas

Introducción

Los timers de systemd proporcionan una alternativa moderna y poderosa a los trabajos cron tradicionales para programar tareas en sistemas Linux. Mientras que cron ha servido a sistemas tipo Unix durante décadas, los timers de systemd ofrecen características mejoradas incluyendo gestión de dependencias, control de recursos, registro detallado e integración con el ecosistema systemd. Comprender los timers de systemd es esencial para las prácticas modernas de administración de sistemas y DevOps.

Esta guía completa explora los timers de systemd desde lo básico hasta patrones avanzados, demostrando cómo migrar desde cron e implementar escenarios de programación sofisticados.

¿Por Qué Elegir Timers de Systemd en Lugar de Cron?

Ventajas de los Timers de Systemd

Mejor Registro: Integración con journald proporciona logs estructurados y consultables Gestión de Dependencias: Iniciar tareas solo cuando servicios específicos están disponibles Control de Recursos: Usar cgroups para límites de CPU, memoria e I/O Disparadores Basados en Eventos: Activar en arranque, inicio de servicio o cambios de archivo Expresiones de Calendario: Sintaxis de programación más flexible Manejo de Fallos: Reintento automático con retroceso configurable Gestión de Entorno: Mejor control sobre el entorno de ejecución Monitoreo: Verificación de estado nativa e integración con herramientas de monitoreo

Cuándo Usar Timers de Systemd

  • Distribuciones Linux modernas (Ubuntu 16.04+, CentOS 7+, Debian 8+)
  • Tareas que requieren límites de recursos
  • Cadenas de dependencias complejas
  • Necesidad de registro detallado y monitoreo
  • Integración con otros servicios systemd

Prerequisitos

  • Sistema Linux con systemd (verificar: systemctl --version)
  • Acceso root o sudo
  • Comprensión básica de servicios systemd
  • Familiaridad con conceptos de cron

Fundamentos de Timers de Systemd

Componentes de Timers

Cada timer de systemd consiste en dos archivos:

  1. Unidad de Servicio (.service): Define qué ejecutar
  2. Unidad de Timer (.timer): Define cuándo ejecutar

Ubicaciones de Archivos

# Timers del sistema
/etc/systemd/system/

# Timers de usuario
~/.config/systemd/user/

# Valores por defecto del sistema
/lib/systemd/system/

Creando Tu Primer Timer

Ejemplo: Timer de Respaldo Diario

Paso 1: Crear la Unidad de Servicio

# /etc/systemd/system/backup.service
[Unit]
Description=Daily Backup Service
Wants=network-online.target
After=network-online.target

[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
User=backup
Group=backup
StandardOutput=journal
StandardError=journal

# Límites de recursos
CPUQuota=50%
MemoryLimit=512M

Paso 2: Crear la Unidad de Timer

# /etc/systemd/system/backup.timer
[Unit]
Description=Daily Backup Timer
Requires=backup.service

[Timer]
# Ejecutar diariamente a las 2:00 AM
OnCalendar=daily
OnCalendar=*-*-* 02:00:00
Persistent=true

# Ejecutar inmediatamente si se perdió
Persistent=true

[Install]
WantedBy=timers.target

Paso 3: Habilitar e Iniciar

# Recargar systemd
sudo systemctl daemon-reload

# Habilitar timer (iniciar al arrancar)
sudo systemctl enable backup.timer

# Iniciar timer ahora
sudo systemctl start backup.timer

# Verificar estado
sudo systemctl status backup.timer

# Listar todos los timers
systemctl list-timers

# Ver logs
journalctl -u backup.service

Sintaxis de Expresiones de Calendario

Especificaciones de Tiempo

# Ejemplos de OnCalendar

# Cada minuto
OnCalendar=*-*-* *:*:00

# Cada 5 minutos
OnCalendar=*-*-* *:0/5:00

# Cada hora
OnCalendar=hourly
OnCalendar=*-*-* *:00:00

# Cada día a medianoche
OnCalendar=daily
OnCalendar=*-*-* 00:00:00

# Cada día a hora específica
OnCalendar=*-*-* 03:30:00

# Cada lunes a las 9 AM
OnCalendar=Mon *-*-* 09:00:00

# Días laborables a las 6 PM
OnCalendar=Mon..Fri *-*-* 18:00:00

# Primer día del mes
OnCalendar=*-*-01 00:00:00

# Cada 15 minutos
OnCalendar=*:0/15

# Múltiples horarios
OnCalendar=*-*-* 06:00:00
OnCalendar=*-*-* 18:00:00

# Horario de oficina (9-5, Lun-Vie)
OnCalendar=Mon..Fri 09..17:00:00

Probar Expresiones de Calendario

# Probar expresión de calendario
systemd-analyze calendar "Mon..Fri *-*-* 09:00:00"

# La salida muestra:
#  Original form: Mon..Fri *-*-* 09:00:00
#  Normalized form: Mon..Fri *-*-* 09:00:00
#  Next elapse: Mon 2024-01-15 09:00:00
#  From now: 3 days left

Configuraciones Avanzadas de Timers

1. Timer de Verificación de Salud del Sistema

# /etc/systemd/system/health-check.service
[Unit]
Description=System Health Check
After=network.target

[Service]
Type=oneshot
ExecStart=/usr/local/bin/health-check.sh
User=monitoring
StandardOutput=journal
StandardError=journal

# Tiempo de espera después de 5 minutos
TimeoutStartSec=300

# Reiniciar en caso de fallo
Restart=on-failure
RestartSec=60
# /etc/systemd/system/health-check.timer
[Unit]
Description=Run Health Check Every 5 Minutes

[Timer]
# Cada 5 minutos
OnBootSec=5min
OnUnitActiveSec=5min

# Ejecutar inmediatamente al arrancar
OnBootSec=1min

[Install]
WantedBy=timers.target

2. Respaldo de Base de Datos con Dependencias

# /etc/systemd/system/db-backup.service
[Unit]
Description=Database Backup
Requires=postgresql.service
After=postgresql.service

[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup-database.sh

# Ejecutar como usuario específico
User=postgres
Group=postgres

# Establecer entorno
Environment="PGDATA=/var/lib/postgresql/data"
EnvironmentFile=/etc/db-backup.conf

# Límites de recursos
Nice=19
IOSchedulingClass=2
IOSchedulingPriority=7

# Registro
StandardOutput=journal+console
StandardError=journal+console
# /etc/systemd/system/db-backup.timer
[Unit]
Description=Daily Database Backup at 2 AM
Requires=db-backup.service

[Timer]
# Diariamente a las 2 AM
OnCalendar=*-*-* 02:00:00

# Ponerse al día si se perdió
Persistent=true

# Aleatorizar hasta 30 minutos
RandomizedDelaySec=1800

[Install]
WantedBy=timers.target

3. Timer de Rotación de Logs

# /etc/systemd/system/log-rotation.service
[Unit]
Description=Rotate Application Logs

[Service]
Type=oneshot
ExecStart=/usr/local/bin/rotate-logs.sh

# Ejecutar con privilegios restringidos
User=logger
Group=logger
PrivateTmp=yes
ProtectSystem=strict
ReadWritePaths=/var/log/app

StandardOutput=journal
# /etc/systemd/system/log-rotation.timer
[Unit]
Description=Rotate Logs Daily at Midnight

[Timer]
# Diariamente a medianoche
OnCalendar=daily

# También ejecutar semanalmente incluso si no se activó
OnCalendar=weekly

Persistent=true

[Install]
WantedBy=timers.target

4. Timer de Limpieza con Múltiples Programaciones

# /etc/systemd/system/cleanup.service
[Unit]
Description=Cleanup Temporary Files

[Service]
Type=oneshot
ExecStart=/usr/local/bin/cleanup-temp.sh
ExecStart=/usr/local/bin/cleanup-old-logs.sh
ExecStart=/usr/local/bin/cleanup-cache.sh

User=root
StandardOutput=journal
# /etc/systemd/system/cleanup.timer
[Unit]
Description=Regular System Cleanup

[Timer]
# Ejecutar diariamente a las 3 AM
OnCalendar=*-*-* 03:00:00

# También ejecutar semanalmente los domingos
OnCalendar=Sun *-*-* 01:00:00

# Y mensualmente el primer día
OnCalendar=*-*-01 02:00:00

Persistent=true
AccuracySec=1h

[Install]
WantedBy=timers.target

Migrando de Cron a Timers de Systemd

Conversión de Cron a Timer

Expresión CronEquivalente Timer Systemd
*/5 * * * *OnUnitActiveSec=5min
0 2 * * *OnCalendar=*-*-* 02:00:00
0 0 * * 0OnCalendar=Sun *-*-* 00:00:00
0 0 1 * *OnCalendar=*-*-01 00:00:00
@rebootOnBootSec=1min
@dailyOnCalendar=daily
@hourlyOnCalendar=hourly

Script de Conversión

#!/bin/bash
# cron-to-systemd.sh
# Convertir trabajo cron a timer systemd

CRON_SCHEDULE="$1"
SERVICE_NAME="$2"
COMMAND="$3"

# Crear archivo de servicio
cat > "/etc/systemd/system/${SERVICE_NAME}.service" <<EOF
[Unit]
Description=${SERVICE_NAME} Service

[Service]
Type=oneshot
ExecStart=${COMMAND}
StandardOutput=journal
StandardError=journal
EOF

# Crear archivo de timer basado en programación cron
# (Conversión simplificada)
TIMER_SPEC=""
case "$CRON_SCHEDULE" in
    "@hourly")
        TIMER_SPEC="OnCalendar=hourly"
        ;;
    "@daily")
        TIMER_SPEC="OnCalendar=daily"
        ;;
    "@weekly")
        TIMER_SPEC="OnCalendar=weekly"
        ;;
    *)
        echo "Manual conversion required for: $CRON_SCHEDULE"
        ;;
esac

cat > "/etc/systemd/system/${SERVICE_NAME}.timer" <<EOF
[Unit]
Description=${SERVICE_NAME} Timer

[Timer]
${TIMER_SPEC}
Persistent=true

[Install]
WantedBy=timers.target
EOF

systemctl daemon-reload
echo "Created ${SERVICE_NAME}.service and ${SERVICE_NAME}.timer"

Características Avanzadas

1. Disparadores Basados en Eventos

# /etc/systemd/system/process-uploads.service
[Unit]
Description=Process New Uploads

[Service]
Type=oneshot
ExecStart=/usr/local/bin/process-uploads.sh
# /etc/systemd/system/process-uploads.path
[Unit]
Description=Monitor Uploads Directory

[Path]
# Observar nuevos archivos
PathChanged=/var/uploads
# Activar servicio
Unit=process-uploads.service

[Install]
WantedBy=multi-user.target

2. Gestión de Recursos

# /etc/systemd/system/heavy-task.service
[Unit]
Description=Resource-Intensive Task

[Service]
Type=oneshot
ExecStart=/usr/local/bin/heavy-task.sh

# Límite de CPU (50%)
CPUQuota=50%

# Límite de memoria
MemoryLimit=1G
MemoryMax=1G

# Prioridad de I/O (mejor esfuerzo, baja prioridad)
IOSchedulingClass=2
IOSchedulingPriority=7

# Valor nice
Nice=19

# Limitar archivos abiertos
LimitNOFILE=1024

3. Manejo de Fallos y Reintentos

# /etc/systemd/system/api-sync.service
[Unit]
Description=Sync with External API
After=network-online.target
Wants=network-online.target

[Service]
Type=oneshot
ExecStart=/usr/local/bin/api-sync.sh

# Reintentar en caso de fallo
Restart=on-failure
RestartSec=60

# Máximo de intentos de reinicio
StartLimitBurst=3
StartLimitIntervalSec=300

# Tiempo de espera
TimeoutStartSec=180

Gestionando Timers de Systemd

Comandos Esenciales

# Listar todos los timers
systemctl list-timers --all

# Mostrar detalles del timer
systemctl status backup.timer

# Mostrar estado del servicio
systemctl status backup.service

# Ver logs
journalctl -u backup.service
journalctl -u backup.service --since "1 hour ago"
journalctl -u backup.service -f  # Seguir

# Iniciar timer manualmente
systemctl start backup.timer

# Detener timer
systemctl stop backup.timer

# Habilitar al arrancar
systemctl enable backup.timer

# Deshabilitar
systemctl disable backup.timer

# Reiniciar timer
systemctl restart backup.timer

# Activar servicio inmediatamente
systemctl start backup.service

# Verificar cuándo se ejecutará el timer
systemctl list-timers backup.timer

# Recargar configuración
systemctl daemon-reload

Monitoreo y Depuración

# Verificar programación de calendario del timer
systemd-analyze calendar "*-*-* 02:00:00"

# Verificar configuración del timer
systemd-analyze verify /etc/systemd/system/backup.timer

# Ver propiedades del timer
systemctl show backup.timer

# Verificar dependencias del servicio
systemctl list-dependencies backup.service

# Modo depuración
SYSTEMD_LOG_LEVEL=debug systemctl start backup.service

Timers de Usuario

Creando Timers de Usuario

# Crear directorio systemd de usuario
mkdir -p ~/.config/systemd/user

# Crear servicio
cat > ~/.config/systemd/user/my-task.service <<EOF
[Unit]
Description=My Personal Task

[Service]
Type=oneshot
ExecStart=/home/user/bin/my-task.sh
EOF

# Crear timer
cat > ~/.config/systemd/user/my-task.timer <<EOF
[Unit]
Description=My Task Timer

[Timer]
OnCalendar=daily
Persistent=true

[Install]
WantedBy=timers.target
EOF

# Recargar demonio de usuario
systemctl --user daemon-reload

# Habilitar e iniciar
systemctl --user enable --now my-task.timer

# Listar timers de usuario
systemctl --user list-timers

Habilitar Lingering

# Permitir que timers de usuario se ejecuten cuando no está conectado
sudo loginctl enable-linger $USER

# Verificar estado de lingering
loginctl show-user $USER | grep Linger

Mejores Prácticas

1. Convenciones de Nomenclatura

# Usar nombres descriptivos
backup-database.timer
cleanup-logs.timer
check-ssl-certificates.timer

2. Documentación

[Unit]
Description=Clear description of what this timer does
Documentation=https://wiki.company.com/backup-procedure

3. Notificaciones de Fallos

# /etc/systemd/system/backup.service
[Service]
ExecStart=/usr/local/bin/backup.sh
OnFailure=failure-notification@%n.service

4. Pruebas

# Probar servicio antes de habilitar timer
systemctl start backup.service

# Verificar logs
journalctl -u backup.service -n 50

# Verificar estado de salida
systemctl show -p Result backup.service

5. Seguridad

[Service]
# Ejecutar como usuario específico
User=backup
Group=backup

# Aislamiento
PrivateTmp=yes
ProtectSystem=strict
ProtectHome=yes
ReadWritePaths=/backup

# Capacidades
NoNewPrivileges=yes

Solución de Problemas

Problemas Comunes

Timer no se ejecuta:

# Verificar si está habilitado
systemctl is-enabled backup.timer

# Verificar estado
systemctl status backup.timer

# Ver logs de systemd
journalctl -xe

Servicio falla:

# Verificar logs del servicio
journalctl -u backup.service -n 100

# Probar servicio manualmente
systemctl start backup.service

# Verificar sintaxis del archivo de servicio
systemd-analyze verify /etc/systemd/system/backup.service

Timer se ejecuta en momento incorrecto:

# Verificar expresión de calendario
systemd-analyze calendar "daily"

# Verificar configuración del timer
systemctl cat backup.timer

# Listar próximo tiempo de activación
systemctl list-timers backup.timer

Conclusión

Los timers de systemd proporcionan una alternativa moderna y rica en características a cron para programar tareas en sistemas Linux. Con mejor registro, gestión de recursos e integración con el ecosistema systemd, los timers ofrecen ventajas significativas para la administración de sistemas y automatización.

Conclusiones clave:

  • Los timers de systemd consisten en unidades .service y .timer
  • Las expresiones de calendario son más flexibles que la sintaxis de cron
  • La integración con journald proporciona mejor registro
  • Los límites de recursos y características de seguridad están integrados
  • Las dependencias y el orden se gestionan explícitamente
  • Los timers de usuario habilitan programación de tareas sin root

Comienza a migrar trabajos cron críticos a timers de systemd para beneficiarte de características modernas mientras mantienes la confiabilidad. Combina timers con otras características de systemd como unidades path y activación de sockets para patrones de automatización aún más poderosos.