Meu servidor executa vários cron jobs à meia-noite. Cada trabalho cria um backup de algo, criando um tarball e compactando-o com xz
.
Como xz
é um porco de CPU e memória, adicionei um atraso aleatório a cada trabalho, para que eles "não deveriam" se sobrepor. Mas de vez em quando isso acontece e carrega muito o servidor.
Suposições:
- Com base no meu tráfego, meia-noite é o melhor horário para fazer backups - mas ainda há tráfego (e é por isso que quero evitar carga excessiva)
- Cada aplicativo voltado para o público está associado a seu próprio trabalho de backup e eles são desacoplados (eles não se conhecem) - portanto, não posso mesclar os trabalhos cron de backup em um único trabalho, pois preciso dessa granularidade
- Não consigo codificar a hora de início de cada um, pois isso aumentaria a manutenção - para adicionar um aplicativo ao servidor (via ansible), basta implantá-lo e soltar um trabalho cron de backup (agendado para meia-noite) em
/etc/cron.d/
, e o random atraso antes do início do trabalho geralmente é bom o suficiente - Eu estrangulo os trabalhos um pouco via
tar ... | pv --rate-limit ... | xz ...
- mas, embora isso reduza a carga por trabalho, também diminui a velocidade de todos os trabalhos e, portanto, aumenta a probabilidade de vários trabalhos serem executados simultaneamente (que, quando somados, podem consumir 100% da CPU)
Uma solução possível é que cada trabalho crie um arquivo temporário que sinalize que está ocupado e, em seguida, exclua-o. O problema é que se um trabalho detecta esse arquivo, o que ele faz? Dorme? Por quanto tempo? Eu poderia fazê-lo dormir por um período aleatório usando at
, mas se algo der errado com meus scripts de backup, eu poderia ter uma enorme fila de trabalhos competindo entre si. Mais uma dor de cabeça de manutenção.
Então, como se costuma resolver esse problema? Basicamente, uma maneira simples de agendar cron jobs relacionados, sem deixá-los atrapalhar uns aos outros e sem a necessidade de ajustar os horários de início.
Use operadores de shell , por exemplo, para executar à meia
command1
-command2
noite, independentemente da saída anterior, use:Alternativamente, você pode executar
command2
apenas secommand1
for concluído com êxito (retorna com status de saída zero):O último talvez seja mais útil quando a falha de
command1
provavelmente significar uma falha subjacente que impede o sucesso decommand2
.Distribuir aleatoriamente os horários de início é bom para evitar horários de pico e é fácil de fazer com o Ansible. Mas não garante realmente que os recursos estarão disponíveis para sustentar vários trabalhos de compactação simultâneos. Existem vários métodos de como fazer backups de baixo impacto, considere alguns ou todos eles.
Execute sua lista de comandos por meio de um programa que estrangula com base na CPU. Por exemplo, o GNU paralelo
--limit 100%
só será executado se a média de carga estiver abaixo do número de CPUs.Cada trabalho tenta adquirir um de um pequeno número de bloqueios. Como com
flock
util-linux, Python ou Perl. Parece simples, mas manter vários deles será chato. Considero um comando wrapper com gerenciamento de tarefas integrado mais robusto, como o GNU paralelo.Avalie seu algoritmo de compactação. zstd é moderno e rápido, para apenas um pouco mais de memória.
Distribua os trabalhos de backup por mais horas. Pense se, digamos, 00:00 às 03:00 é aceitável para seus requisitos de desempenho e backup.
Adicione CPU. Pode ser caro dimensionar para capacidade de pico, mas permite mais threads de compressão.
Descarregue os backups inteiramente para outro host. Faça uma matriz de armazenamento ou um instantâneo de discos baseado em nuvem. Apresente para um anfitrião diferente. Backup de lá.
Dê uma olhada na resposta de @JohnMahowald para obter uma excelente lista de opções, incluindo o manuseio inteligente da contenção.
O que decidi fazer foi em vez de adicionar trabalhos de backup a
/etc/cron.d
, vou adicioná-los a um diretório cron personalizado, por exemplo/etc/cron.backupjobs/
.Em seguida, adicionarei um trabalho "mestre" ao
/etc/cron.d/
qual executa trabalhos/etc/cron.backupjobs/
sequencialmente .