Backup é uma das tarefas mais críticas na administração de sistemas. Nesta aula, vamos explorar como criar scripts de backup eficientes em Bash, desde a estratégia até a automação. Você aprenderá a utilizar ferramentas como tar, gzip, bzip2 e xz, além de implementar rotação de arquivos e agendar execuções com cron.

Um bom script de backup deve ser confiável, automatizável e fácil de manter. Vamos abordar cada aspecto com exemplos práticos, garantindo que você possa adaptar os scripts para suas necessidades.

Estratégia

Antes de escrever qualquer script, é essencial definir uma estratégia de backup. Isso envolve decidir o que será copiado, com que frequência, onde os backups serão armazenados e por quanto tempo. Uma estratégia comum é o esquema 3-2-1: 3 cópias dos dados, em 2 mídias diferentes, com 1 cópia fora do local. No contexto de scripts, podemos implementar backups completos, incrementais ou diferenciais.

Backups completos copiam todos os dados selecionados, são simples mas consomem espaço e tempo. Incrementais copiam apenas as alterações desde o último backup (completo ou incremental), enquanto diferenciais copiam as alterações desde o último backup completo. Em scripts, podemos usar ferramentas como rsync ou tar com opções específicas. Para esta aula, focaremos em backups completos com tar, que é uma ferramenta clássica e poderosa.

#!/bin/bash
# Script para backup completo de diretórios importantes

SOURCE_DIRS="/etc /home /var/www"
BACKUP_DIR="/backup"
DATE=$(date +%Y-%m-%d_%H-%M-%S)
FILENAME="backup_${DATE}.tar.gz"

tar -czf "${BACKUP_DIR}/${FILENAME}" $SOURCE_DIRS
echo "Backup completo criado: ${BACKUP_DIR}/${FILENAME}"

Este script define os diretórios de origem, o diretório de destino, gera um nome de arquivo com data e hora, e usa tar para criar um arquivo compactado. Note que usamos a opção -c (create), -z (gzip), -f (file).

tar e compressão

O comando tar é a ferramenta padrão para arquivar arquivos em Unix/Linux. Ele pode ser combinado com algoritmos de compressão para reduzir o tamanho dos arquivos. As opções mais comuns são -z (gzip), -j (bzip2), -J (xz). Cada um tem trade-offs entre velocidade e taxa de compressão. Para backups, gzip é rápido e amplamente suportado; bzip2 e xz oferecem melhor compressão mas são mais lentos.

Além de criar arquivos, tar pode listar, extrair e atualizar. Em scripts, é importante verificar se o backup foi criado corretamente. Podemos usar a opção -t para testar a integridade, ou verificar o código de saída ($?). Também podemos usar o comando du para verificar o tamanho do arquivo criado.

#!/bin/bash
# Script com verificação de integridade

SOURCE="/home/user/documentos"
BACKUP_DIR="/backup"
DATE=$(date +%Y-%m-%d)
FILENAME="documents_${DATE}.tar.xz"

# Criar backup com compressão xz
tar -cJf "${BACKUP_DIR}/${FILENAME}" "$SOURCE"

# Verificar se o comando foi bem-sucedido
if [ $? -eq 0 ]; then
    echo "Backup criado com sucesso: ${BACKUP_DIR}/${FILENAME}"
    # Listar conteúdo para confirmar
    tar -tJf "${BACKUP_DIR}/${FILENAME}" | head
else
    echo "Erro ao criar backup" >&2
    exit 1
fi

Aqui usamos -J para xz e depois testamos com -tJf. Essa verificação é crucial para garantir que o backup não está corrompido.

Rotação

Manter backups antigos pode encher o disco rapidamente. A rotação de backups é a política de remover backups antigos para liberar espaço. Podemos implementar rotação por número de arquivos ou por idade. Por exemplo, manter apenas os últimos 7 backups diários, ou excluir backups com mais de 30 dias.

No Bash, podemos usar o comando find para localizar arquivos antigos e removê-los. É importante ter cuidado com a remoção, então vamos incluir uma verificação. Uma estratégia comum é manter backups diários por uma semana, semanais por um mês, e mensais por um ano.

#!/bin/bash
# Rotação: manter apenas os últimos 5 backups

BACKUP_DIR="/backup"
KEEP=5

# Listar backups por ordem de criação (mais recentes primeiro)
# e remover os excedentes
cd "$BACKUP_DIR" || exit 1
ls -1t backup_*.tar.gz | tail -n +$((KEEP+1)) | while read file; do
    echo "Removendo backup antigo: $file"
    rm "$file"
done

Este script lista os arquivos que correspondem ao padrão, ordena por tempo (mais recentes primeiro), e remove os que estão além do quinto. Também podemos usar find com -mtime para remover por idade.

# Remover backups com mais de 30 dias
find "$BACKUP_DIR" -name "backup_*.tar.gz" -mtime +30 -delete

Agendamento

Para automatizar backups, usamos o cron, um agendador de tarefas do Unix. O cron lê um arquivo crontab com linhas que especificam quando executar comandos. Cada linha tem cinco campos: minuto, hora, dia do mês, mês e dia da semana. Por exemplo, para executar um backup todos os dias às 2h da manhã, adicionamos a linha: 0 2 * * * /caminho/para/backup.sh.

É importante garantir que o script tenha permissão de execução e que o ambiente esteja configurado corretamente (variáveis PATH, etc.). Podemos também usar o sistema de logs para monitorar a execução. Vamos criar um script completo que combina backup e rotação, e agendá-lo no cron.

#!/bin/bash
# Script completo: backup + rotação

set -euo pipefail

SOURCE_DIRS="/etc /home"
BACKUP_DIR="/backup"
KEEP=7
DATE=$(date +%Y-%m-%d_%H-%M-%S)
FILENAME="backup_${DATE}.tar.gz"

# Criar backup
tar -czf "${BACKUP_DIR}/${FILENAME}" $SOURCE_DIRS

# Verificar integridade
if ! tar -tzf "${BACKUP_DIR}/${FILENAME}" > /dev/null; then
    echo "Falha na verificação do backup" >&2
    exit 1
fi

# Rotação: manter apenas os últimos KEEP backups
ls -1t "${BACKUP_DIR}"/backup_*.tar.gz | tail -n +$((KEEP+1)) | while read old_file; do
    echo "Removendo backup antigo: $old_file"
    rm "$old_file"
done

echo "Backup concluído: ${BACKUP_DIR}/${FILENAME}"

Para agendar, editamos o crontab com crontab -e e adicionamos:

# Backup diário às 2h
0 2 * * * /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1

Redirecionamos a saída para um log para monitoramento. Lembre-se de tornar o script executável com chmod +x.

Boas práticas

  • Sempre testar o script manualmente antes de agendar.
  • Usar caminhos absolutos no script e no cron.
  • Incluir tratamento de erros e logs.
  • Proteger o diretório de backup com permissões adequadas.
  • Considerar criptografia para dados sensíveis.

Referências

Exercícios

  1. Escreva um script que crie um backup compactado com bzip2 de um diretório de sua escolha e verifique a integridade.
  2. Implemente uma rotação que mantenha apenas os últimos 3 backups, usando o comando find com -mtime para remover arquivos com mais de 3 dias.
  3. Crie um script que faça backup de múltiplos diretórios e envie um e-mail de notificação (simule com echo).
  4. Agende o script do exercício 1 para rodar todos os dias às 3:30 da manhã, usando cron, e explique como testar.
  5. Modifique o script completo para usar compressão xz e ajuste a rotação para manter 10 backups. Teste o script e verifique se a rotação funciona.

✓ Resposta:
#!/bin/bash
SOURCE="/home/user/docs"
BACKUP_DIR="/backup"
DATE=$(date +%Y-%m-%d_%H-%M-%S)
FILENAME="docs_${DATE}.tar.bz2"

tar -cjf "${BACKUP_DIR}/${FILENAME}" "$SOURCE"
if tar -tjf "${BACKUP_DIR}/${FILENAME}" > /dev/null; then
    echo "Backup OK: ${BACKUP_DIR}/${FILENAME}"
else
    echo "Backup corrompido" >&2
    exit 1
fi

✓ Resposta:
#!/bin/bash
BACKUP_DIR="/backup"
find "$BACKUP_DIR" -name "backup_*.tar.gz" -mtime +3 -delete

✓ Resposta:
#!/bin/bash
DIRS="/etc /home"
DEST="/backup"
DATE=$(date +%Y-%m-%d)
FILE="multi_${DATE}.tar.gz"

tar -czf "${DEST}/${FILE}" $DIRS && echo "Backup criado: ${DEST}/${FILE}"
# Simulação de envio de e-mail
echo "Backup concluído em $(date)" | mail -s "Backup diário" admin@example.com

✓ Resposta: Adicione a linha 30 3 * * * /caminho/para/script.sh ao crontab com crontab -e. Para testar, execute o script manualmente e verifique se o backup é criado. Depois, use crontab -l para confirmar a linha.

✓ Resposta:
#!/bin/bash
set -euo pipefail

SOURCE_DIRS="/etc /home"
BACKUP_DIR="/backup"
KEEP=10
DATE=$(date +%Y-%m-%d_%H-%M-%S)
FILENAME="backup_${DATE}.tar.xz"

tar -cJf "${BACKUP_DIR}/${FILENAME}" $SOURCE_DIRS
if ! tar -tJf "${BACKUP_DIR}/${FILENAME}" > /dev/null; then
    echo "Erro na verificação" >&2
    exit 1
fi

ls -1t "${BACKUP_DIR}"/backup_*.tar.xz | tail -n +$((KEEP+1)) | while read old; do
    rm "$old"
done

echo "Backup OK: ${BACKUP_DIR}/${FILENAME}"