Резервное копирование MongoDB

В далекие времена довелось поработать с такими комбайнами как система резервного копирования Bacula и Amanda. Однако в системах построенных изначально на высокой доступности (HighAvailability) их применение не совсем оправдано. Причина — большая часть конфигурации хранится в GIT/SVN, каждый узел системы настраивается через puppet, chef или cfengine. Таким образом в случае выхода из строя одного сервера это никак не сказывается на работе комплекса. А систему легко восстановить путем применения написанных манифестов.

Таким образом на первый план вышел более простой в использовании rsnapshot. Бесспорно, основной конек его использование в связке с rsync, однако его можно смело применять для резервного копирования базы данных. Самое простое, что приходит в голову резервная копия PostgreSQL

backup_script   /usr/bin/ssh root@postgresql-hostname "su -l postgres -c pg_dumpall | gzip" > dump.sql.gz                       postgresql-hostname/postgres/

Важно! При копировании примера директивы разделяются табуляцией.

Сложность приключилась при появлении в парке MongoDB. Дело в том, что штатная mongodump создает резервные копии всей базы в отдельной директории, таким образом простым способом аналогичный подход не применим. Существует вариант создания резервной копии через backup_script и последующий его трансфер директивной backup. Стремясь к идеальному мы не могли себе позволить столь прямолинейное решение :) На помощь, как обычно, пришла командная оболочка и на выходе родилась следующая конструкция

backup_script   /usr/bin/ssh root@mongodb-hostname 'cd /var/tmp && tmpdir=$(mktemp -d mongodump.XXXXXX) && trap "{ rm -fr $tmpdir; }" EXIT TERM INT && mongodump -o $tmpdir --oplog > /dev/null && tar czf - $tmpdir' > mongodump.tar.gz      mongodb-hostname/mongo/

Важно! При копировании примера директивы разделяются табуляцией.

Если разложить этот пример по строчкам получим

cd /var/tmp && 
tmpdir=$(mktemp -d mongodump.XXXXXX) && 
trap "{ rm -fr $tmpdir; }" EXIT TERM INT && 
mongodump -o $tmpdir --oplog > /dev/null &&
tar czf - $tmpdir

Второй строчкой мы создаем временную директорию, в которую будем в строке четыре производить сохранение в формате bson резервной копии всех баз MongoDB и oplog. Изначально была использована более сокращенная конструкция, заменяющая две первые строчки

tmpdir=$(mktemp -d /var/tmpmongodump.XXXXXX)

Минус ее заключается в том, что на последнем шаге при получении TAR архива мы на STDERR получаем сообщение

tar: Removing leading `/' from member names

Направлять STDERR в /dev/null не наш путь. Поэтому изначально мы переходим во временную директорию /var/tmp. Вывод STDOUT у mongodump завязан на /dev/null, так как в процессе работы скрипт выводит не нужную отладочную информацию. Здесь должен идти плевок в сторону разработчиков MongoDB, которые нарушают основные парадигмы unixway. Более того не предусмотрели ключ --quiet.

Самой интересной комбинацией и ради чего все это затевалось является строчка

trap "{ rm -fr $tmpdir; }" EXIT TERM INT

Которая при завершении выполнения всех процессов (в нашем случае при успешной передаче TAR архива на принимающую сторону) или при нажатии Ctrl+C произведет удаление временной директории.

Важно отметить, что в нашем случае резервная копия снимается на отдельный раздел. Физически хранилище MongoDB находится на другом дисковом RAID массиве, таким образом дисковые активности не пересекаются. Есть небольшой минус связанный с вымыванием дискового кеша. Так как резервная копия снимается ночью на производительности это не отражается.

Указанный скрипт легко адаптируется к ситуации, когда копия изначально снимается в директорию на резервном сервере (в помощь ключ --host для mongodump), архивируется и производится зачистка временных файлов. Однако это задачка для наших читателей.

Разрушители легенд: мифы и реальность о MongoDB

Всем поклонникам подпихнуть MongoDB в любые мыслимые и немыслимые места посвящается. Некоторые выдержки

When a read lock exists, many read operations may use this lock. However, when a write lock exists, a single write operation holds the lock exclusively, and no other read or write operations may share the lock.

Beginning with version 2.2, MongoDB implements locks on a per-database basis for most read and write operations. Some global operations, typically short lived operations involving multiple databases, still require a global “instance” wide lock. Before 2.2, there is only one “global” lock per mongod instance.

Больше всего понравился тот факт, что на запись у них теперь лочиться вся база, а не весь процесс, они как великое достижение человечества записали. MySQL версии 3.x, если не более ранние, поддерживали аналогичное поведение, что было лет десять тому назад.

MongoDB, бесспорно, отличный инструмент. Но не следует применять его как затычку к каждой бочке.