Backup automatizado com Bash e rsync

1. Introdução ao backup com Bash e rsync

Automatizar backups com scripts shell é uma prática essencial para administradores de sistemas e desenvolvedores que desejam garantir a integridade dos dados sem depender de soluções comerciais complexas. O Bash oferece controle granular sobre o processo, permitindo personalizar cada aspecto da rotina de backup, desde a seleção de arquivos até o tratamento de erros.

O rsync destaca-se como ferramenta central por sua eficiência: ele transfere apenas as diferenças entre origem e destino (sincronização incremental), reduz drasticamente o tráfego de rede e o tempo de execução. Com suporte nativo a SSH, todo o tráfego é criptografado, garantindo segurança em backups remotos.

Pré-requisitos: Bash instalado (padrão em praticamente todas as distribuições Linux), rsync (geralmente disponível via gerenciador de pacotes) e configuração de chaves SSH para autenticação sem senha.

2. Estrutura básica de um script de backup

Um script de backup bem estruturado começa com a definição clara de variáveis. Isso facilita a manutenção e evita erros de digitação.

#!/bin/bash

# Variáveis de configuração
ORIGEM="/home/usuario/dados/"
DESTINO="/mnt/backup/"
LOG="/var/log/backup.log"
DATA=$(date +%Y-%m-%d_%H-%M-%S)
HOST_REMOTO="servidor-backup.local"
SSH_USER="backupuser"

# Verificação de diretórios
if [ ! -d "$ORIGEM" ]; then
    echo "$DATA - ERRO: Diretório de origem não encontrado: $ORIGEM" >> "$LOG"
    exit 1
fi

if [ ! -d "$DESTINO" ]; then
    echo "$DATA - ERRO: Diretório de destino não encontrado: $DESTINO" >> "$LOG"
    exit 1
fi

# Comando rsync essencial
echo "$DATA - Iniciando backup..." >> "$LOG"
rsync -avz --delete --progress "$ORIGEM" "$SSH_USER@$HOST_REMOTO:$DESTINO" >> "$LOG" 2>&1

# Verificação de sucesso
if [ $? -eq 0 ]; then
    echo "$DATA - Backup concluído com sucesso" >> "$LOG"
else
    echo "$DATA - ERRO: Backup falhou" >> "$LOG"
    exit 1
fi

O comando rsync -avz --delete --progress combina: modo archive (-a) que preserva permissões, timestamps e links simbólicos; verbose (-v) para saída detalhada; compressão (-z) para economizar largura de banda; --delete para remover arquivos no destino que não existem mais na origem; e --progress para acompanhamento em tempo real.

3. Implementando backup incremental e diferencial

Backups completos copiam todos os arquivos a cada execução, consumindo muito espaço e tempo. Backups incrementais copiam apenas alterações desde o último backup (completo ou incremental), economizando recursos. Backups diferenciais copiam alterações desde o último backup completo.

O rsync permite implementar backups incrementais eficientes usando --link-dest. Essa opção cria hard links para arquivos inalterados, ocupando espaço apenas para os arquivos modificados.

#!/bin/bash

# Configuração de rotação
BASE="/backup/semanal"
DIARIO="/backup/diario/$(date +%Y-%m-%d)"
SEMANA_ANTERIOR="/backup/semana_anterior"

# Backup incremental com hard links
rsync -avz --delete --link-dest="$SEMANA_ANTERIOR" "$ORIGEM" "$DIARIO"

# Rotação semanal: mover backup diário mais antigo para semanal
if [ "$(date +%u)" -eq 7 ]; then  # Domingo
    mv "$DIARIO" "$BASE/$(date +%Y-%m-%d)"
fi

Neste exemplo, o backup diário referencia o backup semanal anterior via --link-dest. Arquivos idênticos são representados por hard links, economizando espaço em disco. A rotação semanal move o backup mais antigo para um diretório separado.

4. Tratamento de erros e logging

Um script robusto deve capturar e registrar erros de forma adequada. O redirecionamento 2>&1 combina saída padrão e de erro no mesmo arquivo de log, facilitando a análise.

#!/bin/bash

LOG="/var/log/backup.log"
ERRO_LOG="/var/log/backup_erro.log"

# Função de logging com timestamp
log() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$LOG"
}

# Execução com captura de código de retorno
log "Iniciando backup"
rsync -avz --delete "$ORIGEM" "$DESTINO" >> "$LOG" 2>&1
RSYNC_EXIT=$?

if [ $RSYNC_EXIT -ne 0 ]; then
    log "ERRO: rsync falhou com código $RSYNC_EXIT"
    echo "Backup falhou em $(date)" >> "$ERRO_LOG"

    # Notificação via syslog
    logger -p user.err "Backup falhou: código $RSYNC_EXIT"

    # Opcional: envio de e-mail
    # mail -s "Falha no backup" admin@exemplo.com < "$ERRO_LOG"
    exit $RSYNC_EXIT
fi

log "Backup concluído com sucesso"

O código de retorno $? do rsync é crucial: 0 indica sucesso, valores diferentes indicam falhas específicas (1 para erros de sintaxe, 2 para erros de conexão, etc.). A função logger envia mensagens para o syslog, permitindo monitoramento centralizado.

5. Agendamento com cron e systemd timers

O cron é o agendador tradicional do Unix. Para executar o script diariamente às 2h da manhã:

# Editar crontab: crontab -e
0 2 * * * /usr/local/bin/backup.sh

Cuidados importantes com cron:
- Variáveis de ambiente como PATH são limitadas. Defina caminhos absolutos no script ou configure PATH no crontab.
- Redirecione saída para log: 0 2 * * * /usr/local/bin/backup.sh >> /var/log/backup_cron.log 2>&1

Systemd timers oferecem vantagens como logging integrado via journald e gerenciamento de dependências:

# /etc/systemd/system/backup.service
[Unit]
Description=Script de backup automatizado
After=network-online.target

[Service]
Type=oneshot
ExecStart=/usr/local/bin/backup.sh
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target
# /etc/systemd/system/backup.timer
[Unit]
Description=Timer para backup diário
Requires=backup.service

[Timer]
OnCalendar=daily
Persistent=true
RandomizedDelaySec=1800

[Install]
WantedBy=timers.target

Ative com: systemctl enable --now backup.timer

6. Segurança e boas práticas

Para backups remotos via SSH, configure chaves sem passphrase ou use ssh-agent para armazenar a senha na memória:

# Gerar chave (sem passphrase para automação)
ssh-keygen -t ed25519 -f ~/.ssh/backup_key -N ""

# Copiar chave pública para o servidor remoto
ssh-copy-id -i ~/.ssh/backup_key.pub usuario@servidor

No servidor remoto, restrinja o que pode ser executado com a chave editando ~/.ssh/authorized_keys:

command="/usr/local/bin/restrito-rsync.sh",no-agent-forwarding,no-port-forwarding,no-pty,no-user-rc,no-X11-forwarding ssh-ed25519 AAA... chave-backup

Nunca exponha senhas no script. Use variáveis de ambiente ou arquivos .env protegidos:

# .env (permissões 600)
SSH_PASS="minha_senha"
# No script
source /caminho/.env
export SSHPASS="$SSH_PASS"
rsync -avz --rsh="sshpass -e ssh" "$ORIGEM" "$DESTINO"

7. Monitoramento e testes do backup

Antes de colocar em produção, teste o script em ambiente isolado. Crie um diretório de teste com arquivos de exemplo e verifique o comportamento.

Script de verificação pós-backup:

#!/bin/bash
# Verifica integridade comparando checksums

ORIGEM="/home/usuario/dados/"
DESTINO="/mnt/backup/"
LOG_VERIFICACAO="/var/log/backup_verificacao.log"

log() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$LOG_VERIFICACAO"
}

log "Iniciando verificação de integridade"

# Gerar checksums da origem
find "$ORIGEM" -type f -exec sha256sum {} \; > /tmp/origem.sha256

# Gerar checksums do destino
find "$DESTINO" -type f -exec sha256sum {} \; > /tmp/destino.sha256

# Comparar
if diff /tmp/origem.sha256 /tmp/destino.sha256 > /dev/null 2>&1; then
    log "Verificação concluída: todos os arquivos íntegros"
else
    log "ERRO: Discrepância encontrada nos checksums"
    diff /tmp/origem.sha256 /tmp/destino.sha256 >> "$LOG_VERIFICACAO"
fi

rm -f /tmp/origem.sha256 /tmp/destino.sha256

Para alertas automáticos, integre com serviços como Slack via webhook:

WEBHOOK_URL="https://hooks.slack.com/services/TOKEN"
curl -X POST -H 'Content-type: application/json' \
    --data '{"text":"Backup concluído com sucesso"}' \
    "$WEBHOOK_URL"

8. Exemplo completo e considerações finais

Script funcional completo com todas as funcionalidades discutidas:

#!/bin/bash

# =============================================
# Script de Backup Automatizado com Bash e rsync
# Versão: 2.0
# =============================================

# Configurações
ORIGEM="/home/usuario/dados/"
DESTINO_LOCAL="/mnt/backup/local/"
DESTINO_REMOTO="backup@servidor:/backup/"
LOG="/var/log/backup_automatizado.log"
EXCLUIR="--exclude='.cache' --exclude='tmp'"

# Timestamp
DATA=$(date '+%Y-%m-%d %H:%M:%S')
DATA_ARQUIVO=$(date '+%Y%m%d_%H%M%S')

# Função de log
log() {
    echo "$DATA - $1" >> "$LOG"
}

# Verificação de pré-requisitos
for cmd in rsync ssh; do
    if ! command -v $cmd &> /dev/null; then
        log "ERRO: $cmd não encontrado. Instale antes de executar."
        exit 1
    fi
done

# Backup local
log "Iniciando backup local"
rsync -avz --delete $EXCLUIR "$ORIGEM" "$DESTINO_LOCAL" >> "$LOG" 2>&1
if [ $? -ne 0 ]; then
    log "ERRO: Backup local falhou"
    exit 1
fi
log "Backup local concluído"

# Backup remoto via SSH
log "Iniciando backup remoto"
rsync -avz --delete -e "ssh -i ~/.ssh/backup_key" $EXCLUIR "$ORIGEM" "$DESTINO_REMOTO" >> "$LOG" 2>&1
if [ $? -ne 0 ]; then
    log "ERRO: Backup remoto falhou"
    exit 1
fi
log "Backup remoto concluído"

log "Todos os backups concluídos com sucesso"
exit 0

Para estender o script: adicione compactação com gzip, suporte a múltiplas origens com loops, ou backup de bancos de dados com mysqldump antes do rsync.

Melhores práticas resumidas:
- Sempre teste em ambiente isolado antes de produção
- Use logging detalhado com timestamps
- Configure notificações para falhas
- Mantenha chaves SSH seguras e restritas
- Documente o script para manutenção futura

Backups automatizados com Bash e rsync oferecem uma solução poderosa, flexível e gratuita para proteção de dados. Com os conceitos apresentados, você pode construir sistemas de backup robustos adaptados às suas necessidades específicas.

Referências