Добрый день, сегодня мы рассмотрим способ создания множественных точек восстановления СУБД MySQL с помощью возможности файловой системы BTRFS. Вкратце рассмотрим решаемую задачу: Давайте представим, что у нас имеется бизнес-критический ресурс, для которого нам бы имелось иметь почасовые точки восстановления, то есть иметь возможность восстановить состояние нашей базы данных 1 час назад или на произвольный момент с часовой точностью за последние сутки.
Данную задачу мы можем решить несколькими способами, приведем основные способы здесь:
- Дамп базы данных — способ используется часто, но подходит только для небольших баз данных, потому что для обеспечения синхронного бэкапа требуется заблокировать операции записи, чтобы избежать изменения таблиц в процессе создания дампов. Если же дамп создается долго (таблицы в базах большие, занимают несколько гигабайт каждая), то это не только приведет к останову сайта на это время, но и при выполнении данной операции каждый час сделает систему нефункциональной.
- Способ 1, комбинированный с репликацией Master-Slave. В данном способе дампы делаются не с основного (master) сервера, а с резервного (slave), который может спокойно быть остановлен на некоторое время без ущерба. Сложность данного способа заключается как в использовании двухузловой конфигурации, что требует двойного объема ресурсов, так и в том, что slave должен «догнать» master после завершения дампа до точки создания следующего дампа, в противном случае, на slave будет накапливаться отставание, что, в конечном итоге, ничем хорошим не закончится.
- Использование Percona Xtrabackup, способ подробно описан на сайте habrahabr. Способ хороший, годный, но больше предназначен для создания резервных копий, нежели для точек отката.
- Использование снимков файловой системы. Надо сказать, что на сегодняшний день в linux существуют три способа, которые позволяют реализовать данную схему:
- LVM2 — данная система может применяться для резервного копирования, но в нашей задачи ее использование невозможно, так как тома LVM со снимками работают очень медленно;
- ZFS — эта легендарная файловая система словно волшебная таблетка, однако для linux ее требуется собирать отдельно, так как по лицензионным соображениям она не входит в стандартные сборки.
- BTRFS — новая файловая система, которая поддерживает все необходимые нам функции, однако, считается недостаточно стабильной. Слово «считается» здесь ключевое, так как существует достаточно большое число компаний, которые успешно используют данную FS, а на случай фатального сбоя будем иметь бэкап базы для быстрого запуска. Этот способ и будем использовать.
Используемая конфигурация оборудования
Для реализации задачи мы будем использовать VM из облака NetPoint, Вы же можете использовать то, что Вам больше нравится. Самое важное — планирование системы хранения данных VM. Мы будем использовать SSD-диск размером 10GB для СУБД и точек восстановления (за последние 24 часа) и SATA-диск 120GB для хранения бэкапов СУБД за последний месяц и копии точек восстановления за последние сутки для быстрого восстановления в случае если BTRFS сломается. Наша задача — иметь возможность восстановления работоспособности MySQL в последней точке восстановления в случае полной аварии BTRFS за 5-10 минут.
NB: BTRFS чувствительна к ограничением дискового пространства. Не допускайте, чтобы на файловой системе с BTRFS закончилось место.
Используемое программное обеспечение
- Debian 7
- MariaDB 10.0
Тома файловой системы
- SSD10GB: /mnt/db — BTRFS, файловая система для хранения основной СУБД и точек восстановления
- SAS120GB: /mnt/backup — EXT4, файловая система для хранения резервных копий точек и бэкапов за предыдущие дни.
Способ создание точек восстановления
Для создания точки восстановления мы будем использовать снимки BTRFS. Для корректного состояния баз данных в момент создания снимков мы будем использовать FLUSH TABLES WITH READ LOCK и UNLOCK TABLES, что позволит нам обеспечить целостность схемы БД в момент создания снимка, а значит позволит, в случае необходимости быстро восстановить БД из корректного снимка.
Развертывание VM
Мы будем использовать VM в облаке NetPoint, Вы можете использовать VM в нашем или любом другом облаке, а так же аппаратный сервер. При развертывании мы сделаем следующую разметку:
- /dev/vda1 (Ext4) — / (корневая фс), 2GB
- /dev/vda2 (swap) — раздел подкачки, 2GB
- /dev/vda3 (BTRFS) — /mnt/db (остаток места)
- /dev/vdb1 (Ext4) — /mnt/backup (все пространство)
После установки имеем:
root@btrfs-test:~# cat /etc/issue Debian GNU/Linux 7 \n \l root@btrfs-test:~# mount sysfs on /sys type sysfs (rw,nosuid,nodev,noexec,relatime) proc on /proc type proc (rw,nosuid,nodev,noexec,relatime) udev on /dev type devtmpfs (rw,relatime,size=10240k,nr_inodes=506167,mode=755) devpts on /dev/pts type devpts (rw,nosuid,noexec,relatime,gid=5,mode=620,ptmxmode=000) tmpfs on /run type tmpfs (rw,nosuid,noexec,relatime,size=406136k,mode=755) /dev/disk/by-uuid/ce3734ea-967f-40eb-81a3-f6f66e98b000 on / type ext4 (rw,relatime,errors=remount-ro,user_xattr,barrier=1,data=ordered) tmpfs on /run/lock type tmpfs (rw,nosuid,nodev,noexec,relatime,size=5120k) tmpfs on /run/shm type tmpfs (rw,nosuid,nodev,noexec,relatime,size=1202800k) /dev/vdb1 on /mnt/backup type ext4 (rw,relatime,user_xattr,barrier=1,data=ordered) /dev/vda3 on /mnt/db type btrfs (rw,relatime,space_cache) rpc_pipefs on /var/lib/nfs/rpc_pipefs type rpc_pipefs (rw,relatime)
Далее необходимо установить MySQL. Я буду использовать реализацию MariaDB 10.0, которая принята к использованию по умолчанию в нашей компании. Установка достаточно простая.
# apt-get install python-software-properties # apt-key adv --recv-keys --keyserver keyserver.ubuntu.com 0xcbcb082a1bb943db # add-apt-repository 'deb http://mirror.mephi.ru/mariadb/repo/10.0/debian wheezy main' # apt-get update # apt-get install mariadb-server
Теперь перенесем наши базы данных MySQL на BTRFS:
# service mysql stop [ ok ] Stopping MariaDB database server: mysqld. # mv /var/lib/mysql /mnt/db/mysql # ls /mnt/db/mysql/ aria_log.00000001 aria_log_control debian-10.0.flag ibdata1 ib_logfile0 ib_logfile1 multi-master.info mysql mysql_upgrade_info performance_schema # ln -s /mnt/db/mysql /var/lib/mysql
Запустим MariaDB
# service mysql start [ ok ] Starting MariaDB database server: mysqld. [info] Checking for corrupt, not cleanly closed and upgrade needing tables.. # ps xa | grep mysql 6490 pts/0 S 0:00 /bin/bash /usr/bin/mysqld_safe 6694 pts/0 Sl 0:00 /usr/sbin/mysqld --basedir=/usr --datadir=/var/lib/mysql --plugin-dir=/usr/lib/mysql/plugin --user=mysql --pid-file=/var/run/mysqld/mysqld.pid --socket=/var/run/mysqld/mysqld.sock --port=3306 6695 pts/0 S 0:00 logger -t mysqld -p daemon.error 6811 pts/0 R+ 0:00 grep mysql
Попробуем теперь создать снимок mysql (для теста, что все ОК). Данный снимок не будет корректным, потому что таблицы MySQL не ограничены для записи и не синхронизованы с диском, просто проверим, что снимки делаются:
# btrfs subvolume snapshot /mnt/db /mnt/db/@mysql_initial_db Create a snapshot of '/mnt/db' in '/mnt/db/@mysql_initial_db' # ls /mnt/db/@mysql_initial_db/ mysql # ls /mnt/db/@mysql_initial_db/mysql/ aria_log.00000001 aria_log_control debian-10.0.flag ibdata1 ib_logfile0 ib_logfile1 multi-master.info mysql mysql_upgrade_info performance_schema
Итак, все ОК, снимки создаются, MySQL работает. Теперь реализуем сценарий, который будет выполнять резервное копирование MySQL. Я буду использовать язык программирования Perl для этой задачи. Сначала разберем по шагам, что мы хотим сделать.
Создаем файл /opt/backup-mysql следующего содержания:
#!/usr/bin/perl use strict; use warnings; use DBI; my $MYSQL_LOGIN='root'; my $MYSQL_PASSWORD=$ENV{'MYSQLPASS'}; my $DIR = '/mnt/db'; my $BACKUP = '/mnt/backup'; my $hour = `date +"%H"`; chomp $hour; my $day = `date +"%d"`; chomp $day; # create directory where to store last backups system("mkdir $BACKUP/last24 2>/dev/null"); my $dbh = DBI->connect("DBI:mysql:;host=127.0.0.1",$MYSQL_LOGIN,$MYSQL_PASSWORD) or die DBI->errstr; $dbh->do("FLUSH TABLES WITH READ LOCK") or die $dbh->errstr; if(!system("test -d $DIR/\@point-$hour")){ system("btrfs subvolume delete $DIR/\@point-$hour"); } system("btrfs subvolume snapshot $DIR $DIR/\@point-$hour"); $dbh->do("UNLOCK TABLES") or die $dbh->errstr; system("rm -Rf $BACKUP/last24/hour-$hour 2>/dev/null"); system("cp -Rf $DIR/\@point-$hour $BACKUP/last24/hour-$hour"); if ("$hour" == "23") { system("rm -Rf $BACKUP/day-$day 2>/dev/null"); system("cp -Rf $DIR/\@point-$hour $BACKUP/day-$day"); }
Ставим права доступа на файл 755:
# chmod 755 /opt/backup-mysql
Кратко, по шагам, что же он делает:
- соединяемся с mysql
- сбрасываем таблицы на диск
- удаляем снимок на текущий час, если он есть (повторный запуск в этом часе или за предыдущие сутки);
- создаем снимок файловой системы в форме @point-<час>;
- разблокируем таблицы;
- копируем снимок в /mnt/backup/last24/hour-<час>;
- если текущий час равен 23, то копируем снимок в /mnt/backup/day-<деньмесяца>;
Скрипт берет пароль из переменных окружения, то есть перед его запуском необходимо сделать:
# export MYSQLPASS=mysqlsecretpassword
Для периодического запуска возможно воспользоваться CRON, напишем небольшой скрипт в /etc/cron.hourly/backup-mysql:
#!/bin/bash export MYSQLPASS=mysqlsecretpassword /opt/backup-mysql
Дадим ему права на выполнение:
# chmod 755 /etc/cron.hourly/backup-mysql
Все, теперь каждый час будет создаваться точка восстановления MySQL и каждый день будет складываться копия базы на конец дня в месячный архив.