Home Comunidade Eventos YouTube Networks Desafios // produtos Academy News CTI | CVE Contato

06 - Timers (Substituindo cron)

video explicativo
Video explicativoEspaco reservado para o video desta anotação — adicione video: https://youtu.be/... no frontmatter da nota.

Os temporizadores do systemd — identificados pela extensão .timer — representam a evolução arquitetural para o agendamento de tarefas temporais e execução de rotinas periódicas no ecossistema Linux. Historicamente, essa responsabilidade era monopolizada pelo cron (e suas variantes como vixie-cron, cronie e anacron), um daemon independente que consulta a cada minuto a tabela /etc/crontab e dispara comandos via subshell sem integração direta com o estado do sistema.

A transição do cron para as unidades .timer substitui a execução cega de comandos isolados pela orquestração declarativa de processos atrelados ao Grafo Acíclico Dirigido (DAG) do PID 1. Um arquivo .timer não executa scripts diretamente; ele atua como um gatilho de eventos que monitora relógios internos do kernel e dispara a ativação de uma unidade correspondente (tipicamente um .service de mesmo nome base).


1. A Ruptura com o Paradigma do Cron

O modelo tradicional do cron sofre de limitações estruturais severas quando aplicado a infraestruturas modernas e ambientes de alta concorrência:

  • Ausência de Rastreamento de Processos: Quando o cron dispara um script, ele executa um fork() simples. Se o script gerar subprocessos (workers ou threads filhas) e sofrer uma terminação anormal, os subprocessos tornam-se órfãos no espaço de usuário, consumindo memória e I/O sem que o cron tenha visibilidade do estado real.
  • Gestão de Recursos Cega: O cron não possui capacidade nativa para impor cotas de CPU, RAM ou limitar a largura de banda de I/O de disco para os trabalhos agendados. Se três rotinas pesadas coincidirem na mesma janela de tempo (ex: 00:00), o servidor sofre uma tempestade de I/O (I/O storm) que pode causar indisponibilidade total de serviços críticos.
  • Logs Fragmentados e Desconectados: O cron redireciona nativamente a saída das tarefas para o subsistema local de e-mails (mailx / /var/spool/mail) ou exige o redirecionamento manual de saídas para arquivos de texto (>> /var/log/backup.log 2>&1). Não há vinculação direta entre o evento de tempo e o histórico de execução da aplicação.
  • Tratamento de Perda de Janela de Execução: Se a máquina estivesse desligada no exato minuto em que uma tarefa do cron deveria rodar, a execução era simplesmente perdida. A solução para isso exigia a instalação de outro daemon paralelo (anacron), com sintaxe e comportamentos totalmente distintos.

Os systemd timers resolvem integralmente essa dívida técnica. Como o .timer ativa um .service real, a tarefa agendada herda automaticamente:

  1. Isolamento via Cgroups: Limitações estritas de recursos (CPUQuota=, MemoryMax=, IOWeight=).
  2. Rastreamento Inviolável de Processos: Eliminação total de processos zumbis ou órfãos através de encerramento via cgroup.
  3. Coleta de Telemetria Nativa: Integração total com o journald, permitindo inspeção individualizada do histórico de execução (journalctl -u meu-backup.service).
  4. Resiliência contra Desligamentos: Controle nativo de recuperação de tarefas perdidas em janelas de tempo offline via persistent de estado em disco.

2. A Invocação por Par Duplo (Timer + Service)

A arquitetura de agendamento do systemd exige estritamente a criação de duas unidades independentes, porém vinculadas:

┌───────────────────────────┐           ┌───────────────────────────┐
│     backup.timer          │           │     backup.service        │
├───────────────────────────┤           ├───────────────────────────┤
│ [Timer]                   │           │ [Service]                 │
│ OnCalendar=*-*-* 02:00:00 ┼──────────►│ Type=oneshot              │
│ Unit=backup.service       │ (Dispara) │ ExecStart=/usr/bin/backup │
└───────────────────────────┘           └───────────────────────────┘

  1. A Unidade de Gatilho (.timer): Contém a lógica de monitoramento de relógio (quando rodar) e a indicação de qual unidade deve ser disparada.
  2. A Unidade de Execução (.service): Define como o processo deve rodar (diretivas ExecStart=, limites de memória, usuário de execução User=, variáveis de ambiente).

Por convenção de nomenclatura, se o arquivo .timer for nomeado backup.timer, ele procurará e ativará automaticamente o arquivo backup.service no mesmo diretório de carga. Se o serviço possuir um nome diferente, o arquivo .timer deve declarar explicitamente a diretiva Unit=outro-nome.service na seção [Timer].


3. Tipologia dos Relógios: Monotônicos vs. Tempo Real

O systemd provê duas categorias completamente distintas de temporizadores, que mapeiam chamadas de sistema e relógios internos diferentes mantidos pelo kernel Linux (clock_gettime).

3.1. Temporizadores Monotônicos (Monotonic Timers)

Os temporizadores monotônicos medem o tempo corrido a partir de um evento de estado do sistema operativo. Eles utilizam o relógio CLOCK_MONOTONIC do kernel, que é um contador estritamente crescente e imune a alterações manuais no relógio da máquina ou ajustes de fusos horários e sincronizações via NTP (Network Time Protocol).

Todas as diretivas monotônicas aceitam unidades de tempo expressas em segundos (s), minutos (m), horas (h), dias (d) ou semanas (w).

  • OnActiveSec=: Computa o tempo a partir do momento exato em que a própria unidade .timer foi ativada na memória RAM (via systemctl start ou durante o boot).
  • OnBootSec=: Computa o tempo a partir do momento em que o kernel Linux iniciou o processo de inicialização do hardware.
  • OnStartupSec=: Computa o tempo a partir do instante em que o PID 1 (systemd) foi instanciado no espaço de usuário.
  • OnUnitActiveSec=: Computa o tempo a partir da última vez em que a unidade .service associada transitou para o estado active.
  • OnUnitInactiveSec=: Computa o tempo a partir do momento em que a unidade .service associada finalizou sua execução e retornou ao estado inactive.

Exemplo de Temporizador Monotônomo Recorrente:

Para criar um job de limpeza que roda 15 minutos após o boot do sistema e, a partir daí, executa novamente a cada 2 horas exatas:

# /etc/systemd/system/limpeza-cache.timer
[Unit]
Description=Gatilho de Limpeza de Cache Periódica

[Timer]
OnBootSec=15m
OnUnitInactiveSec=2h

[Install]
WantedBy=timers.target

3.2. Temporizadores de Tempo Real (Realtime / Calendar Timers)

Os temporizadores de tempo real medem o tempo absoluto do calendário humano (relógio de parede / Wall Clock), utilizando o relógio CLOCK_REALTIME do kernel. Eles são configurados através da diretiva OnCalendar=.

A sintaxe das expressões de calendário no systemd substitui a notação opaca do cron (* * * * *) por uma especificação textual rica e declarativa:

DiaDaSemana Ano-Mês-Dia Hora:Minuto:Segundo

Regras de Construção de Expressões OnCalendar=:

  • Coringas (*): Representa qualquer valor válido para aquele campo.
  • Faixas (..): Define intervalos contínuos (ex: Mon..Fri para dias úteis, ou 01..15 para os primeiros quinze dias do mês).
  • Listas (,): Agrupa múltiplos valores específicos (ex: 08,12,16:00:00 para rodar às 8h, 12h e 16h).
  • Repetições Periódicas (/): Define incrementos (ex: *:0/15:00 para executar a cada 15 minutos).

Matriz Comparativa de Expressões de Tempo Real:

Expressão OnCalendar= Equivalente Conceitual Descrição de Execução
*-*-* 00:00:00 @daily ou 0 0 * * * Executa todos os dias à meia-noite.
Mon..Fri *-*-* 03:00:00 0 3 * * 1-5 Executa de segunda a sexta-feira, rigorosamente às 03:00:00 AM.
*-*-01 04:00:00 @monthly ou 0 4 1 * * Executa no primeiro dia de cada mês, às 04:00:00 AM.
*-*-15..31 Fri 23:00:00 N/A (Complexo no Cron) Executa na última sexta-feira do mês (se cair entre o dia 15 e 31) às 23:00:00.
hourly @hourly Atalho interno para *-*-* *:00:00 (no início de cada hora).
weekly @weekly Atalho interno para Mon *-*-* 00:00:00 (toda segunda-feira à meia-noite).

4. Recursos Avançados de Orquestração Temporal

A especificação .timer expõe diretivas avançadas que resolvem gargalos clássicos de concorrência e resiliência em data centers e clusters.

4.1. Persistência e Recuperação de Janela (Persistent=true)

Se o servidor for desligado antes da janela de execução de um temporizador OnCalendar= (por exemplo, uma rotina configurada para as 03:00 AM em uma estação de trabalho que é desenergizada à noite), o cron tradicional simplesmente ignoraria a execução.

A diretiva Persistent=true resolve isso ativando um mecanismo de marcação de estado em disco:

  1. Sempre que o serviço atrelado ao timer finaliza com sucesso, o systemd atualiza o timestamp de execução no arquivo /var/lib/systemd/timers/stamp-nome_do_timer.timer.
  2. No próximo ciclo de boot, quando o systemd carrega o arquivo .timer, ele lê a marcação em /var/lib/systemd/timers/.
  3. Se o tempo atual for maior do que o tempo configurado na expressão OnCalendar= e a última execução gravada for anterior ao evento perdido, o PID 1 dispara o serviço associado imediatamente.

4.2. Pulverização Aleatória de Carga (RandomizedDelaySec=)

Em arquiteturas de computação em nuvem, virtualização corporativa ou gerenciamento de configurações (como Ansible/Puppet), implantar o mesmo arquivo de agendamento em 5.000 instâncias virtuais rodando sob o mesmo barramento de armazenamento SAN/NVMe cria tempestades de I/O destrutivas se todas as máquinas tentarem ler e escrever no disco no mesmo segundo (00:00:00).

A diretiva RandomizedDelaySec=15m instrui o systemd a calcular um desvio aleatório entre 0 e o tempo estipulado (15 minutos) para cada máquina individualmente:

  • O timer determina a janela de execução para as 02:00 AM.
  • Cada nó calculará um offset único baseado no seu machine-id (ex: a máquina A rodará às 02:03:12, a máquina B às 02:11:45).
  • A carga de CPU e I/O no hipervisor é distribuída suavemente ao longo da janela, prevenindo gargalos de infraestrutura.

4.3. Precisão do Temporizador (AccuracySec=)

Por padrão, para otimizar o consumo de energia e evitar acordar o processador do estado de suspensão (C-states) a todo segundo, o systemd alinha os temporizadores em janelas de coalescência de eventos de 1 minuto (AccuracySec=1min).

Para tarefas críticas de alta precisão que exigem execução temporal estrita em milissegundos, o administrador pode sobrescrever essa margem:

[Timer]
OnCalendar=*-*-* *:00:00
# Força o disparo com precisão de 1 microssegundo
AccuracySec=1us


5. Inspeção, Gerenciamento e Diagnóstico da CLI

O gerenciamento de temporizadores é integrado à CLI unificada do systemctl.

5.1. Listagem dos Temporizadores Ativos (systemctl list-timers)

O comando systemctl list-timers varre a memória do PID 1 e desenha uma tabela consolidada sobre o estado de todas as unidades .timer carregadas no sistema:

systemctl list-timers

Saída Típica do Comando:

NEXT                        LEFT        LAST                        PASSED     UNIT                         ACTIVATES
Wed 2026-10-14 18:00:00 -03 2h 5min left Wed 2026-10-14 17:00:00 -03 54min ago  logrotate.timer              logrotate.service
Thu 2026-10-15 00:00:00 -03 8h left      Wed 2026-10-14 00:00:00 -03 17h ago    fstrim.timer                 fstrim.service
Thu 2026-10-15 03:10:00 -03 11h left     Thu 2026-10-08 03:10:00 -03 6 days ago  apt-daily-upgrade.timer      apt-daily-upgrade.service

3 timers listed.

Análise das Colunas do Relatório:

  • NEXT: A data e hora exatas da próxima execução agendada no relógio do sistema.
  • LEFT: A contagem regressiva temporal restante até o próximo disparo.
  • LAST: A data e hora exatas da última vez que o temporizador disparou o serviço associado.
  • PASSED: O tempo decorrido desde a última execução com sucesso.
  • UNIT: O nome do arquivo de unidade .timer responsável pelo monitoramento.
  • ACTIVATES: A unidade .service de destino que será invocada no instante do disparo.

5.2. Teste de Expressões e Validação de Sintaxe (systemd-analyze calendar)

Para evitar erros de sintaxe ao escrever expressões complexas na diretiva OnCalendar=, o systemd provê a ferramenta de análise sintática systemd-analyze calendar.

Ao passar uma expressão de tempo como argumento, o utilitário analisa a string, valida a sintaxe, exibe a forma normalizada e calcula as próximas iterações em que a tarefa será disparada:

systemd-analyze calendar "Fri..Sun *-*-15..31 22:00:00"

Saída de Validação da CLI:

  Normalized form: Fri..Sun *-*-15..31 22:00:00
    Next elapse: Fri 2026-10-16 22:00:00 -03
       (in UTC): Sat 2026-10-17 01:00:00 UTC
       From now: 2 days left

5.3. Fluxo de Instalação e Ativação de um Timer

Para criar e ativar um novo agendamento no sistema, a sequência operacional correta é:

  1. Criar o arquivo de serviço /etc/systemd/system/meu-job.service:
[Unit]
Description=Execução de Script de Manutenção

[Service]
Type=oneshot
ExecStart=/usr/local/bin/manutencao.sh
User=root

  1. Criar o arquivo de temporizador /etc/systemd/system/meu-job.timer:
[Unit]
Description=Gatilho Diário para Script de Manutenção

[Timer]
OnCalendar=daily
Persistent=true

[Install]
WantedBy=timers.target

  1. Recarregar a máquina de estados do PID 1 para parsear as novas unidades em RAM:
systemctl daemon-reload

  1. Habilitar e iniciar o temporizador (opera sobre o .timer, não sobre o .service):
systemctl enable --now meu-job.timer


6. Quadro Comparativo: cron vs. systemd.timer

Propriedade Arquitetural Sistema Tradicional (cron) Sistema Moderno (systemd.timer)
Daemon Responsável Processo isolado (crond). PID 1 (systemd).
Granularidade Mínima 1 Minuto. 1 Microsegundo (AccuracySec=1us).
Rastreamento de Processos Não possui (Filhos órfãos escapam do rastreamento). Total via cgroups (Rastreia e destrói subprocessos).
Coleta de Logs Saída redirecionada para arquivos de texto manuais ou e-mail. Integrada nativamente ao Journald (journalctl -u service).
Controle de Recursos Inexistente (Risco de travar o servidor por I/O). Suporta cotas estritas de CPU, RAM e I/O via cgroups no .service.
Recuperação de Boots Offline Requer ferramenta externa paralela (anacron). Nativa via diretiva Persistent=true.
Desvio Aleatório de Carga Requer injeção de comandos sleep $RANDOM em scripts. Nativa via diretiva RandomizedDelaySec=.
Execução Sob Demanda (Teste) Requer edição do arquivo crontab ou execução manual do script. Testável via systemctl start meu-job.service sem alterar a agenda.