Сейчас база данных проекта находится в Digital Ocean, на обычном дроплете с Linux и MariaDB. Сама база порядка 80 гигабайт, некоторые таблицы — более 100 миллионов записей.
Переносить будем в AWS RDS Aurora MySQL.
Содержание
Задача
Что мы хотим: максимально безболезнено перенести саму базу, и переключить приложение на работу с новой БД, при этом желательно обойтись либо без даунтаймов вообще — либо с каким-то максимально минимальным прерыванием сервиса.
Решение
Что используем: AWS Database Migration Service (AWS DMS) — предназначена для переноса баз данных в AWS, при этом исходная база остаётся полностью рабочей:
Обычно ссылки добавляю в конце, но в этот раз пусть будут тут, плюс по ходу в самом посте:
Кроме того, в исходной базе используется партиционирование, и надо проверить как будет работать приложение после миграции, т.к. партиционирование переносить не планируется. См. So, What is MySQL Partitioning? (проверили — не играет роли вообще).
Сначала немного теории, и потом приступим к практике.
Homogeneous vs Heterogeneous migration
homogeneous migration: миграция из source базы в target базу, где и source и target работают под управлением одной и той же системы управления базами данных, например — MySQL в MySQL (или MySQL в MariaDB)
heterogeneous migration: миграция из source базы в target базу, где и source и target работают под управлением различных системы управления базами данных, например — MySQL в PostgreSQL.
В нашем случае это будет homogeneous миграция.
Database Replication vs Migration
При database migration данные переносятся из одной базы данных в другую. После миграции данных — исходная база данных удаляется, а все клиенты перенаправляются на новую базу.
В некоторых случаях исходная база может оставаться в работе на случай проблем с новой базой, но после того, как приложение начинает нормально работать с новой базой — старая удаляется.
При database replication — данные постоянно трансилуются из исходной базы в новую без удаления старой. Так же, такая репликация иногда называется database streaming.
«У репликации есть начало — но нет конца» — обе базы могут оставаться рабочими и использоваться приложением, хотя в конце-концов репликация может стать миграцией.
Варианты миграции
RDS MySQL в RDS Aurora: через снапшот — создаётся копия (snapshot) MySQL RDS базы, из которой потом разворачивается Aurora RDS кластер
RDS MySQL в RDS Aurora: через Aurora Slave инстанс — из копии RDS MySQL создаётся Slave Aurora, который потом промоутится до Master-хоста
MySQL self-hosted: с помощью стандартных инструментов типа Percona XtraBackup или mysqldump — создаём снапшот базы, загружаем в AWS S3 корзину, потом из снапшота в корзине разворачиваем Aurora RDS кластер
Или, разумеется — AWS Database Migration Service, который мы и используем.
Schema Conversion Tool vs Database Migration Service
В некоторых случаях ни один из вариантов может не работать, например при миграции MySQL 5.7 на Aurora, которая совместима с MySQL 5.6.
В таком случае создание из RDS-снапшота или из MySQL бекапа в AWS S3 и затем создание Aurora Read Replica для MySQL может не сработать из-за несовместимости версий MySQL.
В таком случае используется AWS Schema Conversion Tool (AWS SCT) + AWS Database Migration Service (AWS DMS).
SCT
SCT может применяться во время миграций MySQL InnoDB в Amazon Aurora, для переноса и изменения схемы, процедур и функций из исходной БД в новую базу.
Учтите, что SCT не выполняет переноса пользователей — может потребоваться их ручное создание.
Совместимость и варианты миграции с использованием SCT:
Source database
Target database on Amazon RDS
Microsoft SQL Server (version 2008 and later)
Amazon Aurora with MySQL compatibility, Amazon Aurora with PostgreSQL compatibility, MariaDB 10.2 and 10.3, Microsoft SQL Server, MySQL, PostgreSQL
Другой вариант миграции — с использованием AWS DMS.
DMS может выполнить единоразовую миграцию, либо выполнять постоянную репликацию двух баз данных.
В случае миграции MySQL базы в Aurora — мы сначала выполним копирование всех данных, а затем будет выполняться репликация, или Change Data Capture (CDC).
Для этого DMS требует включенного бинлога транзакций на исходной базе, см. Prerequisite и в документации.
Prerequisite
Проверим бинлоги.
В AWS RDS настраивается через parameter group, в on-demand MySQL/MariaDB — через my.cnf.
Проверяем на сервер-источнике:
Dev — отключены:
MariaDB [(none)]> show binary logs;
ERROR 1381 (HY000): You are not using binary logging
На Production всё включено, т.к. там имеется активная read-replica:
MariaDB [(none)]> show binary logs;
+------------------+------------+
| Log_name | File_size |
+------------------+------------+
| mysql-bin.000271 | 1073742323 |
| mysql-bin.000272 | 1073742366 |
| mysql-bin.000273 | 245820301 |
+------------------+------------+
3 rows in set (0.107 sec)
И сама база, которую мы сегодня будем мигрировать:
MariaDB [(none)]> SELECT table_name AS table_name, engine, ROUND(data_length/1024/1024,2) AS total_size_mb, table_rows FROM information_schema.tables WHERE table_schema='new-eat';
Replication Instance — такой себе «промежуточный» сервер баз данных, который собственно и будет ходить в наш Source Target, получать оттуда данные, и сохранять у себя, а потом передавать их на Target Instance.
Переходим в DMS => Replication instances => Create replication instance:
General settings
instance type — мы ожидаем 80 гиг базу, с таблицами свыше 100 миллионов записей, так что возьмём машину посерьёзнее, см. документацию тут>>>
R4 instances are memory optimized for memory-intensive workloads. Ongoing migrations or replications of high-throughput transaction systems using AWS DMS can also consume large amounts of CPU and memory. R4 instances include more memory per vCPU than earlier generation instance types.
Engine version — оставляем дефолтную, последнюю
Allocated storage — у нас только база 80 гиг, плюс запас — пусть будет 200 гиг
VPC — наш Target instance живёт в своей отдельной AWS VPC, поселим реплику туда же, что бы не возиться с Security Groups
Multi AZ — не вижу смысла
Publicly accessible — тоже особо разницы нет — подключиться к нему мы всё-равно не сможем (но это не точно), но пусть будет — всё равно после миграции убьём этот сервер
Advanced security and network configuration
Replication subnet group — вот тут я не понял: VPC выбрана aurora-web-dev-vpc, а в Replication subnet group доступна subnet из default VPC… ну — ладно, пусть будет
Availability zone — без разницы
VPC security group(s) — тут ОК, доcтупна SecurityGroup из выбранной VPC, используем её
В Maintenance — ничего не меняем, жмём Create:
Пока создаётся инстанс — идём в Endpoints, и создадим наши Source и Target endpoints.
Endpoints
Source endpoint
В DigitalOcean сейчас запущен отдельный дроплет, на котором полная копия Production базы данных — это будет наш Source, источник данных.
Создаём Source Endpoint:
Endpoint identifier — имя ендпоинта, как мы будем его видеть в списке дашборды DMS
Source engine — у нас в DigitalOcean работает MariaDB, выбираем mariadb (хотя и с mysql типом работало вроде нормально)
Server name — адрес сервера (при чём тут «name»? можно было яснее поле назвать — Server address, не?)
port, username, password — понятно, указываем рутовые данные доступа
Test endpoint connection — имеет смысл проверить подключение.
Не очень понял, какую VPC он тут запрашивает, но выберем нашу, в которой запущен и будущий Target, и наш Replication instance (но работает и без выбора VPC вообще).
Запускаем тест, ждём минуту — Successful:
Кликаем Create Endpoint.
Target endpoint
Тут всё тоже самое, только вверху выбираем тип Target Instance, и Select RDS DB instance:
Ендпоинты готовы:
Помните про Security Groups между Replication, Source и Target инстансами — должен быть доступ по порту 3306 (в случае MySQL/MariaDB).
Migration task
Переходим в Database migration tasks, кликаем Create task:
Task configuration
Указываем имя задачи, как она будет отображаться у нас в DMS, выбираем Replication instance, Source и Target.
В Migration type выбираем Migrate existing data and replicate ongoing changes — склонируем всю базу, а затем будем подтягивать изменения, которые в ней будут появляться.
Migrate existing data only — Use this migration type for one-time migrations.
Migrate existing data and replicate ongoing changes — Use this migration type to migrate large databases to the AWS Cloud with minimal downtime.
Migrate ongoing replication changes — Use this migration type when you have already migrated the existing data and want to synchronize the source database with the target
Task settings
Target table preparation mode — выбираем Drop tables on target — пересоздадим таблицу, если она уже есть в Авроре
Stop task after full load completes — не останавливаем Change Data Capture (CDC), пусть тянет себе изменения дальше — в Production миграции так и будет, пока мы не переключим приложение на новый сервер баз данных
| 4055 | root | ec2-3-***-*-78.us-east-2.compute.amazonaws.com:54152 | NULL | Query | 39 | Writing to net | SELECT `id`,`created_at`,`updated_at`,`order_id`,`action`,`data` FROM `new-eat`.`order_logs` |
| 4051 | root | ec2-3-***-*-78.us-east-2.compute.amazonaws.com:53936 | NULL | Query | 42 | Writing to net | SELECT `id`,`order_id`,`order_hash`,`cookie` FROM `new-eat`.`order_cookies` |
...
| 4030 | root | ec2-3-***-*-78.us-east-2.compute.amazonaws.com:53650 | NULL | Binlog Dump | 51 | Master has sent all binlog to slave; waiting for binlog to be up | NULL |
| 4030 | root | ec2-3-***-*-78.us-east-2.compute.amazonaws.com:53650 | NULL | Binlog Dump | 13400 | Master has sent all binlog to slave; waiting for binlog to be up | NULL |
Добавим руками запись в исходную базу, например — создадим новую таблицу:
MariaDB [new-eat]> CREATE TABLE dms_test (id varchar(255) PRIMARY KEY);
Query OK, 0 rows affected (0.116 sec)
Проверяем в DMS:
И в Aurora:
MySQL [new-eat]> show tables like "dms_test";
+----------------------------------+
| Tables_in_new-eat (dms_test) |
+----------------------------------+
| dms_test |
+----------------------------------+
1 row in set (0.123 sec)
Всё скопировалось.
Ошибки DMS
При первом запуске наткнулся на несколько ошибок — вынесу их отдельным блоком.
Errors in MySQL server binary logging configuration
Ошибка:
Last Error Failed in resolving configuration. Task error notification received from subtask 0, thread 0 [reptask/replicationtask.c:2814] [1020418] Error Code [10002] : MySQL binary Logging must use ROW format; Errors in MySQL server binary logging configuration. Follow all prerequisites for ‘MySQL as a source in DMS’ from https://docs.aws.amazon.com/dms/latest/userguide/CHAP_Source.MySQL.html or’MySQL as a target in DMS’ from https://docs.aws.amazon.com/dms/latest/userguide/CHAP_Target.MySQL.html ; Failed while preparing stream component ‘st_0_V4MHLPR4QEGCKXNIEW6EFBECZJWXY45HPAH5VDA’.; Cannot initialize subtask; Stream component ‘st_0_V4MHLPR4QEGCKXNIEW6EFBECZJWXY45HPAH5VDA’ terminated [reptask/replicationtask.c:2821] [1020418] Stop Reason FATAL_ERROR Error Level FATAL
Проверяем настройки в Source:
MariaDB [(none)]> show variables like 'binlog_format';
MariaDB [(none)]> show variables like 'binlog_format';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| binlog_format | ROW |
+---------------+-------+
Заодно меняем binlog_checksum:
MariaDB [(none)]> show variables like 'binlog_checksum';
+-----------------+-------+
| Variable_name | Value |
+-----------------+-------+
| binlog_checksum | CRC32 |
+-----------------+-------+
Должен быть в NONE.
CloudWatch logs && DMS
Логи? Ага, счаз:
Иногда не создаются Log Group, почему — не знаю, написал в тех. поддержку.
UPD: уже перед самой «отправкой в печать» этого поста — пришёл ответ от тех. поддержки. В двух словах — «В AWS IAM что-то пошло не так, как должно — поправьте вручную»:
I have investigated the DMS task and found that logging is enabled. This generally occurs when DMS does not have the needed role to publish logs to CloudWatch. What happens in the back-end is that every running tasks will have its logs created on the Replication Instance (In DMS). DMS will then try to push the logs to CloudWatch for so that you can view the Logs from CloudWatch. Due to permissions, DMS as a service would need a role that allows it Access to CloudWatch for it to be able to publish/push the task logs to CloudWatch. If this role does not exist, then DMS silently fails and when you check for logs in CloudWatch, you will not find any.
This role is called «dms-cloudwatch-logs-role». I checked for this role under your environment and could not find it. Usually if you create the Task using the DMS Console, this role automatically gets added to your environment. If you however create your resources using the CLI or SDK, then you need to manually add this role as explained on the link [1] under references.
Ну, окей — мелочь, в принципе, но для Production миграции надо будет включить.