Показаны сообщения с ярлыком backup. Показать все сообщения
Показаны сообщения с ярлыком backup. Показать все сообщения

воскресенье, 20 декабря 2015 г.

Обратная сборка пакета tvheadend

Вчера вечером ставил обновления на домашнем сервере и не заметил обновление tvheadend. Был tvheadend 3.4.27, а обновился до 4.0.8. В итоге отвалилась настройка всех каналов в IPTV. Новая версия здорово отличается от старой и разбираться с ней нет никакого желания. У меня уже есть набор скриптов tvheadend-iptv-damavik, который делает всю черную работу.

Чтобы решить проблему малой кровью нужно установить старую версию пакета и заново импортировать настройки каналов и EPG. Полез в /var/cache/apt/archives - есть только новая версия. Собрался поискать пакет в бэкапе bacula, но вспомнил, что /var/cache/apt/archives исключен из бэкапа. В репозитарии tvheadend нужный пакет тоже недоступен, а поиск в гугле по названию пакета дает только ссылки на apt.tvheadend.org.

Хоть сам пакет и исключен из бэкапа, но ведь содержимое пакета в бэкапе есть. Нужно только извлечь нужные файлы, метаданные пакета и собрать пакет обратно. Сначала восстанавливаем метаданные - для этого выбираю восстановление бэкапа сервера, предшествующего дате 2015-12-19 00:00:00 и восстанавливаю директорию /var/lib/dpkg. Восстановленные данные будут находиться в /bacula/restore/.

Создаю директорию /root/tvheadend_3.4.27~gfbda802~wheezy_amd64 - сюда будут складываться нужные файлы пакета. Из бэкапа мне нужен /bacula/restore/var/lib/dpkg/info/tvheadend.list - тут описано содержимое пакета, которое мне нужно дополнительно восстановить из бэкапа. В сумме нужно восстановить следующие файлы и директории:
  • /usr/share/tvheadend/
  • /usr/share/doc/tvheadend/
  • /usr/share/man/tvheadend.1.gz
  • /etc/default/tvheadend
  • /etc/init.d/tvheadend
  • /etc/init/tvheadend.conf
  • /usr/bin/tvheadend
Восстановленные файлы перемещаем в /root/tvheadend_3.4.27~gfbda802~wheezy_amd64. Данные пакета готовы, теперь очередь метаданных.

Создаем директорию /root/tvheadend_3.4.27~gfbda802~wheezy_amd64/DEBIAN/. Восстановленные файлы метаданных /bacula/restore/var/lib/dpkg/info/tvheadend.* перемещаем в /root/tvheadend_3.4.27~gfbda802~wheezy_amd64/DEBIAN и переименовываем чтобы убрать префикс tvheadend. из имени файла (tvheadend.md5sums -> md5sums, tvheadend.postinst -> postinst, и т.д.).

Для окончательной сборки пакета не хватает только файла DEBIAN/control - его содержимое можно взять в var/lib/dpkg/status.

Теперь собираем пакет

cd /root/tvheadend_3.4.27~gfbda802~wheezy_amd64
dpkg-deb -b . ../tvheadend_3.4.27~gfbda802~wheezy_amd64.deb
dpkg -i ../tvheadend_3.4.27~gfbda802~wheezy_amd64.deb

Осталось восстановить из бэкапа /home/hts/.hts/ и перезапустить tvheadend.

UPDATE: В комментарии напомнили, что недостает шага по фиксированию версии пакета. Можно либо через dpkg selections:

echo tvheadend hold | sudo dpkg --set-selections

либо через aptitude

sudo aptitude hold tvheadend

Или можно пошаманить с apt pinning.

пятница, 24 июля 2015 г.

Извлечение несколько файлов из бэкапа Clonezilla

Есть полный бэкап диска, выполненный в clonezilla. Нужно вытащить несколько файлов с рабочего стола пользователя.

Сначала смотрим что есть в бэкапе:

$ cd /data/Backups/2015-07-23-14-img
$ ls -1
total 16625923
-rw------- 1 root root        583 Jul 23 17:09 blkdev.list
-rw------- 1 root root        197 Jul 23 17:09 blkid.list
-rw------- 1 root root       6036 Jul 23 17:23 clonezilla-img
-rw------- 1 root root          4 Jul 23 17:17 disk
-rw------- 1 root root      20560 Jul 23 17:17 Info-dmi.txt
-rw------- 1 root root      24254 Jul 23 17:17 Info-lshw.txt
-rw------- 1 root root       2243 Jul 23 17:17 Info-lspci.txt
-rw------- 1 root root        171 Jul 23 17:17 Info-packages.txt
-rw------- 1 root root         90 Jul 23 17:23 Info-saved-by-cmd.txt
-rw------- 1 root root         10 Jul 23 17:17 parts
-rw------- 1 root root         33 Jul 23 17:10 sda1.info
-rw------- 1 root root    9588755 Jul 23 17:09 sda1.ntfs-ptcl-img.gz.aa
-rw------- 1 root root 2097152000 Jul 23 17:10 sda2.ntfs-ptcl-img.gz.aa
-rw------- 1 root root 2097152000 Jul 23 17:11 sda2.ntfs-ptcl-img.gz.ab
-rw------- 1 root root 2097152000 Jul 23 17:12 sda2.ntfs-ptcl-img.gz.ac
-rw------- 1 root root 2097152000 Jul 23 17:13 sda2.ntfs-ptcl-img.gz.ad
-rw------- 1 root root 2097152000 Jul 23 17:15 sda2.ntfs-ptcl-img.gz.ae
-rw------- 1 root root 2097152000 Jul 23 17:15 sda2.ntfs-ptcl-img.gz.af
-rw------- 1 root root 2097152000 Jul 23 17:16 sda2.ntfs-ptcl-img.gz.ag
-rw------- 1 root root 2097152000 Jul 23 17:17 sda2.ntfs-ptcl-img.gz.ah
-rw------- 1 root root  237018999 Jul 23 17:17 sda2.ntfs-ptcl-img.gz.ai
-rw------- 1 root root         37 Jul 23 17:09 sda-chs.sf
-rw------- 1 root root    1048064 Jul 23 17:09 sda-hidden-data-after-mbr
-rw------- 1 root root        512 Jul 23 17:09 sda-mbr
-rw------- 1 root root        319 Jul 23 17:09 sda-pt.parted
-rw------- 1 root root        281 Jul 23 17:09 sda-pt.parted.compact
-rw------- 1 root root        259 Jul 23 17:09 sda-pt.sf

Нужные нам данные хранились на втором разделе (sda2). Для восстановления нам понадобится partclone и pigz (вместо pigz можно использовать gzip, но восстановление займет больше времени)

$ sudo apt-get install partclone pigz

Восстановим сырой образ второго раздела в файл:

# cat sda2.ntfs-ptcl-img.gz.a* | pigz -dc | partclone.restore -C -s - -O /data/Backups/sda2-restore.bin --restore_raw_file
Partclone v0.2.73 http://partclone.org
Starting to restore image (-) to device (/data/Backups/sda2-restore.bin)
Reading Super Block
Calculating bitmap... Please wait... done!
File system:  NTFS
Device size:  119.9 GB = 29278719 Blocks
Space in use:  56.3 GB = 13755539 Blocks
Free Space:    63.6 GB = 15523180 Blocks
Block size:   4096 Byte
Elapsed: 00:17:22, Remaining: 00:00:00, Completed: 100.00%, Rate:   3.24GB/min, 
current block:   16720764, total block:   29278719, Complete: 100.00%           
Total Time: 00:17:22, Ave. Rate:    3.2GB/min, 100.00% completed!
Syncing... OK!
Partclone successfully restored the image (-) to the device (/data/Backups/sda2-restore.bin)
Cloned successfully.

Пробуем смонтировать образ

$ sudo mount -o loop,ro /data/Backups/sda2-restore.bin /mnt
Failed to read last sector (234229758): Invalid argument
HINTS: Either the volume is a RAID/LDM but it wasn't setup yet,
   or it was not setup correctly (e.g. by not using mdadm --build ...),
   or a wrong device is tried to be mounted,
   or the partition table is corrupt (partition is smaller than NTFS),
   or the NTFS boot sector is corrupt (NTFS size is not valid).
Failed to mount '/dev/loop0': Invalid argument
The device '/dev/loop0' doesn't seem to have a valid NTFS.
Maybe the wrong device is used? Or the whole disk instead of a
partition (e.g. /dev/sda, not /dev/sda1)? Or the other way around?

но получаем ошибку при попытке прочесть сектор, находящийся за пределами восстановленного файла. Дело в том, что partclone не копирует незанятые области и в данном случае мы получили образ меньшего объема, чем изначальный размер исходного раздела. Чтобы исправить нужно либо увеличить размер образа до заведомо большего объема, либо посмотреть в информации clonezilla правильный размер.

Я опишу второй способ. Смотрим в файле sda-pt.parted информацию о исходном диске:

$ cat sda-pt.parted
Model: ATA Samsung SSD 840 (scsi)
Disk /dev/sda: 234441648s
Sector size (logical/physical): 512B/512B
Partition Table: msdos

Number  Start    End         Size        Type     File system  Flags
 1      2048s    206847s     204800s     primary  ntfs         boot
 2      206848s  234436607s  234229760s  primary  ntfs

Размер сектора диска 512 байт, размер второго раздела (sda2) - 234229760 секторов. Значит правильный размер образа файла - 512 * 234229760 = 119925637120 байт. Изменим размер файла образа и попробуем смонтировать образ

$ truncate -s $((512 * 234229760)) /data/Backups/sda2-restore.bin
$ sudo mount -o ro,loop /data/Backups/sda2-restore.bin /mnt

Теперь монтирование прошло без ошибок и можно вытаскивать файлы.

среда, 29 октября 2014 г.

Bacula: Как восстановить одну директорию из бэкапа используя bconsole

Раньше если нужно восстановить данные из бэкапа, то для работы с Bacula пользовался графическим интерфейсом bat. Сегодня потребовалось восстановить только одну директорию из бэкапа сервера, содержащего большое количество файлов. Попытка подготовить задание восстановления через bat намертво его повесила. Пришлось прибить bat, снять запросы к БД и идти осваивать консольный вариант - bconsole.

$ sudo bconsole
*restore client=srv11.example.com-fd
Using Catalog "MyCatalog"

First you select one or more JobIds that contain files
to be restored. You will be presented several methods
of specifying the JobIds. Then you will be allowed to
select which files from those JobIds are to be restored.

To select the JobIds, you have the following choices:
     1: List last 20 Jobs run
     2: List Jobs where a given File is saved
     3: Enter list of comma separated JobIds to select
     4: Enter SQL list command
     5: Select the most recent backup for a client
     6: Select backup for a client before a specified time
     7: Enter a list of files to restore
     8: Enter a list of files to restore before a specified time
     9: Find the JobIds of the most recent backup for a client
    10: Find the JobIds for a backup for a client before a specified time
    11: Enter a list of directories to restore for found JobIds
    12: Select full restore to a specified Job date
    13: Cancel
Select item:  (1-13):

Поскольку нужно восстановить последний бэкап, выбираю "5: Select the most recent backup for a client"

Select item:  (1-13): 5
Automatically selected FileSet: Standard Set
+-------+-------+-----------+-----------------+---------------------+--------------+
| JobId | Level | JobFiles  | JobBytes        | StartTime           | VolumeName   |
+-------+-------+-----------+-----------------+---------------------+--------------+
| 1,119 | F     | 5,022,824 | 168,370,798,105 | 2014-10-12 23:37:33 | Archive-0078 |
| 1,119 | F     | 5,022,824 | 168,370,798,105 | 2014-10-12 23:37:33 | Archive-0079 |
| 1,119 | F     | 5,022,824 | 168,370,798,105 | 2014-10-12 23:37:33 | Archive-0081 |
| 1,119 | F     | 5,022,824 | 168,370,798,105 | 2014-10-12 23:37:33 | Archive-0082 |
| 1,277 | D     |   430,784 |  18,791,703,231 | 2014-10-27 06:26:52 | Archive-0080 |
| 1,294 | I     |    90,159 |   3,764,072,742 | 2014-10-28 00:28:47 | Archive-0013 |
| 1,294 | I     |    90,159 |   3,764,072,742 | 2014-10-28 00:28:47 | Archive-0080 |
| 1,310 | I     |    75,993 |   4,224,432,445 | 2014-10-28 23:08:27 | Archive-0013 |
+-------+-------+-----------+-----------------+---------------------+--------------+
You have selected the following JobIds: 1119,1277,1294,1310

Building directory tree for JobId(s) 1119,1277,1294,1310 ...  ++++++++++++++++++++++++++++++++++++++++++
4,260,904 files inserted into the tree.

You are now entering file selection mode where you add (mark) and
remove (unmark) files to be restored. No files are initially added, unless
you used the "all" keyword on the command line.
Enter "done" to leave this mode.

cwd is: /
$

Около минуты занимает построение списка. После появление приглашения можно добавить нужные файлы в задание восстановления.

$ cd /var/www/vhosts/
cwd is: /var/www/vhosts/

$ add shop.example.com
23,405 files marked.

$ exit

После команды "exit" bacula выдаст информацию по созданному заданию восстановления и запросит подтверждение.

Bootstrap records written to /var/lib/bacula/bacula-dir.restore.4.bsr

The job will require the following
   Volume(s)                 Storage(s)                SD Device(s)
===========================================================================
   
    Archive-0082              File                      FileStorage              
    Archive-0013              File                      FileStorage              

Volumes marked with "*" are online.


23,408 files selected to be restored.

Run Restore job
JobName:         RestoreFiles
Bootstrap:       /var/lib/bacula/bacula-dir.restore.4.bsr
Where:           /bacula/restore
Replace:         always
FileSet:         Standard Set
Backup Client:   srv11.example.com-fd
Restore Client:  srv11.example.com-fd
Storage:         File
When:            2014-10-29 11:56:46
Catalog:         MyCatalog
Priority:        10
Plugin Options:  *None*
OK to run? (yes/mod/no):

Если все верно, то выбираем "yes" и ждем пока данные восстановятся.

понедельник, 31 марта 2014 г.

Сегодня "World Backup Day 2014" - начать делать бэкапы никогда не поздно

Сегодня всемирный день бэкапа. Создайте и проверьте бэкап сегодня, чтобы не стать жертвой розыгрыша завтра.

Я вот под шумок перевел парочку хостов с rdiff-backup на bacula и проверил восстановление из старого бэкапа. Теперь останется только просматривать отчеты bacula по утрам.


четверг, 13 февраля 2014 г.

Резервное копирование домашнего медиацентра

Сегодня наводил окончательный лоск в настройках домашнего медиацентра (Raspberry PI + OpenELEC) после переноса его директории /storage с SD карты на сервер NFS (SD карта используется только для загрузки, а все файлы конфигурации и пользовательских данных хранятся на сервере и доступны через NFS).

Когда с настройками было покончено я сделал резервную копию средствами OpenELEC, но вот что интересно - резервная копия выполняется без завершения работы XBMC, а значит часть файлов копируется в процессе использования.

Узнать список файлов, которые в данный момент используются, можно на самом медиацентре

OpenELEC:~ # lsof | grep /storage
866 /usr/sbin/connmand /storage/.cache/connman/ethernet_b827eb9ace35_cable/data
946 /usr/lib/xbmc/xbmc.bin /storage/.xbmc/temp/xbmc.log
946 /usr/lib/xbmc/xbmc.bin /storage/.xbmc/userdata/Database/Addons15.db
946 /usr/lib/xbmc/xbmc.bin /storage/.xbmc/userdata/Database/Textures13.db
946 /usr/lib/xbmc/xbmc.bin /storage/.xbmc/userdata/Database/TV22.db
946 /usr/lib/xbmc/xbmc.bin /storage/.xbmc/userdata/Database/Epg7.db
946 /usr/lib/xbmc/xbmc.bin /storage/.xbmc/userdata/Database/Addons15.db

Открытые файлы *.db это базы SQLite, которые могут быть в промежуточном состоянии на момент создания резервной копии.

Чтобы избежать копирования открытых файлов можно выключить оболочку XBMC, создать резервную копию и затем включить XBMC обратно.

После изучения системы инициализации в OpenELEC оказалось, что самым простым способом достичь желаемого - выполнить перезагрузку всего медиацентра. В процессе завершения работы системы выполняется скрипт /storage/.config/shutdown.sh, который запускается после выключения XBMC, но до перезагрузки всей системы.

Собственно скрипт

if [ "`date +'%H'`" != "05" ]; then
 echo "Seems triggered not by cron job, simply exiting"
 exit 0
fi

STAMP=`date +'%Y%m%d%H%M%S'`

if [ ! -d /storage/backup ]; then
 mkdir /storage/backup
fi

echo "Settings backup..."
tar -cf /storage/backup/${STAMP}.tar /storage/.cache /storage/.config /storage/.xbmc

if [ "`date +'%u'`" == "7" ]; then
 echo "Flash backup..."
 tar -cf /storage/backup/${STAMP}_flash.tar /flash
fi

echo "Backup finished, syncing..."
sync
sleep 30s

Скрипт проверяет в котором часу он запущен и если сейчас не 5 часов утра, то создание резервной копии отменяется - скорее всего это перезагрузка по требованию пользователя, а не задания крона. Далее создается TAR-архив, содержащий конфигурацию OpenELEC и XBMC. Также если скрипт запущен в воскресенье, то дополнительно создается резервная копия всей системы (защита от неудачного обновления самого OpenELEC или выхода из строя SD карточки). Перед завершением скрипта выполняется сброс буферов и выжидается 30 секунд (на всякий случай).

Задание крона выглядит так

# crontab -u root -l

# Reboot to create semi-offline backup
10 5 * * * /usr/bin/xbmc-send --action="XBMC.Reboot"

Согласно этому заданию в каждый день в 5:10 утра выполняется команда перезагрузки средствами XBMC, что гарантирует корректное завершение работы.

среда, 3 июля 2013 г.

Ошибка при удалении снапшота LVM: Can't remove open logical volume

Выкроил пару часов времени чтобы переписать скрипты резервного копирования на работу с LVM снапшотами.

В процессе работы скрипт создает снапшоты для всех точек монтирования, которые нужно бэкапить и которые расположены на LVM. У меня это все, кроме /boot.

Снапшот создается как обычно

# lvcreate --snapshot --name rootfs-backup --size 500M /dev/VolGroup0/rootfs

Затем идет логика резервного копирования и по завершении снапшоты удаляются. Вот тут меня и поджидали грабли.

# lvremove -f /dev/VolGroup0/rootfs-backup
Can't remove open logical volume "rootfs-backup"

Ошибка проявляется не всегда, что наводит на мысли о гонке. Пробую гуглить и натыкаюсь на обсуждение аналогичной проблемы. Оказывается корень проблемы в установленном udisks, который использует современный десктопный софт в Linux.

Как вариант, можно закомментировать строку KERNEL=="dm-*", OPTIONS+="watch" в файле /lib/udev/rules.d/80-udisks.rules, но вместо этого я добавил костыльworkaround в свой скрипт, который несколько раз повторяет действие, если была ошибка.

for i in rootfs usr var home
do
  for j in 1 2 3 4 5 6 7 8 9 10
    lvremove -f /dev/VolGroup0/${i}-backup && break
    sleep 3
  done
  if [ -e /dev/VolGroup0/${i}-backup ]; then
    echo "Failed to remove LVM snapshot VolGroup0/${i}-backup"
    exit 1
  fi
done

среда, 16 мая 2012 г.

Делаем бэкап перед обновлением системы средствами LVM

Дошли руки до своего нетбука, который знакомый попросил одолжить на время. Перед тем как отдать нетбук я решил сделать полный бэкап системы, но потом не смог найти достаточно свободного места на домашнем компе, чтобы забэкапить весь диск целиком. Поскольку знакомому вполне подходил ноут без жесткого диска, то я просто снял винт и таким образов временно решил проблему полного бэкапа.

Однако близится день заморозки кодовой базы Debian в предверии нового релиза Debian Wheezy. Основные черты нового дистрибутива уже видны и по возвращении нетбука я хочу пробно обновить систему на нем, чтобы иметь представление, что меня ожидает при обновлении домашнего компа.

Чтобы не было неприятных сюрпризов перед обновлением, мне нужно сделать бэкап, причем желательно таким образом, чтобы была возможность возиться с "последствиями" обновления и иметь возможность быстро переключиться на стабильный Squeeze. Если делать это по-старинке, то мне придется делать зеркало системных файлов на домашний комп, причем в двух вариантах (стабильный squeeze и обновление на wheezy). Понятное дело, что восстановиться из такого бэкапа в полевых условиях не получится (если вдруг всплывет косяк, связанный с обновлением). Нужна возможность держать всю информацию на самом нетбуке, но проблема в том, что там просто нету столько свободного места, чтобы держать две полные копии системы. Потому я начал искать новое решение...

В общих чертах меня могла бы спасти BTRFS у которой есть поддержка снапшотов на уровне файловой системы (т.е. в снапшотах хранится только разница относительно базы, с которой они были созданы). Но btrfs еще в стадии разработки и спотыкаться об ее недоработки нет желания.

Далее вспомнилось, что в самом начале освоения LVM я пробовал делать снапшоты перед обновлением системы. Если обновление нужно было отменить, то приходилось монтировать снапшот и затем rsync'ом приводить систему в чувство. Но хочется чего-то проще. Что-то вроде сделал снапшот и если результат обновления не нравится - просто возвращаем исходное состояние нужному LVM тому, используя для этого соответствующий снапшот.

Пока искал выход нагуглил новость о том, что начиная с ядра 2.6.33 появилась возможность объединять LVM том и его снапшот - BINGO! - как раз то, что нужно мне и без лишних телодвижений.

Разбивка диска на нетбуке у меня не слишком мудреная:

/dev/sda1  /boot  ext2
/dev/sda2  LVM PV
/dev/sda3  SWAP

/dev/laptop/rootfs  /  ext4
/dev/laptop/home  /home

Посколько /boot находится вне LVM, то я сделал его копию:

# rsync -a /boot/ /boot~lvm/

Смотрим, сколько у меня места в группе томов (VG), еще не размеченного под логические тома (LV):

# vgs
  VG        #PV #LV #SN Attr   VSize   VFree
  laptop      1   2   0 wz--n- 156,2g  15,44g

Далее делаем снапшоты всех разделов:

# sync
# lvcreate -s -n rootfs-before -L 5G laptop/rootfs
# lvcreate -s -n home-before -L 1G laptop/home

После этого можно смело обновлять систему. Если потребуется откатить обновление, то создаем новый снапшот (чтобы разобраться, когда появится свободное время) и мержим старый снапшот и соответствующий логический том:

# lvcreate -s -n rootfs-after -L 7G laptop/rootfs
# lvcreate -s -n home-after -L 1G laptop/home
# lvconvert --merge laptop/rootfs-before
# lvconvert --merge laptop/home-before

Теперь можно перезагрузиться, чтобы тома начали слияние (слияние начинается только при последующей активации тома).

Если в процессе обновления поменялось содержимое /boot, то после перезагрузки или при помощи LiveCD/LiveUSB его можно восстановить так:

# rsync -a --delete /boot~lvm/ /boot/
# rm -fr /boot~lvm
# update-grub

После завершения слияния, пространство, занятое под laptop/rootfs-before и laptop/home-before освободится. Аналочично поступаем, если нужно вернуться в играм с обновлением.

вторник, 27 марта 2012 г.

MySQL: backup and partial restore

Одна из методик создания резервных копий баз данных MySQL основывается на использовании снимков LVM. Скрипт, который отвечает за создание резервной копии выглядит примерно так:

#!/bin/bash
MYSQL_VG="VolGroup00"
MYSQL_LV="mysql"
MYSQL_SRC="/mnt/mysql-root"
MYSQL_DST="root@backup.server:/backup/mysql-server"

echo "FLUSH TABLES WITH READ LOCK; \! /sbin/lvcreate -s -n ${MYSQL_LV}-backup -L 1G ${MYSQL_VG}/${MYSQL_LV}" | mysql
echo "UNLOCK TABLES" | mysql
mkdir ${MYSQL_SRC}
mount /dev/${MYSQL_VG}/${MYSQL_LV} ${MYSQL_SRC} -o ro
rsync -avzxHS --delete --numeric-ids ${MYSQL_SRC}/ ${MYSQL_DST}/
umount ${MYSQL_SRC}
rmdir ${MYSQL_SRC}
lvchange -an ${MYSQL_VG}/${MYSQL_LV}-backup
lvremove ${MYSQL_VG}/${MYSQL_LV}-backup

Примечание: примеры ориентированы на Debian Squeeze и в других дистрибутивах могут потребоваться доработки.

В самом начале буферы таблиц в MySQL базах сбрасываются на диск и вводится блокировка операций записи в таблицы. Затем создается снимок LVM тома, на котором расположены базы MySQL. После создания снимка снимается блокировка записи и MySQL продолжает работать в нормальном режиме. Расплатой за это удобство является снижение скорости дисковых операций записи в MySQL в то время, пока активен снимок LVM.

Далее снимок монтируется как обычное блочное устройство и файлы, в которых MySQL хранит базы данных, переносятся в резервную копию любым удобных способом. В примере используется rsync, что позволяет передавать по сети только измененившиеся части файлов. По окончании переноса файлов, снимок LVM размонтируется и удаляется из менеждера томов.

Таким образом делается бэкап без остановки работы сервера. Однако создание резервных копий не самоцель, их нужно также восстанавливать в случае потери данных или необходимости получить доступ к более раннему состоянию баз данных. Самый простой способ восстановить резервную копию, созданную описанных выше способом, это остановить сервер MySQL и скопировать данные обратно.

# service mysql stop
# rsync -avxzHS --delete --numeric-ids root@backup.server:/backup/mysql-server/ /var/lib/mysql/
# service mysql start

Такой способ восстановления подходит, если нужно вернуть все базы в исходное состояние на момент создания резервной копии. Но что делать, если нужно восстановить только одну базу, одну таблицу или только несколько записей из конкретной таблицы? Что если основной сервер не допускает остановки? Конечно можно поднять отдельный MySQL сервер на другом сервере, скопировать туда бэкап и после запуска вытащить из него нужные данные посредством mysqldump. Но что делать, если другого сервера нет или версия MySQL сервера содержит патчи, которые делают резервную копию несовместимой с дистрибутивной версией? Я постараюсь дать одно из возможных решений этой проблемы.

В своем способе я также буду запускать отдельную копию MySQL сервера, но основная идея заключается в том, чтобы не копировать данные из бэкапа, а примонтировать их через сеть. Я буду использовать sshfs для этих целей. Чтобы при запуске второго сервера не изменились данные в бэкапе я применю оверлей на базе AUFS. Одна из его частей будет примонтированным бэкапом, а вторая - хранить все изменения относительно бэкапа. Таким образом я избегаю любых модификаций резервной копии.

# mkdir /mnt/mysql-{backup,datadir,tmp}
# sshfs root@backup.server:/backup/mysql-server /mnt/mysql-backup
# mount -t aufs none /mnt/mysql-datadir -o dirs=/mnt/mysql-tmp:/mnt/mysql-backup=ro
# mysqld_safe --defaults-file=/root/mysql-backup-restore.cnf

/root/mysql-backup-restore.cnf - конфигурация для второго сервера, которую я сделал на базе конфига из дистрибутива.

[mysqld]
user=root
pid-file        = /root/mysql-restore.pid
socket          = /root/mysql-restore.sock
basedir         = /usr
datadir         = /mnt/mysql-datadir
tmpdir          = /tmp
language        = /usr/share/mysql/english
skip-networking
skip-external-locking
key_buffer              = 16M
max_allowed_packet      = 16M
thread_stack            = 192K
thread_cache_size       = 8
myisam-recover         = BACKUP
query_cache_limit       = 1M
query_cache_size        = 16M
expire_logs_days        = 10
max_binlog_size         = 100M

[mysqldump]
quick
quote-names
max_allowed_packet      = 16M

[isamchk]
key_buffer              = 16M

!includedir /etc/mysql/conf.d/

После запуска вторая копия MySQL сервера доступна только через сокет /root/mysql-restore.sock, чем позволяет исключить посторонний доступ на время восстановления данных. Чтобы подключиться к запущенному серверу, нужно явно указать сокет:

# mysql -S /root/mysql-restore.sock
# mysqldump -S /root/mysql-restore.sock --add-drop-table dbname table1 table2 > dbrestore.sql

После того, как данные сняты посредством mysqldump нужно остановить вторую копию сервера. Для этого нужно выполнить:

# mysqladmin -S /root/mysql-restore.sock shutdown

Когда дополнительный сервер остановится можно отмонтировать /mnt/mysql-datadir и /mnt/mysql-backup, и удалить директории /mnt/mysql-{backup,datadir,tmp}

Несомненным преимуществом такого подхода является время, которое тратится на частичное восстановление резервной копии. Из недостатков пожалуй отсутствие sshfs и aufs во многих дистрибутивах (для centos я так и не смог найти пакета для добавления поддержки aufs). Возможно его следует заменить на любую другую реализацию union fs, но я это делать не пробовал.