Перенести данные из управляемого PostgreSQL Yandex Cloud в облачную базу данных PostgreSQL
Как устроена миграция
Данные переносятся между двумя управляемыми кластерами PostgreSQL:
- исходный кластер — Managed Service for PostgreSQL в Yandex Cloud;
- целевой кластер — облачная база данных PostgreSQL в Selectel.
Для миграции данных используется логическая репликация: на исходном кластере создается публикация, на которую подписывается принимающий кластер облачных баз данных. Сначала копируются существующие данные, затем непрерывно применяются изменения. Переключение приложения выполняется, когда приемник окончательно синхронизируется с источником, поэтому простой сервисов сводится к нескольким минутам.
В облачных базах данных Selectel не предоставляется прямой доступ к серверу баз данных и права суперпользователя, поэтому все действия выполняются на уровне SQL-запросов и встроенных механизмов СУБД.
Что нужно для миграции
Инфраструктура
- административный доступ к облачной инфраструктуре в Yandex Cloud и Selectel;
- пользователь в СУБД исходного кластера с ролями
mdb_superuser,mdb_replication,mdb_monitor,mdb_adminи доступом к переносимым базам; - стабильный сетевой канал между площадками и настроенная маршрутизация — особенно важно для крупных баз, подробнее в подразделе Сетевая связность;
- настроенные сетевые политики (группы безопасности, файрвол), чтобы кластеры могли обмениваться трафиком друг с другом;
- при связи площадок по внутреннему каналу (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), чтобы сессии не обрывались при потере связи с рабочим местом.
Риски
Сетевая связность
Чтобы провести миграцию, необходимо организовать стабильное сетевое соединение между Yandex Cloud и Selectel.
Варианты соединения между Yandex Cloud и Selectel
-
вариант 1 — Direct Connect. Рекомендуется для крупных баз данных. Выделенное подключение между Selectel и Yandex Cloud через точку присутствия операторов связи: создается L2/L3-соединение, дальше взаимодействие выполняется через глобальный роутер Selectel. Услугу заказывают на обеих сторонах, каждый провайдер коммутирует свою часть. Маршрутизация настраивается на облачном роутере в Selectel и в таблице маршрутизации в Yandex Cloud. Условия тарификации трафика на время миграции уточняйте у вашего менеджера в Selectel;
-
вариант 2 — VPN-туннель. На каждой площадке создается виртуальная машина (облачный сервер — в Selectel), между ними строится туннель через интернет (WireGuard, OpenVPN, IPsec). Виртуальные машины должны находиться в облачных сетях, доступных для управляемых баз данных. Маршрутизация — через облачный роутер в Selectel и таблицу маршрутизации в Yandex Cloud. Мы рекомендуем настраивать статические маршруты на облачном роутере, а не в настройках подсети: тогда они применятся ко всем продуктам, для которых роутер является шлюзом по умолчанию, включая управляемые базы данных. Сделать это можно через OpenStack CLI или API, в том числе Terraform.
DNS-резолвер для зоны yandexcloud.net
К хосту PostgreSQL в 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.
Шаги миграции
В инструкции используются названия параметров из примера — замените их на свои.
1. Организовать связность между Yandex Cloud и Selectel
- Убедитесь, что вы организовали соединение между Yandex Cloud и Selectel и настроили маршрутизацию, подробнее в подразделе Сетевая связность.
- Убедитесь, что исходный и целевой кластеры находятся в разных подсетях, иначе маршрутизация не сработает.
- Убедитесь, что настроенные сетевые политики (группы безопасности, файрвол) позволяют кластерам обмениваться трафиком друг с другом.
2. Провести анализ базы данных перед миграцией
Примеры запросов для проведения анализа смотрите в подразделе Полезные запросы.
- Определите, какие базы нужно перенести.
- Проверьте локали
LC_COLLATE,LC_CTYPE. В целевом кластере они задаются при создании базы данных. После создания базы данных изменить локали нельзя. - Соберите список пользователей и ролей.
- Выпишите параметры кластера, измененные относительно значений по умолчанию.
- Определите схемы, из которых реплицируются таблицы (
publicи другие при наличии). - Убедитесь, что все таблицы поддерживаются логической репликацией. Foreign-таблицы, временные таблицы и материализованные представления исключаются из публикации.
- Найдите внешние источники (foreign servers) и обеспечьте их доступность из Selectel.
- Остановите изменение данных на время миграции. Таблицы, созданные после запуска репликации, в том числе новые партиции и таблицы из миграций приложения, не попадут в нее.
- Проверьте таблицы без первичного ключа: для
UPDATEиDELETEдолжен бытьREPLICA IDENTITY, иначе изменения не реплицируются. - Составьте список расширений — их нужно установить в целевом кластере до запуска репликации.
Полезные запросы
-
таблицы схемы
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. Подготовить исходный кластер в Yandex Cloud
- Сделайте бэкап баз данных.
- Создайте отдельного пользователя для миграции, например
selectel, с ролямиmdb_superuser,mdb_replication,mdb_monitor,mdb_adminи доступом к переносимым базам. - В группах безопасности разрешите трафик из подсетей Selectel. Если группа в значении
default, ограничений нет. - Проверьте запас свободного места на мастере, подробнее в подразделе Риски.
4. Подготовить целевой кластер в Selectel
-
Создайте кластер PostgreSQL. При создании кластера:
-
выберите версию PostgreSQL не ниже версии исходной базы данных;
-
выберите конфигурацию нод с размером диска, достаточным для объема переносимых данных.
-
-
Создайте пользователя - будущего владельца базы.
-
Создайте базу данных. Укажите локали такие же, как в источнике. После создания базы данных изменить локали нельзя.
-
Опционально: создайте остальных пользователей, как в источнике.
-
Добавьте расширения, которые вы определили при проведении анализ базы данных перед миграцией на шаге 10.
-
Для кастомного DNS (адрес Unbound-02) создайте тикет в техподдержку Selectel — в панели управления для уже созданного кластера этот параметр не меняется.
5. Перенести схему базы данных из исходного кластера в целевой кластер
-
Выгрузите схему базы данных из исходного кластера:
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
6. Запустить репликацию
В облачной базе данных PostgreSQL нет прав на команду 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в целевом кластере.
7. Проверить состояние репликации
-
Проверьте метрики кластеров:
- исходный кластер — нагрузка CPU и памяти на мастере,
Disk usage on primary,Total size of WAL files,Free space, сетевой трафик и пакеты; - целевой кластер — vCPU и память кластера, размер WAL-файлов, размер базы, нагрузка сети. Подробнее об отслеживании состояния кластера в инструкции Мониторинг кластера, нод и баз данных PostgreSQL.
- исходный кластер — нагрузка CPU и памяти на мастере,
-
Если свободное место на мастере источника заканчивается, а первичное копирование еще не завершено, остановите репликацию, иначе кластер источника перейдет в режим
read-only. Подробнее об остановке репликации в шаге 5 подраздела Переключить приложение. -
В целевом кластере проверьте статус копирования по каждой таблице:
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;
8. Переключить приложение
-
Остановите запись в базу источника — остановите приложение или переведите его в режим обслуживания.
-
Проверьте лаг на источнике:
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; -
Дождитесь, пока лаг на источнике станет равен 0.
-
Сверьте количество строк. Запрос может выполняться долго для больших баз данных:
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 $$;Размер таблиц на источнике и приемнике может отличаться примерно до десятки гигабайт. В целевом кластере размер таблиц обычно меньше. Это возникает из-за того, что в исходном кластере хранятся «мертвые» кортежи до их очистки, а репликация переносит только актуальные данные.
-
Восстановите значения последовательностей в целевом кластере — логическая репликация их не переносит, значения последовательностей в целевом кластере остаются начальными. Если не выставить их, приложение получит ошибку при вставке:
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 <sequences>;Укажите
<sequences>— имя последовательности.4.3. Выставьте значения последовательностей, которые вы получили на шаге 4.2, в целевом кластере через
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 и запустите приложение.
-
Опционально: удалите временные ресурсы, которые вы создавали для миграции данных.