Como Evitar Desastres com Tarefas Agendadas no Linux: Usar o flock para que Scripts Lentos Nunca Esgotem a Memória RAM
O Inimigo Invisível dos Servidores Linux: A Cascata de Processos no Cron
Se geres servidores Linux, certamente já acordaste com um alerta a meio da noite a dizer que a máquina deixou de responder. A ligação SSH não entra, o Nginx devolve erros 502 Bad Gateway e o painel de controlo mostra o processador a 100% e a fila de espera do disco saturada.
Quando consegues finalmente aceder à consola, a causa quase nunca é um ataque informático sofisticado. Na maioria esmagadora das vezes, a culpa é de uma linha simples colocada no teu /etc/crontab:
# A BOMBA-RELÓGIO NO CRON: Uma tarefa pesada agendada a cada 10 minutos
*/10 * * * * /usr/local/bin/backup-para-s3.sh
A CASCATA DE DESTRUIÇÃO DE UM CRON SEM BLOQUEIO
- 02:00: Tarefa 1 Arranca → A rede abranda (Demora 18 minutos a terminar)
- 02:10: Tarefa 2 Arranca → A Tarefa 1 e 2 correm em simultâneo! O disco sofre.
- 02:20: Tarefa 3 Arranca → 3 Backups em paralelo! A RAM esgota e entra em swap.
- 02:30: Tarefa 4 Arranca → 4 Backups ativos! O load average dispara para 80.
- 02:32: O Kernel ativa o Out-Of-Memory (OOM) Killer → MATA A BASE DE DADOS!
O cron clássico é cego. É apenas um relógio de parede que dispara comandos de forma mecânica. Ele não sabe—nem quer saber—se a execução anterior já terminou. Se uma quebra de velocidade na ligação à internet fizer com que o envio de um ficheiro demore 15 minutos em vez de 5, o cron lança uma segunda cópia ao minuto 10. Os dois processos começam a lutar pelo disco e pela memória. Ao minuto 20 nasce um terceiro processo. Em menos de uma hora, dezenas de cópias do mesmo script devoraram toda a memória RAM, forçando o kernel do Linux a abater o PostgreSQL ou o servidor web para não rebentar.
Nos nossos serviços de alojamento gerido e nas intervenções de consultoria técnica, aplicamos regras estritas de bloqueio em todos os ambientes baseados em Debian GNU/Linux para erradicar este problema.
Porque Criar Ficheiros .lock à Mão Falha Sempre
Quando um programador se depara com este problema pela primeira vez, a sua reação instintiva é tentar criar uma validação manual dentro do script de bash:
# A FORMA ERRADA: Verificação ingénua de ficheiro
FICHEIRO_LOCK="/tmp/backup.lock"
if [ -f "$FICHEIRO_LOCK" ]; then
echo "A tarefa já está a correr! A abortar."
exit 1
fi
touch "$FICHEIRO_LOCK"
# ... executa o backup ...
rm -f "$FICHEIRO_LOCK"
Esta abordagem parece lógica mas tem duas falhas fatais em produção:
- Condição de Corrida (TOCTOU): Entre o instante em que o comando
[ -f ]testa se o ficheiro existe e o comandotoucho cria, o processador pode alternar entre tarefas. Se duas instâncias arrancarem no mesmo segundo, ambas veem que o ficheiro não existe, ambas o criam e correm as duas ao mesmo tempo. - A Armadilha do Ficheiro Órfão: Se a máquina for reiniciada de repente, faltar a energia ou o script for terminado com
kill -9, o ficheiro/tmp/backup.lockfica esquecido no disco para sempre. A partir desse dia, o teu backup nunca mais corre até que alguém se lembre de ir lá apagar o ficheiro à mão.
A Solução Robusta do Unix: Bloqueio Atómico com o Utilitário flock
A resposta definitiva e matemática para este problema é o utilitário nativo flock, que comunica diretamente com a chamada de sistema flock() do kernel do Linux.
BLOQUEIO ATÓMICO COM FLOCK NO KERNEL
- Instância A do Cron (02:00) → flock -n /var/lock/backup.lock
- BLOQUEIO OBTIDO (FD 9): Executa o Backup…
- Instância B do Cron (02:10) → flock -n /var/lock/backup.lock
- BLOQUEIO RECUSADO (EBUSY)
- Aborta de imediato! (Consome 0% de CPU e RAM)
- Tarefa A Termina → Libertado!
Como o flock trabalha com descritores de ficheiro geridos pelo kernel:
- A exclusão mútua é 100% atómica: O sistema operativo garante que dois processos nunca conseguem o bloqueio em simultâneo.
- Fim dos ficheiros órfãos: Se o script crashar ou a máquina for desligada, o kernel do Linux fecha o descritor de ficheiro e liberta o bloqueio de forma automática.
1. Proteger Comandos Diretamente no Crontab
Podes proteger qualquer script existente na tua crontab adicionando o flock -n à frente do comando:
# Crontab Segura: Salta a execução se a tarefa anterior ainda não tiver acabado
*/10 * * * * /usr/bin/flock -n /var/lock/backup.lock /usr/local/bin/backup-para-s3.sh
Se o script anterior ainda estiver a comprimir ficheiros quando bater o minuto 10, o flock -n vê que o fecho está ocupado e desliga-se num milissegundo com código de erro 1. Gasta zero recursos e deixa o processo legítimo terminar sem pressas.
2. Bloqueio Embutido no Próprio Script com Descritores de Ficheiro
Se estás a escrever um script em Bash complexo, podes colocar a proteção no cabeçalho recorrendo ao descritor 200:
#!/usr/bin/env bash
set -euo pipefail
FICHEIRO_LOCK="/var/lock/sincronizar-ficheiros.lock"
exec 200>"${FICHEIRO_LOCK}"
# Tenta obter o bloqueio exclusivo; sai em silêncio se já estiver ocupado
if ! flock -n 200; then
echo "[$(date -u)] Já existe outra cópia em execução. A terminar sem conflitos." >&2
exit 0
fi
# Todo o código a partir daqui corre com garantia absoluta de isolamento!
echo "[$(date -u)] Bloqueio garantido. A iniciar processamento pesado..."
/usr/bin/node /opt/scripts/otimizar-imagens.mjs
echo "[$(date -u)] Trabalho concluído com sucesso."
# O bloqueio é libertado sozinho quando o processo terminar!
E em Portáteis ou Computadores Pessoais? O Papel do Anacron
O cron clássico assume que a máquina está ligada 24 horas por dia. Se agendares uma tarefa para as 03:00 da manhã, mas estiveres a trabalhar num portátil de desenvolvimento que desligaste às 19:00, o cron simplesmente ignora a tarefa e nunca a executa.
É aqui que deves recorrer ao anacron.
DIFERENÇA DE COMPORTAMENTO: CRON VS ANACRON
- CRON NORMAL: Computador Desligado às 03:00 → A Hora Passou → A Tarefa NUNCA Corre!
- ANACRON: Computador Desligado às 03:00 → Ligas o Portátil às 09:15 → O Anacron deteta
- Corre a tarefa em falta às 09:20!
O anacron, configurado no ficheiro /etc/anacrontab, guarda a data da última execução numa pasta de controlo. Quando ligas o computador pela manhã, ele percebe que o dia mudou e corre as rotinas em atraso após um compasso de espera inicial.
O Padrão Moderno: Timers Nativos do Systemd
Nas versões recentes do Debian e Ubuntu, a boa prática de engenharia recomenda substituir tarefas crontab por Timers do Systemd (systemd.io). O systemd divide o trabalho em dois ficheiros: um ficheiro .service que define o que executar e um .timer que define a periodicidade.
CONTROLO NATIVO DE CONCORRÊNCIA NO SYSTEMD
- backup-diario.timer dispara o evento
- O systemd valida: O serviço backup-diario.service já está a correr?
- SIM → Regista um aviso no log e NÃO lança outro processo!
- NÃO → Inicia o serviço num processo isolado e limpo!
A grande vantagem do systemd é que, por omissão, nunca corre duas instâncias do mesmo serviço ao mesmo tempo. Se a execução anterior ainda estiver ativa, o disparo do timer é descartado em segurança.
1. Criar o Ficheiro de Serviço: /etc/systemd/system/backup-noturno.service
[Unit]
Description=Serviço de Backup Noturno de Dados
After=network-online.target postgresql.service
Wants=network-online.target
[Service]
Type=oneshot
User=admin
ExecStart=/usr/local/bin/executar-backup-geral.sh
TimeoutSec=7200
StandardOutput=journal
StandardError=journal
2. Criar o Ficheiro de Temporizador: /etc/systemd/system/backup-noturno.timer
[Unit]
Description=Dispara o backup noturno e recupera execuções falhadas
[Timer]
OnCalendar=*-*-* 03:00:00
# Corre logo se o servidor estiver desligado à hora marcada
Persistent=true
# Adiciona uma variação aleatória de 3 minutos para não sobrecarregar a rede
RandomizedDelaySec=180
[Install]
WantedBy=timers.target
Ativa o teu novo agendador com:
systemctl daemon-reload
systemctl enable --now backup-noturno.timer
systemctl list-timers
Ao usares o flock nos teus scripts e ao migrares rotinas críticas para timers do systemd, eliminas de vez os bloqueios de memória causados por scripts fora de controlo. Esta disciplina de anti-over-engineering protege o teu servidor contra quebras de produção sem necessidade de ferramentas externas caras ou complexas. Podes consultar mais automações no GitHub, mas as ferramentas nativas do teu Linux chegam perfeitamente para te proteger.
No terreno, em conjunto com a experiência acumulada na Webxtek Studio, aplicamos exatamente estes padrões operacionais em setores exigentes como especialistas em devops e arquitetura cloud e provedores de cloud computing e iaas. Quando alinhas a tua estrutura através de alojamento gerido para websites e aplicações e consultoria técnica antes de construir, transformas boas práticas técnicas numa vantagem competitiva real e mensurável para o teu negócio.
[+] [ INDEX_DIRECTORY ] 17 tópicos ▼
- O Inimigo Invisível dos Servidores Linux: A Cascata de Processos no Cron
- Porque Criar Ficheiros .lock à Mão Falha Sempre
- A Solução Robusta do Unix: Bloqueio Atómico com o Utilitário flock
- 1. Proteger Comandos Diretamente no Crontab
- 2. Bloqueio Embutido no Próprio Script com Descritores de Ficheiro
- E em Portáteis ou Computadores Pessoais? O Papel do Anacron
- O Padrão Moderno: Timers Nativos do Systemd
- 1. Criar o Ficheiro de Serviço: /etc/systemd/system/backup-noturno.service
- 2. Criar o Ficheiro de Temporizador: /etc/systemd/system/backup-noturno.timer
- Autor
- Perguntas Frequentes
- Qual é o maior perigo de correr scripts no cron sem controlo de concorrência?
- Como é que o utilitário flock do Linux garante exclusão mútua atómica?
- Qual é a diferença entre usar flock -n e flock sem opções?
- Porque deves preferir os timers do systemd em vez do crontab tradicional no Debian 12?
- Nós Relacionados
- Sequência do Caminho
Perguntas Frequentes
Qual é o maior perigo de correr scripts no cron sem controlo de concorrência?
Se uma tarefa agendada (como um backup de base de dados ou sincronização de ficheiros) demorar mais tempo a terminar do que o intervalo do cron, o agendador lança uma segunda cópia sem verificar nada. Estes processos competem pelo disco e processador, ficam mais lentos e acumulam novas instâncias até esgotarem a memória RAM, momento em que o kernel ativa o OOM Killer e desliga serviços vitais.
Como é que o utilitário flock do Linux garante exclusão mútua atómica?
O comando `flock` recorre à chamada de sistema nativa `flock()` do kernel do Linux sobre um descritor de ficheiro. Ao contrário de scripts caseiros que verificam se existe um ficheiro `.pid` (que falham se dois processos verificarem ao mesmo tempo), o bloqueio ao nível do kernel é atómico: apenas um processo pode deter o fecho de escrita exclusivo em cada instante.
Qual é a diferença entre usar flock -n e flock sem opções?
Sem opções adicionais, o comando `flock /tmp/trava.lock script.sh` fica em pausa à espera que o processo anterior acabe para arrancar. Com a opção não-bloqueante `-n` (ou `--nonblock`), se já existir uma tarefa a correr, o `flock` aborta imediatamente devolvendo o código de saída 1, evitando qualquer acumulação de processos.
Porque deves preferir os timers do systemd em vez do crontab tradicional no Debian 12?
Os timers do systemd oferecem execução automática de tarefas em falta (`Persistent=true`), atrasos aleatórios para diluir tráfego (`RandomizedDelaySec=`), centralização de registos no journald e garantia absoluta de que a mesma tarefa de serviço nunca é disparada em duplicado se a anterior ainda estiver ativa.
[ RELATED_NODES ]
> INICIAR_PROJETO
Precisa de um website que transmita confiança, apareça na pesquisa e dê mais força à sua presença digital? Comece a conversa aqui.