Agendamento e Cron
Scripts Python se tornam jobs confiáveis quando agendados com cron do sistema, timers systemd ou agendadores em processo como APScheduler. O script em si deve ser idempotente, logado e sair com códigos significativos.
Receita
# 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()Quando usar isso:
- ETL noturno e geração de relatórios
- Verificações de rotação de certificados e logs
- APIs de polling quando webhooks não estão disponíveis
- Agente de desenvolvimento/longa duração com APScheduler em um único processo
Exemplo de Trabalho
Script compatível com Cron com arquivo de lock, logging e alternativa APScheduler para desenvolvimento.
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("outra instância está em execução")
raise SystemExit(0)
yield
def job() -> int:
log.info("início da sincronização")
# ... trabalho ...
log.info("sincronização concluída")
return 0
def main() -> int:
with single_instance(LOCK):
return job()
if __name__ == "__main__":
raise SystemExit(main())O que isso demonstra:
- O lock de arquivo impede invocações de cron sobrepostas quando o job excede o intervalo
- Saída
0ao pular execução duplicada - cron não alerta falsamente - Caminhos absolutos para o python do venv no crontab evitam o interpretador errado
Mergulho Profundo
Opções de Agendador
| Ferramenta | Modelo |
|---|---|
| cron | Nível do SO, execução única por agendamento |
| systemd timer | Nível do SO com hooks de dependência |
| APScheduler | Em processo, Blocking/Background |
Biblioteca schedule | Loops de intervalo simples |
Higiene do Cron
- Definir
PATHecdpara o diretório raiz do projeto - Usar o caminho absoluto do python do venv
- Redirecionar stdout/stderr para logs rotacionados
- Monitorar o timestamp do último sucesso externamente
Notas Python
from apscheduler.schedulers.background import BackgroundScheduler
scheduler = BackgroundScheduler()
scheduler.add_job(send_heartbeat, "cron", hour=3)
scheduler.start() # não bloqueante em aplicativo web - desligar na saídaArmadilhas
- Caminhos relativos no cron - arquivo não encontrado. Correção:
cdpara o diretório do aplicativo ou caminhos absolutos em todos os lugares. - Execuções sobrepostas - processamento duplo. Correção: flock ou lock distribuído (Redis/DynamoDB).
- Falha silenciosa do cron - log vazio significa sucesso mesmo em caso de falha antes do logging. Correção: envolver try/except de nível superior, enviar e-mail em caso de saída não zero.
- APScheduler em múltiplos workers Gunicorn - jobs duplicados. Correção: apenas o agendador em um processo dedicado ou usar cron do SO.
- Surpresas com fuso horário - cron usa o TZ do servidor. Correção: documentar UTC vs local; usar
TZ=no crontab se necessário.
Alternativas
| Alternativa | Usar Quando | Não Usar Quando |
|---|---|---|
| Celery beat | Fila de tarefas distribuída existe | Cron simples em um único host é suficiente |
| Airflow/Prefect | Dependências de DAG complexas | Um script noturno |
| CloudWatch Events | Agendamento nativo da AWS | Cargas de trabalho apenas on-premise |
FAQs
cron vs APScheduler?
cron para batch de produção em servidores; APScheduler para daemons de desenvolvimento ou tarefas infrequentes embutidas em processos de longa duração.
Como testar scripts cron?
Executar manualmente com o mesmo ambiente do crontab; testar a função job() com pytest sem o agendador.
Qual usuário o cron deve usar?
Conta de serviço dedicada com permissões mínimas - não root, a menos que absolutamente necessário.
Como alertar sobre execuções perdidas?
Monitor de heartbeat externo (Dead Man's Snitch, healthchecks.io) esperando um ping após o job.
Posso agendar código async?
APScheduler 3.x suporta executores async em algumas configurações; cron invoca um ponto de entrada síncrono - use asyncio.run(main()) dentro.
Como a rotação de logs interage?
Cron anexa ao arquivo de log - configure o logrotate nesse caminho separadamente dos manipuladores de log do Python.
Vantagens do systemd timer?
Ordem de dependência, integração com journald e testes de unidade mais fáceis em Linux moderno.
Quanto tempo os jobs devem rodar?
Definir o intervalo do cron > tempo de execução p99; adicionar lock para lidar com overruns graciosamente.
Problemas com horário de verão?
Preferir agendamentos UTC para sistemas globais; documentar jobs em horário local em torno das transições do horário de verão.
Como isso se relaciona com scripts robustos?
Scripts agendados precisam de idempotência, dry-run e códigos de saída explícitos - veja a página Scripts Robustos.
Relacionados
- Scripts Robustos - locks e códigos de saída
- Noções Básicas de Scripting - ponto de entrada CLI
- Trabalhando com APIs e Webhooks - fallback de polling
- Melhores Práticas de Scripting de Sistema - operações não supervisionadas
- Prefect & Dagster - orquestração de fluxo de trabalho em escala
Versões de Stack: Esta página foi escrita para Python 3.14.0 (estável 3.14, manutenção 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+, e uv 0.6+.