Backup incremental com rsync e hardlinks

Esse artigo permanece aqui por motivos históricos. O script é funcional mas recomendo a utilização do aplicativo rsnapshot que provê funcionalidade similiar com muito mais recursos. Atualizado em 13/12/2012

Desde que assinei um VPS onde hospedo esse site e outros projetos pessoais eu venho enrolando para implementar algum tipo de backup. Meu objetivo é ter uma cópia de segurança dos sites e arquivos de configuração do servidor. Queria algo bem simples então apostei no rsync, por SSH eu sincronizava os diretórios remotos para meu disco local.

Tudo jóia até eu fazer vários backups e ver como aquilo ia consumindo espaço em disco. A cada novo backup, poucos arquivos mudavam mas eu sempre copiava tudo. Eu precisava é de um backup incremental para resolver o problema, para isso acabei montando um script que usa o rsync e a opção -l do cp para gerar hardlinks. Um hardlink não é nada mais do que um link para um inode já existente no disco, assim o hardlink e o arquivo alvo são o mesmo arquivo no file system. Sendo o mesmo arquivo eu economizo espaço.

O que o script faz é procurar um backup anterior no disco, se ele encontra cria um novo diretório feito apenas de hardlinks para esse backup anterior. Depois disso rodamos o rsync contra esse novo diretório, dessa maneira atualizamos o que foi modificado desde o último backup. Para que o novo backup reflita exatamente o que esta no servidor na data atual, rodamos o rsync com a opção --delete que remove do destino arquivos não existentes na origem.

Simples e bem eficiente, abaixo um exemplo da economia:

$ du -csh *
59M 20080907
65M 20081026
14M 20081109

O backup 20080907 foi o primeiro feito com rsync e tem 59MB. Para o backup 20081026 eu rodei novamente o rsync sem criar hardlinks, ou seja baixei todo o conteúdo novamente mais as diferenças, fiz 65MB de download. Para o último backup eu usei o script com hardlinks, baixei apenas a diferença em relação ao último backup, como o texto mostra, apenas 14MB. Para melhorar a coisa, o script também remove backups muito antigos.

Convencido da utilidade? Vamos ao script:

#!/usr/bin/env bash
#
# Faz backups incrementais de diretorios em um servidor remoto utilizando rsync e
# hardlinks para economia de banda e espaço em disco.
#
# Por Vinicius Figueiredo <viniciusfs [arroba] gmail.com>
# Dominio Publico
#

SERVER="servidor.com.br"
DIRS_TO_COPY="/var/www /etc"
BACKUP_DIR="/backup"
RETENTION=15

DATE=`date +%Y%m%d`
LOG="${0}_${DATE}.log"

CURRENT_BACKUP="${BACKUP_DIR}/${DATE}"

echo "Iniciando script em `date`" > ${LOG}

if [ -e ${BACKUP_DIR}/last ]; then
    LAST_BACKUP=`cat ${BACKUP_DIR}/last`

    if [ -d ${LAST_BACKUP} ]; then
        echo "Ultimo backup encontrando, linkando arquivos..." >> ${LOG}
        cp -la ${LAST_BACKUP} ${CURRENT_BACKUP} >> ${LOG} 2>&1
    fi
else
    echo "Nenhum backup anterior encontrado, criando diretorio para backup atual..." >> ${LOG}
    mkdir ${CURRENT_BACKUP} >> ${LOG} 2>&1
fi

for dir in ${DIRS_TO_COPY}; do
    echo "Iniciando backup de ${SERVER}:${dir}" >> ${LOG}
    rsync -av --delete ${SERVER}:${dir} ${CURRENT_BACKUP} >> ${LOG} 2>&1
done

echo ${CURRENT_BACKUP} &gt; ${BACKUP_DIR}/last

for dir in `find ${BACKUP_DIR} -maxdepth 1 -type d -mtime +${RETENTION}`; do
    echo "Removendo backup ${dir}" >> ${LOG}
    rm -rf ${dir} &gt;&gt; ${LOG} >> ${LOG} 2>&1
done

O que você deve configurar são as 4 primeiras variáveis.

  • SERVER é o servidor de onde iremos tirar esse backup.
  • DIRS_TO_COPY é uma lista de diretórios a serem copiados desse servidor, os diretórios devem ser separados por espaço.
  • BACKUP_DIR é o destino local do backup.
  • RETENTION é a retenção dos backups em dias. Backups mais antigos do que esse número serão deletados no final do script.

Para automatizar a tarefa você pode criar um job na cron. Vale lembrar que você vai precisar ter acesso via SSH sem senha ao servidor alvo dos backups para que fique 100% automático.

[, , , , , , ]

Comentários