Задача – набросать скрипт для создания бекапов всех баз сервера БД и сохранять их в корзину.
В общем – всё просто: бекапы делаем с помощью 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
) выполняетmysqldump
push_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.