Задача — набросать скрипт для создания бекапов всех баз сервера БД и сохранять их в корзину.
В общем — всё просто: бекапы делаем с помощью mysqldump, в S3 корзину пушим с помощью AWS CLI.
Далее:
- создаём корзину для бекапов
- создаём пользователя с read-write политикой для доступа к этой корзине
- и сам скрипт
Для простоты — всё вносим в bash-скрипт, его — добавляем в крон.
Интереснее было бы скрипт написать на Python с boto3 — но время.
Содержание
Подготовка
AWS
Корзина S3
Создаём корзину, в которой будут храниться бекапы баз данных — create-bucket:
[simterm]
$ aws s3api create-bucket --bucket rtfm-prod-db-backups --region eu-west-1
{
"Location": "/rtfm-prod-db-backups"
}
[/simterm]
IAM политика
Создаём политику доступа только к этой корзине, файл rtfm-prod-db-backups.policy.json:
The policy is separated into two parts because the ListBucket action requires permissions on the bucket while the other actions require permissions on the objects in the bucket.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:ListBucket"],
"Resource": ["arn:aws:s3:::rtfm-prod-db-backups"]
},
{
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:DeleteObject"
],
"Resource": ["arn:aws:s3:::rtfm-prod-db-backups/*"]
}
]
}
Подробнее — тут>>>.
Добавляем эту политику в аккаунт — create-policy:
[simterm]
$ aws iam create-policy --policy-name rtfm-prod-db-backups --policy-document file://rtfm-prod-db-backups.policy.json
{
"Policy": {
"PolicyName": "rtfm-prod-db-backups",
"PolicyId": "ANPAI647ABN34GYLW5I4O",
"Arn": "arn:aws:iam::264***286:policy/rtfm-prod-db-backups",
"Path": "/",
"DefaultVersionId": "v1",
"AttachmentCount": 0,
"IsAttachable": true,
"CreateDate": "2017-10-13T08:55:27.670Z",
"UpdateDate": "2017-10-13T08:55:27.670Z"
}
}
[/simterm]
Проверяем:
[simterm]
$ aws iam list-policies --scope Local
{
"Policies": [
{
"PolicyName": "rtfm-prod-db-backups",
"PolicyId": "ANPAI647ABN34GYLW5I4O",
"Arn": "arn:aws:iam::264***286:policy/rtfm-prod-db-backups",
"Path": "/",
"DefaultVersionId": "v1",
"AttachmentCount": 0,
"IsAttachable": true,
"CreateDate": "2017-10-13T08:55:27Z",
"UpdateDate": "2017-10-13T08:55:27Z"
}
]
}
[/simterm]
IAM пользователь
Создаём отдельного пользователя для бекапов:
[simterm]
$ aws iam create-user --user-name rtfm-prod-db-backups
{
"User": {
"Path": "/",
"UserName": "rtfm-prod-db-backups",
"UserId": "AID***AR2",
"Arn": "arn:aws:iam::264***286:user/rtfm-prod-db-backups",
"CreateDate": "2017-10-13T08:57:40.966Z"
}
}
[/simterm]
Подключаем ему созданную ранее политику:
[simterm]
$ aws iam attach-user-policy --user-name rtfm-prod-db-backups --policy-arn arn:aws:iam::264***286:policy/rtfm-prod-db-backups
[/simterm]
Проверяем:
[simterm]
$ aws iam list-attached-user-policies --user-name rtfm-prod-db-backups
{
"AttachedPolicies": [
{
"PolicyName": "rtfm-prod-db-backups",
"PolicyArn": "arn:aws:iam::264***286:policy/rtfm-prod-db-backups"
}
]
}
[/simterm]
Создаём ACCESS_KEY и SECRET_ACCESS_KEY:
[simterm]
$ aws iam create-access-key --user-name rtfm-prod-db-backups
{
"AccessKey": {
"UserName": "rtfm-prod-db-backups",
"AccessKeyId": "AKI***ZJA",
"Status": "Active",
"SecretAccessKey": "dAQ***oDT",
"CreateDate": "2017-10-13T09:02:32.872Z"
}
}
[/simterm]
Проверяем доступ.
Создаём новый именованный профиль для CLI:
[simterm]
$ aws configure --profile rtfm-prod-db-backups AWS Access Key ID [None]: AKI***ZJA AWS Secret Access Key [None]: dAQ***oDT Default region name [None]: eu-west-1 Default output format [None]: json
[/simterm]
Пробуем загрузить файл:
[simterm]
$ touch testfile $ aws s3 --profile rtfm-prod-db-backups cp testfile s3://rtfm-prod-db-backups upload: ./testfile to s3://rtfm-prod-db-backups/testfile
[/simterm]
И пробуем просмотреть корзину:
[simterm]
$ aws s3 --profile rtfm-prod-db-backups ls s3://rtfm-prod-db-backups 2017-10-13 12:09:02 0 testfile
[/simterm]
bash скрипт
Потребуется AWS CLI, на сервере устанавливаем:
[simterm]
# apt update && apt -y install python-pip # pip install awscli
[/simterm]
Скрипт состоит из трёх функций:
mysql_all_dbs_backup (): получает список всех баз и для каждой (кромеperformance_schemaиmysql) выполняетmysqldumppush_to_s3 (): загружает файл в AWS S3 через AWS CLIsave_backups (): находит все файлы бекапов, созданныеmysql_all_dbs_backup (), и вызываетpush_to_s3 ()для сохранения их в корзину
Что ещё хорошо бы сделать — это удаление старых бекапов скриптом или настроить AWS S3 Lifecycle политики.
Сам скрипт mysql_all_dbs_backups_to_s3.sh:
#!/usr/bin/env bash
MYSQL_ROOT=root
MYSQL_PASS=password
AWS_ACCESS_KEY_ID=keyid
AWS_SECRET_ACCESS_KEY=secretkey
S3_BACKUPS_BUCKET="rtfm-prod-db-backups"
BACKUPS_LOCAL_PATH="/tmp"
BACKUP_DATE="$(date +"%d_%m_%y")"
mysql_all_dbs_backup () {
# get all databases list
databases=$(mysql -u $MYSQL_ROOT -p$MYSQL_PASS -e "SHOW DATABASES;" | tr -d "| " | grep -v Database)
cd $BACKUPS_LOCAL_PATH || { echo "ERROR: can't cd to the $BACKUPS_LOCAL_PATH! Exit."; exit 1; }
for db in $databases; do
if [[ "$db" != "information_schema" ]] && [[ "$db" != "performance_schema" ]] && [[ "$db" != "mysql" ]] && [[ "$db" != _* ]] ; then
# e.g. 14_10_17_rtfm_db1.sql.gz
local backup_name="$BACKUP_DATE"_$db.sql.gz
echo "Dumping database: $db to $BACKUPS_LOCAL_PATH/$backup_name"
mysqldump -u $MYSQL_ROOT -p$MYSQL_PASS --databases $db | gzip > $backup_name
[[ -e $backup_name ]] && echo "Database $db saved to $backup_name..." || echo "WARNING: can't find $db dump!"
fi
done
}
push_to_s3 () {
local backup_name=$1
echo "Uploading file $backup_name..."
AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY aws s3 cp $backup_name s3://$S3_BACKUPS_BUCKET/$backup_name
}
save_backups () {
# get all databases list
databases=$(mysql -u $MYSQL_ROOT -p$MYSQL_PASS -e "SHOW DATABASES;" | tr -d "| " | grep -v Database)
cd $BACKUPS_LOCAL_PATH || { echo "ERROR: can't cd to the $BACKUPS_LOCAL_PATH! Exit."; exit 1; }
# like an assert - if $databases empty - exit
[[ $databases ]] || exit 1
for db in $databases; do
if [[ "$db" != "information_schema" ]] && [[ "$db" != "performance_schema" ]] && [[ "$db" != "mysql" ]] && [[ "$db" != _* ]]; then
# e.g. 14_10_17_rtfm_db1.sql.gz
local backup_name="$BACKUP_DATE"_$db.sql.gz
if [[ -e $backup_name ]]; then
# for testing before run - echo instead of push_to_s3() file instead of rm
# echo "Pushing $backup_name && file $backup_name && echo "Done."
push_to_s3 $backup_name && rm $backup_name && echo "Done."
else
echo "ERROR: can't find local backup file $backup_name! Exit."
exit 1
fi
fi
done
}
echo -e "\nStarting MySQL backup at $(date) to /tmp/\n"
if mysql_all_dbs_backup; then
echo -e "\nLocal backups done."
else
echo -e "\nERROR during performing backup! Exit.\n"
exit 1
fi
echo -e "\nStarting S3 upload to s3://$S3_BACKUPS_BUCKET\n"
if save_backups; then
echo -e "\nUpload done.\n"
else
echo -e "\nERROR during upload to S3!. Exit.\n"
exit 1
fi
Запускаем:
[simterm]
root@ip-172-31-64-60:/home/admin# ./scripts/mysql_all_dbs_backups_to_s3.sh Starting MySQL backup at Sat Oct 14 06:50:48 UTC 2017 to /tmp/ Dumping database: m_blog to /tmp/14_10_17_m_blog.sql.gz Database m_blog saved to 14_10_17_m_blog.sql.gz... Dumping database: one to /tmp/14_10_17_one.sql.gz Database one saved to 14_10_17_one.sql.gz... Dumping database: rtfm_db1 to /tmp/14_10_17_rtfm_db1.sql.gz Database rtfm_db1 saved to 14_10_17_rtfm_db1.sql.gz... Dumping database: setevoy_ki to /tmp/14_10_17_setevoy_ki.sql.gz Database setevoy_ki saved to 14_10_17_setevoy_ki.sql.gz... Local backups done. Starting S3 upload to s3://rtfm-prod-db-backups Uploading file 14_10_17_m_blog.sql.gz... upload: ./14_10_17_m_blog.sql.gz to s3://rtfm-prod-db-backups/14_10_17_m_blog.sql.gz Done. Uploading file 14_10_17_one.sql.gz... upload: ./14_10_17_one.sql.gz to s3://rtfm-prod-db-backups/14_10_17_one.sql.gz Done. Uploading file 14_10_17_rtfm_db1.sql.gz... upload: ./14_10_17_rtfm_db1.sql.gz to s3://rtfm-prod-db-backups/14_10_17_rtfm_db1.sql.gz Done. Uploading file 14_10_17_setevoy_ki.sql.gz... upload: ./14_10_17_setevoy_ki.sql.gz to s3://rtfm-prod-db-backups/14_10_17_setevoy_ki.sql.gz Done. Upload done.
[/simterm]
Проверяем содержимое корзины:
[simterm]
$ aws s3 ls s3://rtfm-prod-db-backups 2017-10-14 09:50:55 163857 14_10_17_m_blog.sql.gz 2017-10-14 09:50:56 2014052 14_10_17_one.sql.gz 2017-10-14 09:50:58 9775699 14_10_17_rtfm_db1.sql.gz 2017-10-14 09:51:01 300827 14_10_17_setevoy_ki.sql.gz
[/simterm]
Осталось добавить скрипт в крон и запускать каждую ночь:
0 2 * * * /home/admin/scripts/mysql_all_dbs_backups_to_s3.sh > /var/log/mysql/mysql_all_dbs_backups_to_s3.log
Готово.
Скрипт и файл IAM политики можно посмотреть в Github.