Yet Another Postrges on BTRFS
Для работы бывает полезно иметь несколько копий одной реальной базы для экспериментов, фикстур или просто тестовых приложений. База растет и время копирования через разворачивание дампа или с помощью шаблона также возрастает до утомительных величин. Для решения этого кейса уже описаны варианты использования файловой системы с поддержкой CoW - Btrfs. В интернете находил такие инструкции, они сводятся к тому, что делается снепшот всего сервера. И для работы второго "скопированного" нужно перегенерировать pid и сменить порт для предотвращения конфликтов. Этот способ довольно универсальный относительно конфигурации баз на сервере, но кажется имеет ограничение для неопределенного кол-ва параллельных снепшотов серверов.
В этой статье предлагаю свой вариант реализации снепшотов на одном экземпляре сервера postgres и одной базы, на произвольное кол-во копий.
Инструкция linux only, про поддержку CoW файловых систем на Windows не в курсе.
I. Подготовка
1. Нам понадобится специальный раздел для Btrfs, где будут хранится базы и снепштоы.
Это может быть отдельное устройство (подключенное аппаратно либо в менеджере виртуальных устройств)
Это может быть чистый раздел на текущем устройстве (настроить разделы via cfdisk)
Это даже может быть loop device на основе файла (См. losetup)
# Пусть устройство называется
/dev/sdb
2. Отформатируем устройство в Btrfs.
sudo apt-get update && apt-get install -y btrfs-progs
sudo mkfs.btrfs -L btrfs_disk /dev/sdb
3. Примонтируем устройство в систему.
# Для примера возьмем каталог /mnt/btrfs
sudo mkdir -p /mnt/btrfs && sudo mount /dev/sdb /mnt/btrfs
- (Optional) Можно сделать отдельный каталог только для tablespace.
sudo mkdir /mnt/btrfs/pg
4. Установить postgres сервер, если не установлен.(Инструкцию можно найти на оф. сайте)
II. Для создания начальной базы $ORIGIN(той которую будем снепшотить):
1. Создать подраздел btrfs
sudo btrfs subvolume create /mnt/btrfs/pg/$ORIGIN
2. Postgres нужны права на этот каталог
sudo chown postgres:postgres /mnt/btrfs/pg/$ORIGIN
3. Теперь создадим tablespace в этом каталоге. Tablespace может быть назван также как и директория.
psql -c "CREATE TABLESPACE $ORIGIN LOCATION '/mnt/btrfs/pg/$ORIGIN'"
4. Создадим пустую БД в tablespace.
psql -c "CREATE DATABASE $ORIGIN TABLESPACE $ORIGIN;"
6. Теперь базу можно заполнить данными - из дампа или вашего приложения.
pgbench -i -s 10 $ORIGIN
# Будет отображено время затраченное на генерацию
# У pgbench есть и другие параметры, scale только влияет на кол-во записей и
# размер базы. (У меня -s 10 дало базу 92Мб)
III. Чтобы сделать снепшот базы $ORIGIN в базу $SNAPSHOT, понадобится:
1. Создать простой каталог в pg_btrfs для “каркаса” базы $SNAPSHOT. (Пока это будет БД-пустышка, для того чтобы узнать какой OID назначит сервер)
sudo mkdir /mnt/btrfs/pg/$SNAPSHOT
2. Не забудем дать права на этот каталог пользователю postgres.
sudo chown postgres:postgres /mnt/btrfs/pg/$SNAPHOT
3. Создадим в этом каталоге tablespace.
psql -c "CREATE TABLESPACE $SNAPSHOT LOCATION '/mnt/btrfs/pg/$SNAPSHOT'"
4. Создадим пустую БД в этом tablespace.
psql -c "CREATE DATABASE $SNAPSHOT TABLESPACE $SNAPSHOT;"
5. HINT! Теперь нужно запомнить OID, который будет названием каталога внутри созданной структуры директорий после создания БД. Важно учесть, что такой подход позволяет снепшотить только одну базу в tablespace.
/mnt/btrfs/pg/
- $SNAPSHOT/
- PG_11_201809051 # версия postgres (может отличаться)
- 99174299 # - DB OID. Запишем ее в переменную
new_oid=$(sudo ls -1 /mnt/btrfs/pg/$SNAPSHOT/PG_*/ | grep -P [0-9]+)
6. Таким же образом скопируем OID БД $ORIGIN
origin_oid=$(sudo ls -1 /mnt/btrfs/pg/$ORIGIN/PG_*/ | grep -P [0-9]+)
7. Теперь удалим обычный каталог, в котором находится tablespace для снепшота. См. сноску [1].
sudo rm -rf /mnt/btrfs/pg/$SNAPSHOT/
8. И наконец сделаем честный снепшот $ORIGIN через btrfs subvolume
sudo btrfs subvolume snapshot /mnt/btrfs/pg/$ORIGIN/ /mnt/btrfs/pg/$SNAPSHOT
9. В каталоге /mnt/btrfs/pg/$SNAPSHOT будет Copy-on-Write копия $ORIGIN subvolume с OID как в оригинальной БД. Поэтому переименуем ее, чтобы она выглядела для сервера как БД $SNAPSHOT из пункта 4.
cd /mnt/btrfs/pg/$SNAPSHOT/PG_*/
sudo mv $origin_oid $new_oid
cd -
10. Теперь можно подключится к БД $SNAPSHOT и проверить, что там те же данные что и в $ORIGIN.
IV. Когда понадобится удалить БД $SNAPSHOT (тоже верно и для $ORIGIN).
0. Полезно перед удалением завершить всю активность с базой.
PGQUERY=$(cat<< EOM
SELECT pg_terminate_backend(pid)
FROM pg_stat_activity
WHERE pid <> pg_backend_pid()
AND datname = '$SNAPSHOT';
EOM
)
psql -c "$PGQUERY"
1. Удаляем базу $SNAPSHOT
psql -c "DROP DATABASE IF EXISTS $SNAPSHOT;"
2. Удаляем tablespace $SNAPSHOT
psql -c "DROP TABLESPACE IF EXISTS $SNAPSHOT;
3. Удаляем subvolume btrfs.
btrfs subvolume delete /mnt/btrfs/pg/$SNAPSHOT
V. Если нужно выполнить восстановление состояния из $ORIGING в $SNAPSHOT:
1. Повторяем шаги для удаления $SNAPSHOT
2. Повторяем шаги для создания $SNAPSHOT из $ORIGIN
[1] Шаги II.1-6 нужны только для создания OID базы данных в tablespace. Удаление каталога в котором зарегистрирован tablespace не должен сломать postgres(но не думаю, что будет хорошей мыслью делать запросы во время создания снепшота). Мета информация о базах и tablespace-ах продолжает хранится в системной базе postgres, и мы на нее не влияем. Возможно, есть и другой способ создания OID базы данные и его подмена в снепшот $ORIGIN, если вы знаете такой, пожалуйста поделитесь.
P.S. Этот способ очень помог мне на работе, где нужно было каждый день проверять ошибки пользователей в системе на реальных данных, т.е. копии из продакшн. База занимала больше 100Гб, а разворачивание дампа около часа. По старинке, после разворачивания тестовая база захламлялась патчами или миграциями разных разработчиков и данные в ней начинали конфликтовать. Тогда нужно либо иметь запас развернутых копий БД, либо ждать пока удалится и восстановиться новая.
В случае с Btrfs, нужно один раз подождать разворачивание оригинальной базы, а потому за пару секунд создавать снепшоты, и когда необходимо также быстро откатываться к оригинальной базе.
Все эти шаги были вынесены в 3 bash скрипта, которые совпадают с последними тремя разделами, и запускались через jenkins. За год работы проблем на стороне btrfs или postgres замечено не было.
Комментарии (5)
chemtech
01.05.2022 09:23+1Спасибо за пост. Посмотрите https://github.com/postgres-ai/database-lab-engine - Thin PostgreSQL clones. DLE provides blazing-fast database cloning to build powerful development, test, QA, staging environments. Follow to stay updated.
akhkmed
02.05.2022 01:38Верно понимаю идею, что делается снепшот лишь одного tablespace одной БД чтобы развернуть в соседней БД в другом tablespace? Так нельзя делать, снепшот должен включать все tablespace, xact и wal. В противном случае данные превратятся в мусор при первой возможности.
r9deyes Автор
02.05.2022 23:30Хорошее замечание. БД и снепшоты использовались в относительно несложном приложении. Т.е. только работа с таблицами внутри одной базы.
Ещё нужно заметить, что раздел с системными сущностями postgres остался нетронутым, он даже может не располагаться в btrfs. Поэтому кажется, что все снепшоты с базами и tablespace остаются независимыми.
Можете привести пример, какие действия могут привести к превращению данных в мусор?
mvv-rus
02.05.2022 21:43+2Инструкция linux only, про поддержку CoW файловых систем на Windows не в курсе.
Немного весьма сырых сведений про Windows и мгновенные копии томов: на всякий случай, вдруг кому пригодится.
В Windows снимки томов (в том числе и путем копирования при записи AKA CoW) поддерживаются Volume Shadow Copy Service (VSS) на уровне дискового тома, т.е. — ниже уровня файловой системы. Фактическая поддержка осуществляется компонентом под названием VSS Provider. Этот компонент может быть как аппаратным (использющим возможности хранилища SAN, на котором размещен том; как именно реализована поддержка — зависит от хранилища), так и программным, реализованным внутри Windows. Программный — «Microsoft Software Shadow Copy provider» — реализует именно копирование при записи и взаимодействует с файловой системой (на том же или другом томе) для хранения в ней старых копий перезаписываемых блоков.
Созданный снимок может быть смонтирован в системе как обычный дисковый том, с которым можно работать обычными средсвами ОС.
Для взаимодействия с VSS используются, естественно, другие команды, нежели с файловыми системами Unix — vssadmin и diskshadow.
Написать скрипты для использования этих команд для целей, указанных в этой статье, наверное, можно, но лично я этим не занимался и не планирую. Может, кто-нибудь ещё?
PS А ещё для взаимодействия приложений с VSS в Windows есть концепция VSS Writer: зарегистрированных в системе модулей, к которым VSS обращается в момент создания снимка и которые приводят файлы данных «своего» приложения в состояние, в котором они записываются на ленту при резервном копировании средствами приложения — то есть, для полученных таким образом снимков файлов можно использовать опции инкрементного и разностного копирования. При этом Writer сообщает VSS список файлов относящихся к данным приложения — чтобы при создании резервной копии можно было копировать только их. Но AFAIK, для Postgres Writer никто так и не написал, так что для получения копий его файлов данных в Windows остается только брать файлы с теневой копии на момент создания снимка (crash-consistent), как и в этой статье
rrrad
У postgres.ai есть чтото похожее, при чем там целая экосистема вокруг этой штуки сделана