Backup de diretórios e MySQL dbs direto pra AWS S3
Quem me conhece sabe o quando eu sou freak com logs, registros. backups etc. E recentemente isso me salvou a vida quando roubaram meu computador.
Mas como trabalho com isso, não dá pra pensar apenas no âmbito pessoal. Hoje, para alguns projetos específicos, eu rodo 3 máquinas na AWS: um server nginx, um server MySQL e um lab com apache pra coisas mais rápidas. No total, são duas máquinas de produção e um lab com muitas coisas importantes que não posso nem sonhar em perder. Os códigos estão todos em repositórios git, mas os arquivos de upload, logs etc, esse não estão em lugar nenhum a não essas máquinas.
Então pensei numa maneira simples e de baixo custo para fazer backup disso tudo: backups completos feitos a cada X tempo e transferidos pro S3. Nos arquivos de publicação, logs e configuração, a frequência é de uma vez ao dia. Pois por mais que seja produção, a quantidade de arquivos trafegados não é tão grande assim. Mas nos bancos de dados, faço um dump completo de todos os schemas a cada 15 minutos. Então temos 4 dumps por hora e 96 por dia. Para isso eu uso 3 scripts:
Scripts
Todos os arquivos podem ser encontrados aqui: https://github.com/quagliato/backup-with-shell
1 Backup de diretório para um tar.gz
dir_bkp.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
NOW=$(date +"%Y-%m-%d_%H-%M-%S_%z")
Y=$(date +"%Y")
M=$(date +"%m")
BASENAME=$1
SOURCE_DIR=$2
STASH_DIR=$3
WORK_DIR=$4
STASH_DIR=$STASH_DIR/$BASENAME/$Y/$M
FILENAME="$BASENAME-$NOW"
echo "******************************************************************************"
echo "BEGIN: $NOW"
echo "------------------------------------------------------------------------------"
cd $WORK_DIR
echo "ENTERING WORK DIR $WORK_DIR"
mkdir -p $STASH_DIR
echo "CREATED STASH DIR $STASH_DIR"
echo "CREATING FILE..."
tar zcf $FILENAME.tar.gz $SOURCE_DIR
echo "FILE CREATED $FILENAME.tar.gz"
mv $FILENAME.tar.gz $STASH_DIR/
echo "FILE MOVED TO $STASH_DIR"
echo "------------------------------------------------------------------------------"
END=$(date +"%Y-%m-%d_%H-%M-%S_%z")
echo "BEGIN: $NOW"
echo " END: $END"
echo "******************************************************************************"
Esse código recebe 4 parâmetros:
- BASENAME: o nome que será padrão nos arquivos
- SOURCE_DIR: o diretório onde ele irá buscar os arquivos pra fazer o tar.gz
- STASH_DIR: onde ele guardará o tar.gz criado
- WORK_DIR: onde ele trabalhará
Nesse caso, o STASH_DIR é sobrescrito por uma concatenacao de STASH_DIR/BASENAME/ ANO/MES para que os arquivos fiquem separados da melhor maneira.
2 Backup de todos os bancos de dados para um arquivo sql
mysql_all_dbs_bkp.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
NOW=$(date +"%Y-%m-%d_%H-%M-%S_%z")
Y=$(date +"%Y")
M=$(date +"%m")
D=$(date +"%d")
BASENAME=$1
STASH_DIR=$2
STASH_DIR=$STASH_DIR/$BASENAME/$Y/$M/$D
WORK_DIR=$3
FILENAME="$BASENAME-$NOW"
DB_HOST=$4
DB_USER=$5
DB_PASS=$6
echo "******************************************************************************"
echo "BEGIN: $NOW"
echo "------------------------------------------------------------------------------"
cd $WORK_DIR
echo "ENTERED WORK DIR"
mkdir -p $STASH_DIR
echo "CREATED STASH DIR"
echo "CREATING FILE..."
mysqldump -h $DB_HOST --user=$DB_USER --password="${DB_PASS}" --all-databases > $WORK_DIR/mysqldump.sql
tar zcf $FILENAME.tar.gz $WORK_DIR/mysqldump.sql
echo "FILE CREATED $FILENAME.tar.gz"
mv $FILENAME.tar.gz $STASH_DIR/
echo "FILE MOVED."
echo "------------------------------------------------------------------------------"
END=$(date +"%Y-%m-%d_%H-%M-%S_%z")
echo "BEGIN: $NOW"
echo " END: $END"
echo "******************************************************************************"
rm -f $WORK_DIR/mysqldump.sql
Já esse código recebe um total de 6 parâmetros:
- BASENAME: o nome que será padrão nos arquivos
- SOURCE_DIR: o diretório onde ele irá buscar os arquivos pra fazer o tar.gz
- STASH_DIR: onde ele guardará o tar.gz criado
- DB_HOST: o endereço do server de MySQL que será feito o backup
- DB_USER: usuário do DB
- DB_PASS: senha do usuário
Aqui o STASH_DIR é sobrescrito por uma concatenacao de STASH_DIR/BASENAME/ ANO/MES/DIA já que a quantidade de arquivos por dia é grande o suficiente para ficar ruim de encontrar um arquivo se separassemos apenas por mês.
Enviar pro S3
send_s3.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
NOW=$(date +"%Y-%m-%d_%H-%M-%S_%z")
STASH_DIR=$1
UPLOADED_DIR=$2
BUCKET=$3
echo "******************************************************************************"
echo "BEGIN: $NOW"
echo "------------------------------------------------------------------------------"
cd $STASH_DIR
for file in $(find . -name '*.tar.gz')
do
resource="/${BUCKET}/${file}"
echo ${resource} > resource.path;
sed "s/\/.\//\//g" resource.path > resource.path.new;
resource=`cat resource.path.new`;
echo " ORIGIN FILE: ${file}"
echo "DESTINY FILE: ${resource}"
echo "* * * * *";
contentType="application/x-compressed-tar"
dateValue=`date -R`
stringToSign="PUT\n\n${contentType}\n${dateValue}\n${resource}"
s3Key=XXXXXXXXXXXXXXXXXXXX # correct length
s3Secret=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX # correct length
signature=`echo -en ${stringToSign} | openssl sha1 -hmac ${s3Secret} -binary | base64`
curl -X PUT -T "${file}" \
-H "Host: ${BUCKET}.s3.amazonaws.com" \
-H "Date: ${dateValue}" \
-H "Content-Type: ${contentType}" \
-H "Authorization: AWS ${s3Key}:${signature}" \
https://${BUCKET}.s3.amazonaws.com/${file}
# mv $file $UPLOADED_DIR/$file
rm resource.path;
rm resource.path.new;
done
echo "------------------------------------------------------------------------------"
END=$(date +"%Y-%m-%d_%H-%M-%S_%z")
echo "BEGIN: $NOW"
echo " END: $END"
echo "******************************************************************************"
Esse script recebe 3 parâmetros:
- STASH_DIR: onde ele buscará os arquivos que serão subidos pro S3
- UPLOADED_DIR: onde ele guardará os arquivos que foram subidos pro S3 (para uma possível auditoria sem perda)
- BUCKET: bucket do S3 que os arquivos serão salvos
E ele também precisa que 2 valores sejam alterados:
- s3Key
- s3Secret
Execução
Eu executo os 3 através de crontab com um usuário específico para essa rotina ( bkpagent) e tudo funciona perfeitamente:
1
2
3
4
5
6
7
8
# maquina 001
00 08 * * * /opt/backup/dir_bkp.sh nginx-pub /var/www /opt/backup/stash /opt/backup
00 08 * * * /opt/backup/dir_bkp.sh nginx-conf /etc/apache2 /opt/backup/stash /opt/backup
# maquina 002
15,30,45 * * * * /opt/backup/mysql-bkp.sh mysql-bkp /opt/backup/stash /opt/backup $DB_HOST $DB_USER $DB_PASS
# maquina 003
00 08 * * * /opt/backup/dir_bkp.sh a2-pub /var/www /opt/backup/stash /opt/backup
00 08 * * * /opt/backup/dir_bkp.sh a2-conf /etc/apache2 /opt/backup/stash /opt/backup
Já precisei restaurar algumas coisas e o sofrimento foi praticamente zero. Mas, obviamente, a minha infra não demanda tanto espaço assim e isso facilita. Meus dumps de DB não chegam a 100MB então fazer o backup e upload de tudo a cada 15 minutos é possível.
Mas essa é uma medida que cada um tem que descobrir na própria infra.