Jak jsme viděli dříve, pro společnosti může být náročné přesunout svá data z RDS pro MySQL. V první části tohoto blogu jsme vám ukázali, jak nastavit vaše cílové prostředí na EC2 a vložit proxy vrstvu (ProxySQL) mezi vaše aplikace a RDS. V této druhé části vám ukážeme, jak provést skutečnou migraci dat na váš vlastní server a poté bez prostojů přesměrovat vaše aplikace na novou instanci databáze.
Kopírování dat z RDS
Jakmile provoz naší databáze běží přes ProxySQL, můžeme začít s přípravami na kopírování našich dat z RDS. Musíme to udělat, abychom mohli nastavit replikaci mezi RDS a naší instancí MySQL běžící na EC2. Jakmile to uděláme, nakonfigurujeme ProxySQL tak, aby přesměroval provoz z RDS do našeho MySQL/EC2.
Jak jsme diskutovali v prvním příspěvku na blogu v této sérii, jediný způsob, jak můžete získat data z RDS, je logický výpis. Bez přístupu k instanci nemůžeme používat žádné horké nástroje fyzického zálohování, jako je xtrabackup. Nemůžeme použít ani snímky, protože neexistuje způsob, jak ze snímku vytvořit něco jiného než novou instanci RDS.
Jsme omezeni na nástroje logického výpisu, proto by logickou možností bylo použít ke zpracování dat mydump/myloader. Naštěstí může mydumper vytvářet konzistentní zálohy, takže se na něj můžeme spolehnout, že poskytne binlogové souřadnice pro našeho nového otroka, ke kterému se může připojit. Hlavním problémem při vytváření replik RDS je zásada rotace binlogů – logický výpis a načítání může trvat i dny u větších (stovky gigabajtů) datových sad a po dobu celého tohoto procesu musíte uchovávat binlogy na instanci RDS. Jistě, můžete zvýšit uchování rotace binlogů na RDS (zavolejte mysql.rds_set_configuration('binlog retenční hodiny', 24); – můžete je ponechat až 7 dní), ale je mnohem bezpečnější to udělat jinak.
Než přistoupíme k vytváření výpisu, přidáme repliku do naší instance RDS.
Amazon RDS Dashboard Vytvoření repliky DB v RDSJakmile klikneme na tlačítko „Vytvořit přečtenou repliku“, spustí se snímek „hlavní“ repliky RDS. Bude použit k zajištění nového otroka. Proces může trvat hodiny, vše závisí na velikosti svazku, kdy byl naposledy pořízen snímek a výkonu svazku (io1/gp2? Magnetický? Kolik pIOPS má svazek?).
Hlavní replika RDSKdyž je slave připraven (jeho stav se změnil na „dostupný“), můžeme se k němu přihlásit pomocí jeho koncového bodu RDS.
RDS SlaveJakmile se přihlásíme, zastavíme replikaci na našem podřízeném zařízení – to zajistí, že hlavní server RDS nevyčistí binární protokoly a budou stále dostupné pro naši podřízenou jednotku EC2, jakmile dokončíme proces výpisu/znovu načtení.
mysql> CALL mysql.rds_stop_replication;
+---------------------------+
| Message |
+---------------------------+
| Slave is down or disabled |
+---------------------------+
1 row in set (1.02 sec)
Query OK, 0 rows affected (1.02 sec)
Nyní je konečně čas zkopírovat data do EC2. Nejprve musíme nainstalovat mydumper. Můžete jej získat z github:https://github.com/maxbube/mydumper. Proces instalace je poměrně jednoduchý a pěkně popsaný v souboru readme, takže se jím zde nebudeme zabývat. S největší pravděpodobností budete muset nainstalovat několik balíčků (uvedených v readme) a těžší je určit, který balíček obsahuje mysql_config - záleží na verzi MySQL (a někdy také verzi MySQL).
Jakmile budete mít mydump zkompilovaný a připravený k použití, můžete jej spustit:
[email protected]:~/mydumper# mkdir /tmp/rdsdump
[email protected]:~/mydumper# ./mydumper -h rds2.cvsw8xpajw2b.us-east-1.rds.amazonaws.com -p tpccpass -u tpcc -o /tmp/rdsdump --lock-all-tables --chunk-filesize 100 --events --routines --triggers
.
Vezměte prosím na vědomí --lock-all-tables, který zajišťuje, že snímek dat bude konzistentní a bude možné jej použít k vytvoření slave. Nyní musíme počkat, až mydumper dokončí svůj úkol.
Je vyžadován ještě jeden krok – nechceme obnovovat schéma mysql, ale potřebujeme zkopírovat uživatele a jejich granty. K tomu můžeme použít pt-show-grants:
[email protected]:~# wget http://percona.com/get/pt-show-grants
[email protected]:~# chmod u+x ./pt-show-grants
[email protected]:~# ./pt-show-grants -h rds2.cvsw8xpajw2b.us-east-1.rds.amazonaws.com -u tpcc -p tpccpass > grants.sql
Příklad pt-show-grants může vypadat takto:
-- Grants for 'sbtest'@'%'
CREATE USER IF NOT EXISTS 'sbtest'@'%';
ALTER USER 'sbtest'@'%' IDENTIFIED WITH 'mysql_native_password' AS '*2AFD99E79E4AA23DE141540F4179F64FFB3AC521' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK;
GRANT ALTER, ALTER ROUTINE, CREATE, CREATE ROUTINE, CREATE TEMPORARY TABLES, CREATE USER, CREATE VIEW, DELETE, DROP, EVENT, EXECUTE, INDEX, INSERT, LOCK TABLES, PROCESS, REFERENCES, RELOAD, REPLICATION CLIENT, REPLICATION SLAVE, SELECT, SHOW DATABASES, SHOW VIEW, TRIGGER, UPDATE ON *.* TO 'sbtest'@'%';
Je na vás, abyste si vybrali, kteří uživatelé mají být zkopírováni do vaší instance MySQL/EC2. Nemá smysl to dělat pro všechny. Například uživatelé root nemají oprávnění „SUPER“ na RDS, takže je lepší je znovu vytvořit od začátku. Co potřebujete zkopírovat, jsou granty pro uživatele vaší aplikace. Potřebujeme také zkopírovat uživatele používané ProxySQL (v našem případě proxysql-monitor).
Vkládání dat do vaší instance MySQL/EC2
Jak je uvedeno výše, nechceme obnovovat systémová schémata. Proto přesuneme soubory související s těmito schématy z našeho adresáře mydump:
[email protected]:~# mkdir /tmp/rdsdump_sys/
[email protected]:~# mv /tmp/rdsdump/mysql* /tmp/rdsdump_sys/
[email protected]:~# mv /tmp/rdsdump/sys* /tmp/rdsdump_sys/
Když jsme s tím hotovi, je čas začít načítat data do instance MySQL/EC2:
[email protected]:~/mydumper# ./myloader -d /tmp/rdsdump/ -u tpcc -p tpccpass -t 4 --overwrite-tables -h 172.30.4.238
Vezměte prosím na vědomí, že jsme použili čtyři vlákna (-t 4) - ujistěte se, že toto nastavíte na cokoli, co má ve vašem prostředí smysl. Je to všechno o nasycení cílové instance MySQL – buď CPU nebo I/O, v závislosti na úzkém hrdle. Chceme z toho vymáčknout co nejvíce, abychom zajistili, že jsme k načtení dat využili všechny dostupné zdroje.
Po načtení hlavních dat je třeba provést další dva kroky, oba se týkají vnitřních částí RDS a oba mohou přerušit naši replikaci. Za prvé, RDS obsahuje několik tabulek rds_* ve schématu mysql. Chceme je načíst pro případ, že některé z nich používá RDS – replikace se přeruší, pokud je náš slave nebude mít. Můžeme to udělat následujícím způsobem:
[email protected]:~/mydumper# for i in $(ls -alh /tmp/rdsdump_sys/ | grep rds | awk '{print $9}') ; do echo $i ; mysql -ppass -uroot mysql < /tmp/rdsdump_sys/$i ; done
mysql.rds_configuration-schema.sql
mysql.rds_configuration.sql
mysql.rds_global_status_history_old-schema.sql
mysql.rds_global_status_history-schema.sql
mysql.rds_heartbeat2-schema.sql
mysql.rds_heartbeat2.sql
mysql.rds_history-schema.sql
mysql.rds_history.sql
mysql.rds_replication_status-schema.sql
mysql.rds_replication_status.sql
mysql.rds_sysinfo-schema.sql
Podobný problém je s tabulkami časových pásem, musíme je načíst pomocí dat z instance RDS:
[email protected]:~/mydumper# for i in $(ls -alh /tmp/rdsdump_sys/ | grep time_zone | grep -v schema | awk '{print $9}') ; do echo $i ; mysql -ppass -uroot mysql < /tmp/rdsdump_sys/$i ; done
mysql.time_zone_name.sql
mysql.time_zone.sql
mysql.time_zone_transition.sql
mysql.time_zone_transition_type.sql
Až bude vše připraveno, můžeme nastavit replikaci mezi RDS (master) a naší instancí MySQL/EC2 (slave).
Nastavení replikace
Mydump, když provádí konzistentní výpis, zapíše pozici binárního protokolu. Tato data můžeme najít v souboru s názvem metadata v adresáři výpisu. Pojďme se na to podívat, pozici pak použijeme k nastavení replikace.
[email protected]:~/mydumper# cat /tmp/rdsdump/metadata
Started dump at: 2017-02-03 16:17:29
SHOW SLAVE STATUS:
Host: 10.1.4.180
Log: mysql-bin-changelog.007079
Pos: 10537102
GTID:
Finished dump at: 2017-02-03 16:44:46
Poslední věc, kterou nám chybí, je uživatel, kterého bychom mohli použít k nastavení našeho otroka. Vytvořme jeden na instanci RDS:
[email protected]:~# mysql -ppassword -h rds2.cvsw8xpajw2b.us-east-1.rds.amazonaws.com
mysql> CREATE USER IF NOT EXISTS 'rds_rpl'@'%' IDENTIFIED BY 'rds_rpl_pass';
Query OK, 0 rows affected (0.04 sec)
mysql> GRANT REPLICATION SLAVE ON *.* TO 'rds_rpl'@'%';
Query OK, 0 rows affected (0.01 sec)
Nyní je čas podřídit náš server MySQL/EC2 instanci RDS:
mysql> CHANGE MASTER TO MASTER_HOST='rds2.cvsw8xpajw2b.us-east-1.rds.amazonaws.com', MASTER_USER='rds_rpl', MASTER_PASSWORD='rds_rpl_pass', MASTER_LOG_FILE='mysql-bin-changelog.007079', MASTER_LOG_POS=10537102;
Query OK, 0 rows affected, 2 warnings (0.03 sec)
mysql> START SLAVE;
Query OK, 0 rows affected (0.02 sec)
mysql> SHOW SLAVE STATUS\G
*************************** 1. row ***************************
Slave_IO_State: Queueing master event to the relay log
Master_Host: rds2.cvsw8xpajw2b.us-east-1.rds.amazonaws.com
Master_User: rds_rpl
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin-changelog.007080
Read_Master_Log_Pos: 13842678
Relay_Log_File: relay-bin.000002
Relay_Log_Pos: 20448
Relay_Master_Log_File: mysql-bin-changelog.007079
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Replicate_Do_DB:
Replicate_Ignore_DB:
Replicate_Do_Table:
Replicate_Ignore_Table:
Replicate_Wild_Do_Table:
Replicate_Wild_Ignore_Table:
Last_Errno: 0
Last_Error:
Skip_Counter: 0
Exec_Master_Log_Pos: 10557220
Relay_Log_Space: 29071382
Until_Condition: None
Until_Log_File:
Until_Log_Pos: 0
Master_SSL_Allowed: No
Master_SSL_CA_File:
Master_SSL_CA_Path:
Master_SSL_Cert:
Master_SSL_Cipher:
Master_SSL_Key:
Seconds_Behind_Master: 258726
Master_SSL_Verify_Server_Cert: No
Last_IO_Errno: 0
Last_IO_Error:
Last_SQL_Errno: 0
Last_SQL_Error:
Replicate_Ignore_Server_Ids:
Master_Server_Id: 1237547456
Master_UUID: b5337d20-d815-11e6-abf1-120217bb3ac2
Master_Info_File: mysql.slave_master_info
SQL_Delay: 0
SQL_Remaining_Delay: NULL
Slave_SQL_Running_State: System lock
Master_Retry_Count: 86400
Master_Bind:
Last_IO_Error_Timestamp:
Last_SQL_Error_Timestamp:
Master_SSL_Crl:
Master_SSL_Crlpath:
Retrieved_Gtid_Set:
Executed_Gtid_Set:
Auto_Position: 0
Replicate_Rewrite_DB:
Channel_Name:
Master_TLS_Version:
1 row in set (0.01 sec)
Posledním krokem bude přepnutí našeho provozu z instance RDS na MySQL/EC2, ale nejprve ho musíme nechat dohnat.
Když slave doběhne, musíme provést přerušení. Abychom to zautomatizovali, rozhodli jsme se připravit krátký bash skript, který se připojí k ProxySQL a udělá, co je třeba udělat.
# At first, we define old and new masters
OldMaster=rds2.cvsw8xpajw2b.us-east-1.rds.amazonaws.com
NewMaster=172.30.4.238
(
# We remove entries from mysql_replication_hostgroup so ProxySQL logic won’t interfere
# with our script
echo "DELETE FROM mysql_replication_hostgroups;"
# Then we set current master to OFFLINE_SOFT - this will allow current transactions to
# complete while not accepting any more transactions - they will wait (by default for
# 10 seconds) for a master to become available again.
echo "UPDATE mysql_servers SET STATUS='OFFLINE_SOFT' WHERE hostname=\"$OldMaster\";"
echo "LOAD MYSQL SERVERS TO RUNTIME;"
) | mysql -u admin -padmin -h 127.0.0.1 -P6032
# Here we are going to check for connections in the pool which are still used by
# transactions which haven’t closed so far. If we see that neither hostgroup 10 nor
# hostgroup 20 has open transactions, we can perform a switchover.
CONNUSED=`mysql -h 127.0.0.1 -P6032 -uadmin -padmin -e 'SELECT IFNULL(SUM(ConnUsed),0) FROM stats_mysql_connection_pool WHERE status="OFFLINE_SOFT" AND (hostgroup=10 OR hostgroup=20)' -B -N 2> /dev/null`
TRIES=0
while [ $CONNUSED -ne 0 -a $TRIES -ne 20 ]
do
CONNUSED=`mysql -h 127.0.0.1 -P6032 -uadmin -padmin -e 'SELECT IFNULL(SUM(ConnUsed),0) FROM stats_mysql_connection_pool WHERE status="OFFLINE_SOFT" AND (hostgroup=10 OR hostgroup=20)' -B -N 2> /dev/null`
TRIES=$(($TRIES+1))
if [ $CONNUSED -ne "0" ]; then
sleep 0.05
fi
done
# Here is our switchover logic - we basically exchange hostgroups for RDS and EC2
# instance. We also configure back mysql_replication_hostgroups table.
(
echo "UPDATE mysql_servers SET STATUS='ONLINE', hostgroup_id=110 WHERE hostname=\"$OldMaster\" AND hostgroup_id=10;"
echo "UPDATE mysql_servers SET STATUS='ONLINE', hostgroup_id=120 WHERE hostname=\"$OldMaster\" AND hostgroup_id=20;"
echo "UPDATE mysql_servers SET hostgroup_id=10 WHERE hostname=\"$NewMaster\" AND hostgroup_id=110;"
echo "UPDATE mysql_servers SET hostgroup_id=20 WHERE hostname=\"$NewMaster\" AND hostgroup_id=120;"
echo "INSERT INTO mysql_replication_hostgroups VALUES (10, 20, 'hostgroups');"
echo "LOAD MYSQL SERVERS TO RUNTIME;"
) | mysql -u admin -padmin -h 127.0.0.1 -P6032
Když je vše hotovo, měli byste v tabulce mysql_servers vidět následující obsah:
mysql> select * from mysql_servers;
+--------------+-----------------------------------------------+------+--------+--------+-------------+-----------------+---------------------+---------+----------------+-------------+
| hostgroup_id | hostname | port | status | weight | compression | max_connections | max_replication_lag | use_ssl | max_latency_ms | comment |
+--------------+-----------------------------------------------+------+--------+--------+-------------+-----------------+---------------------+---------+----------------+-------------+
| 20 | 172.30.4.238 | 3306 | ONLINE | 1 | 0 | 100 | 10 | 0 | 0 | read server |
| 10 | 172.30.4.238 | 3306 | ONLINE | 1 | 0 | 100 | 10 | 0 | 0 | read server |
| 120 | rds2.cvsw8xpajw2b.us-east-1.rds.amazonaws.com | 3306 | ONLINE | 1 | 0 | 100 | 10 | 0 | 0 | |
| 110 | rds2.cvsw8xpajw2b.us-east-1.rds.amazonaws.com | 3306 | ONLINE | 1 | 0 | 100 | 10 | 0 | 0 | |
+--------------+-----------------------------------------------+------+--------+--------+-------------+-----------------+---------------------+---------+----------------+-------------+
Na straně aplikace byste neměli zaznamenat velký dopad, díky schopnosti ProxySQL řadit dotazy do fronty po nějakou dobu.
Tímto jsme dokončili proces přesunu vaší databáze z RDS do EC2. Posledním krokem, který je třeba udělat, je odstranit našeho RDS slave – odvedl svou práci a lze jej smazat.
V našem příštím příspěvku na blogu na tom budeme stavět. Projdeme si scénář, ve kterém přesuneme naši databázi z AWS/EC2 do samostatného poskytovatele hostingu.