Перенести данные из управляемого PostgreSQL Yandex Cloud в облачную базу данных PostgreSQL
Как устроена миграция
Данные переносятся между двумя управляемыми кластерами PostgreSQL:
- исходный кластер — Managed Service for PostgreSQL в Yandex Cloud;
- целевой кластер — облачная база данных PostgreSQL в Selectel.
В DBaaS нет доступа к хостовой системе и прав суперпользователя, поэтому все действия выполняются на уровне SQL-запросов и встроенных механизмов СУБД.
Используется встроенная логическая репликация PostgreSQL (Publication и Subscription): на исходном кластере создается публикация, на целевом кластере — подписка на нее. Сначала копируются существующие данные, затем непрерывно применяются изменения. Переключение приложения выполняется, когда приемник догнал источник, поэтому простой сводится к нескольким минутам.
Прямое создание слота логической репликации требует привилегированных прав на сервере, которых нет в DBaaS. Схема Publication/Subscription работает в рамках прав, доступных в управляемых кластерах.
Что нужно для миграции
Инфраструктура
- административный доступ к облачной инфраструктуре в Yandex Cloud и Selectel или возможность оперативно вносить изменения силами вашей команды;
- пользователь в СУБД исходного кластера с ролями
mdb_superuser,mdb_replication,mdb_monitor,mdb_adminи доступом к переносимым базам; - стабильный сетевой канал между площадками и настроенная маршрутизация — особенно важно для крупных баз, подробнее в подразделе Сетевая связность;
- настроенные сетевые политики (группы безопасности, firewall): кластеры должны видеть друг друга;
- при связи площадок по внутреннему каналу (VPN или Direct Connect) — DNS-резолвер для внутренней зоны
yandexcloud.net, чтобы обращаться к мастеру Yandex по имени хоста, подробнее в подразделе DNS-резолвер для зоны yandexcloud.net; - cвободное место на диске мастера исходного кластера под растущий WAL — не менее трети от размера базы;
- версия PostgreSQL в Selectel не ниже версии в исходном кластере;
Рабочее окружение
- ВМ с сетевым доступом к обоим кластерам для выполнения SQL-запросов и мониторинга. Альтернатива — рабочая станция с VPN, откуда доступны оба кластера;
- клиенты PostgreSQL нужной версии:
psql,pg_dump,pg_restore; - желательно - утилиты для проверки сети:
ping,telnet,traceroute,mtr,iftop, а также терминальный мультиплексор (tmux), чтобы сессии не обрывались при потере связи с рабочим местом.
Риски, которые нужно учесть заранее
-
нехватка места на источнике. После создания публикации и подписки сервер резервирует WAL под слот репликации. Чем больше записей, изменений и удалений, тем быстрее растет WAL. Для базы от 1 ТБ при канале около 1 Гбит/с за время первичного копирования может накопиться порядка 300 ГБ WAL. При заполнении диска кластер перейдет в режим
read-only, а это остановка записи для приложения; -
нестабильный канал. Обрыв связи во время первичного копирования прерывает репликацию, и ее придется запускать заново. Короткий обрыв на этапе доливки изменений обычно восстанавливается автоматически;
-
запись в целевой кластер. Кластер в Selectel во время репликации доступен на запись. Любые изменения данных в реплицируемых таблицах приведут к конфликту и остановке репликации. До переключения не записывайте ничего в целевой кластер;
-
смена мастера в источнике. Если мастер в Yandex Cloud переедет на другой хост, репликация прервется. Особые FQDN Yandex, указывающие на текущий мастер, использовать для репликации не рекомендуется: после смены ролей они могут указывать на старый хост до 10 минут;
-
пересечение подсетей. Подсети в Yandex Cloud и Selectel не должны пересекаться, иначе маршрутизация не заработает.
Сетевая связность
Чтобы провести миграцию, необходимо организовать стабильное сетевое соединение между Yandex Cloud и Selectel.
Варианты соединения между Yandex Cloud и Selectel
-
вариант 1 — Direct Connect. Рекомендуется для крупных баз данных. Выделенное подключение между Selectel и Yandex Cloud через точку присутствия операторов связи: строится L2/L3-линк, дальше взаимодействие идет через глобальный роутер Selectel. Услугу заказывают на обеих сторонах, каждый провайдер коммутирует свою часть. Маршрутизация настраивается на облачном роутере в Selectel и в таблице маршрутизации в Yandex Cloud. Условия тарификации трафика на время миграции уточняйте у вашего менеджера в Selectel;
-
вариант 2 — VPN-туннель. На каждой площадке поднимается ВМ, между ними строится туннель через интернет (WireGuard, OpenVPN, IPsec). ВМ должны находиться в облачных сетях, доступных для DBaaS. Маршрутизация — через облачный роутер в Selectel и таблицу маршрутизации в Yandex Cloud. Статические маршруты в Selectel лучше задавать на облачном роутере, а не в настройках подсети: тогда они применятся ко всем продуктам, для которых роутер является шлюзом по умолчанию, включая DBaaS. Сделать это можно через OpenStack CLI или API, в том числе Terraform.
DNS-резолвер для зоны yandexcloud.net
К хосту DBaaS в Yandex Cloud обращаются только по FQDN: внутренний IP-адрес хоста может измениться в любой момент. При этом FQDN относится к внутренней зоне Yandex и не резолвится снаружи, поэтому кластер в Selectel не найдет его через свой DNS.
Решение - временный DNS-резолвер на базе unbound на обеих площадках (подойдут и виртуальные машины с VPN):
- Unbound-01 в Yandex Cloud пересылает запросы к зоне
yandexcloud.netво внутренний DNS Yandex и слушает порт53на своем локальном интерфейсе; - Unbound-02 в Selectel пересылает запросы к зоне
yandexcloud.netна Unbound-01 и слушает порт53на своем локальном интерфейсе.
Кластер в Selectel использует Unbound-02 как DNS-сервер и получает актуальный внутренний IP-адрес мастера Yandex.
Можно обойтись без Unbound-02 и указать в Selectel адрес Unbound-01 напрямую, но при потере связи между площадками хосты останутся без единственного DNS-сервера.
В Yandex Cloud DNS-сервер — это первый адрес подсети. Например, для подсети 10.2.0.0/24 это 10.2.0.1. Посмотреть можно в параметрах облачной сети в консоли.
Пример конфигурации Unbound-01 (Yandex Cloud):
server:
port: 53
interface: 127.0.0.1
interface: 10.1.0.10 # IP локального интерфейса
access-control: 127.0.0.0/8 allow
access-control: 10.0.0.0/8 allow
do-ip4: yes
do-ip6: no
do-udp: yes
do-tcp: yes
num-threads: 2
num-queries-per-thread: 1024
hide-identity: yes
hide-version: yes
prefetch: yes
verbosity: 1
forward-zone:
name: "yandexcloud.net"
forward-addr: 10.1.0.1 # внутренний DNS Yandex
Пример конфигурации Unbound-02 (Selectel):
server:
port: 53
interface: 127.0.0.1
interface: 10.200.0.10 # IP локального интерфейса
access-control: 127.0.0.0/8 allow
access-control: 10.0.0.0/8 allow
do-ip4: yes
do-ip6: no
do-udp: yes
do-tcp: yes
num-threads: 2
num-queries-per-thread: 1024
hide-identity: yes
hide-version: yes
prefetch: yes
verbosity: 1
forward-zone:
name: "yandexcloud.net"
forward-addr: 10.1.0.10 # адрес Unbound-01
Здесь 10.200.0.10 — пример IP-адреса DNS-сервера для кластера в Selectel.
Шаги миграции
В инструкции используются названия параметров из примера — замените их на свои.
Подробнее о работе с кластером PostgreSQL в Selectel в разделе PostgreSQL.
1. Организовать связность между Yandex Cloud и Selectel
- Убедитесь, что вы организовали соединение между Yandex Cloud и Selectel и настроили маршрутизацию, подробнее в подразделе Сетевая связность.
- Убедитесь, что исходный и целевой кластер находятся в разных подсетях, иначе маршрутизация не сработает.
- Убедитесь, что настроенные сетевые политики (группы безопасности, firewall) позволяют кластерам видеть друг друга.
2. Провести анализ базы данных перед миграцией
Примеры запросов для проведения анализа смотрите в подразделе Полезные запросы.
- Определите, какие базы нужно перенести. Обязательно проверьте локаль (
LC_COLLATE,LC_CTYPE) - в целевом кластере она задается при создании базы данных и потом не меняется. - Соберите список пользователей и ролей.
- Выпишите параметры кластера, измененные относительно значений по умолчанию.
- Определите схемы, из которых реплицируются таблицы, — чаще всего public, но их может быть несколько.
- Убедитесь, что все таблицы поддерживаются логической репликацией. Foreign-таблицы, временные таблицы и материализованные представления исключаются из публикации.
- Найдите внешние источники (foreign servers) и обеспечьте их доступность из Selectel.
- Договоритесь с разработчиками о заморозке схемы на время миграции. Таблицы, созданные после запуска репликации, в том числе новые партиции и таблицы из миграций приложения, не попадут в нее.
- Проверьте таблицы без первичного ключа: для
UPDATEиDELETEв них нуженREPLICA IDENTITY, иначе изменения не реплицируются. - Составьте список расширений (extensions) - их нужно установить в целевом кластере до запуска репликации.
Полезные запросы
-
таблицы схемы
public:SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'; -
таблицы, поддерживаемые логической репликацией:
SELECT n.nspname, c.relname, c.relkindFROM pg_class cJOIN pg_namespace n ON n.oid = c.relnamespaceWHERE n.nspname NOT IN ('pg_catalog', 'information_schema')AND c.relkind IN ('r', 'p')AND c.relpersistence = 'p'; -
таблицы без первичного ключа:
SELECT n.nspname, c.relnameFROM pg_class cJOIN pg_namespace n ON n.oid = c.relnamespaceWHERE c.relkind = 'r'AND n.nspname NOT IN ('pg_catalog', 'information_schema')AND NOT EXISTS (SELECT 1 FROM pg_constraintWHERE conrelid = c.oid AND contype = 'p'); -
расширения:
\dx -
foreign-серверы, таблицы, обертки:
\des+\det+\dew+ -
базы и пользователи:
\l+\du+
3. Сделать резервную копию
Cделайте резервную копию базы данных. Для очень больших баз убедитесь, что есть актуальный бэкап средствами DBaaS.
4. Подготовить исходный кластер в Yandex Cloud
- Создайте отдельного пользователя для миграции, например,
selectel, с ролямиmdb_superuser,mdb_replication,mdb_monitor,mdb_adminи доступом к переносимым базам. - В группах безопасности разрешите трафик из подсетей Selectel. Если группа в значении
default, ограничений нет. - Проверьте запас свободного места на мастере, подробнее в подразделе Риски, которые нужно учесть заранее.
5. Подготовить целевой кластер в Selectel
Имена пользователя и базы задайте такими же, как в источнике: это упростит импорт схемы. Подробнее о работе с кластером PostgreSQL в Selectel в разделе PostgreSQL.
- Задайте параметры СУБД, близкие к источнику.
- Создайте пользователя - будущего владельца базы.
- Создайте пустую базу с этим владельцем. Локаль укажите такую же, как в источнике (например, en_US.UTF-8) - изменить ее потом нельзя.
- При необходимости создайте остальных пользователей, как в источнике.
- Установите расширения, найденные на этапе анализа.
- Разрешите в группах безопасности трафик из подсетей Yandex Cloud.
- Для кастомного DNS (адрес Unbound-02) создайте тикет в техподдержку Selectel - в панели управления для уже созданного кластера этот параметр не меняется.
6. Перенести схему базы данных из исходного кластера в целевой кластер
-
Выгрузите схему базы данных из исходного кластера:
pg_dump -h rc1a-source-srv.mdb.yandexcloud.net -p 6432 -U selectel \-d my_database --schema-only -f /data/my_database.sql -
Загрузите схему в целевой кластер:
psql -h master.destination-srv.c.dbaas.selcloud.ru -p 5432 -U my_user \-d my_database -f /data/my_database.sql -
Проверьте вывод на ошибки. Ошибки
GRANTдопустимы, если дополнительные пользователи еще не созданы. Сравните объекты в источнике и приемнике:\dt+ *.*\dv\des\dew
7. Запустить репликацию
В DBaaS нет прав на команду CREATE PUBLICATION ... FOR ALL TABLES, поэтому список таблиц задается вручную.
Пауза между созданием публикации и созданием подписки должна быть минимальной, поэтому мы предлагаем сначала подготовить обе команды, а затем выполнить их одну за другой. После создания подписки источник начинает копить WAL, и чем быстрее завершится первичное копирование, тем меньше он вырастет.
-
Получите список таблиц в исходном кластере:
psql -h rc1a-source-srv.mdb.yandexcloud.net -p 6432 -U selectel -d my_databaseSELECT table_schema || '.' || table_nameFROM information_schema.tablesWHERE table_schema = 'public' AND table_type = 'BASE TABLE'; -
Подготовьте команду для создания публикации в исходном кластере, но не выполняйте ее. В команде укажите все необходимые таблицы, кторые получили на шаге 1. Имя таблицы указывается обязательно со схемой, иначе будет ошибка:
CREATE PUBLICATION my_pub FOR TABLEpublic.table_1,public.table_2,public.table_3; -
Подготовьте команду для создания подписки в целевом кластере, но не выполняйте ее:
CREATE SUBSCRIPTION my_subCONNECTION 'host=rc1a-source-srv.mdb.yandexcloud.net port=6432dbname=my_database user=selectel password=<password>'PUBLICATION my_pubWITH (copy_data = true);Здесь:
hostиport— адрес и порт мастера источника;dbname— база источника;userиpassword— имя и пароль пользователя для миграции;my_pub— имя публикации;copy_data = true— параметр, который указывает на обязательное копирование существующих данных перед потоком изменений. Значение выставляется по умолчанию, но лучше указать его явно.
-
Выполните подготовленную команду
CREATE PUBLICATIONв исходном кластере. -
Выполните подготовленную команду
CREATE SUBSCRIPTIONв целевом кластере.
8. Проверить состояние репликации
-
Отслеживайте метрики в консолях. Если свободное место на мастере источника подходит к порогу, а первичное копирование еще не завершено, немедленно остановите репликацию (подробнее в подразделе Переключить приложение, шаг 5), иначе кластер источника перейдет в
read-only:- Yandex Cloud (исходный кластер) — нагрузка CPU и памяти на мастере, Disk usage on primary, Total size of WAL files, Free space (расстояние до порога перехода в read-only), сетевой трафик и пакеты;
- Selectel (целевой кластер) — vCPU и память кластера, размер WAL-файлов, размер базы (должен расти до размера источника), нагрузка сети.
-
В целевом кластере проверьте статус копирования по каждой таблице:
SELECT sub.subname, c.relname, sr.srsubstateFROM pg_subscription_rel srJOIN pg_subscription sub ON sr.srsubid = sub.oidJOIN pg_class c ON sr.srrelid = c.oidORDER BY sr.srsubstate, c.relname;Значения srsubstate:
i- инициализация,d- идет копирование данных,f- копирование таблицы завершено,s- синхронизирована,r- готово, идет штатная репликация.
Миграция готова к переключению, когда все таблицы в статусе r.
-
В целевом кластере проверьте общее состояние подписки:
SELECT subname, pid, received_lsn, last_msg_send_time,last_msg_receipt_time, latest_end_lsnFROM pg_stat_subscription;SELECT * FROM pg_stat_subscription_stats; -- счетчики ошибок -
В исходном кластере проверьте слот репликации и размер удерживаемого WAL:
SELECT slot_name, active,pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(),confirmed_flush_lsn)) AS lagFROM pg_replication_slots;SELECT * FROM pg_publication;
9. Переключить приложение
-
Остановите запись в базу источника — остановите приложение или переведите его в режим обслуживания.
-
Дождитесь, пока лаг на источнике станет равен 0. Используйте запрос к
pg_replication_slotsиз этапа Проверить состояние репликации, шаг 4. -
Сверьте количество строк. Запрос может выполняться долго для больших баз:
DO $$DECLARErec RECORD;cnt BIGINT;BEGINFOR rec INSELECT n.nspname, c.relnameFROM pg_class cJOIN pg_namespace n ON n.oid = c.relnamespaceWHERE c.relkind = 'r'AND n.nspname NOT IN ('pg_catalog', 'information_schema')ORDER BY 1, 2LOOPEXECUTE format('SELECT count(*) FROM %I.%I', rec.nspname, rec.relname)INTO cnt;RAISE NOTICE '%.%: %', rec.nspname, rec.relname, cnt;END LOOP;END $$;Размер таблиц на источнике и приемнике может отличаться вплоть до десятков гигабайт, в целевом кластере он обычно меньше. Это нормально: в исходном кластере хранятся «мертвые» версии строк до очистки (VACUUM), а репликация переносит только актуальные данные.
-
Восстановите значения последовательностей в целевом кластере — логическая репликация их не переносит, значения последовательностей в целевом кластере остаются начальными. Если не выставить их, приложение получит ошибку при вставке:
ERROR: duplicate key value violates unique constraint "users_pkey"DETAIL: Key (id)=(27) already exists.4.1. Выполните скрипт в целевом кластере в переносимой базе. Скрипт находит последовательности, привязанные к столбцам (
serialиidentity), и выставляет каждой значениеMAX(<column>) + 1:DO $$DECLAREseq RECORD;max_id BIGINT;BEGINFOR seq INSELECT c.oid::regclass AS seqname,n.nspname AS schemaname,t.relname AS tablename,a.attname AS columnnameFROM pg_class cJOIN pg_depend d ON d.objid = c.oid AND d.deptype IN ('a', 'i')JOIN pg_class t ON t.oid = d.refobjidJOIN pg_namespace n ON n.oid = t.relnamespaceJOIN pg_attribute a ON a.attrelid = t.oid AND a.attnum = d.refobjsubidWHERE c.relkind = 'S'LOOPEXECUTE format('SELECT max(%I) FROM %I.%I',seq.columnname, seq.schemaname, seq.tablename)INTO max_id;IF max_id IS NOT NULL THENPERFORM setval(seq.seqname, max_id + 1, false);END IF;END LOOP;END $$;4.2. Последовательности, не привязанные к столбцам, перенесите вручную: получите их текущие значения в исходном кластере через
SELECT last_value FROM <имя_последовательности>и выставьте в целевом кластере черезsetval. -
Остановите репликацию:
5.1. Удалите подписку в целевом кластере:
DROP SUBSCRIPTION my_sub;5.2. Удалите публикацию в исходном кластере:
DROP PUBLICATION my_pub;DROP SUBSCRIPTIONудаляет и слот репликации в исходном кластере, после чего накопленный WAL освобождается.5.3. Если исходный кластер в этот момент недоступен, слот останется — удалите его на исходном кластере вручную, иначе WAL продолжит расти:
SELECT pg_drop_replication_slot('my_sub'); -
Переключите строку подключения приложения на целевой кластер в Selectel и запустите приложение.
-
Опционально: удалите временные ресурсы (ВМ с VPN и DNS-резолверами, пользователь
selectelв исходном кластере).