Mysql
 sql >> Base de données >  >> RDS >> Mysql

MySQL dans le cloud - Migration en ligne d'Amazon RDS vers votre propre serveur :2e partie

Comme nous l'avons vu précédemment, il peut être difficile pour les entreprises de déplacer leurs données hors de RDS pour MySQL. Dans la première partie de ce blog, nous vous avons montré comment configurer votre environnement cible sur EC2 et insérer une couche proxy (ProxySQL) entre vos applications et RDS. Dans cette deuxième partie, nous allons vous montrer comment effectuer la migration proprement dite des données vers votre propre serveur, puis rediriger vos applications vers la nouvelle instance de base de données sans temps d'arrêt.

Copier des données hors du RDS

Une fois que le trafic de notre base de données s'exécute via ProxySQL, nous pouvons commencer les préparatifs pour copier nos données hors de RDS. Nous devons le faire afin de configurer la réplication entre RDS et notre instance MySQL exécutée sur EC2. Une fois cela fait, nous configurerons ProxySQL pour rediriger le trafic de RDS vers notre MySQL/EC2.

Comme nous en avons discuté dans le premier article de blog de cette série, la seule façon d'extraire des données du RDS est via un vidage logique. Sans accès à l'instance, nous ne pouvons pas utiliser d'outils de sauvegarde physique à chaud comme xtrabackup. Nous ne pouvons pas non plus utiliser d'instantanés car il n'y a aucun moyen de créer autre chose qu'une nouvelle instance RDS à partir de l'instantané.

Nous sommes limités aux outils de vidage logique, donc l'option logique serait d'utiliser mydumper/myloader pour traiter les données. Heureusement, mydumper peut créer des sauvegardes cohérentes afin que nous puissions compter sur lui pour fournir les coordonnées binlog auxquelles notre nouvel esclave pourra se connecter. Le principal problème lors de la création de répliques RDS est la politique de rotation binlog - le vidage et le chargement logiques peuvent prendre même des jours sur des ensembles de données plus volumineux (des centaines de gigaoctets) et vous devez conserver les binlogs sur l'instance RDS pendant toute la durée de ce processus. Bien sûr, vous pouvez augmenter la rétention de rotation binlog sur RDS (appelez mysql.rds_set_configuration('binlog retention hours', 24); - vous pouvez les conserver jusqu'à 7 jours) mais il est beaucoup plus sûr de le faire différemment.

Avant de procéder au vidage, nous allons ajouter un réplica à notre instance RDS.

Tableau de bord Amazon RDS Créer une réplique de la base de données dans RDS

Une fois que nous avons cliqué sur le bouton "Créer un réplica en lecture", un instantané sera lancé sur le réplica RDS "maître". Il sera utilisé pour provisionner le nouvel esclave. Le processus peut prendre des heures, tout dépend de la taille du volume, de la dernière fois qu'un instantané a été pris et des performances du volume (io1/gp2 ? Magnétique ? Combien de pIOPS un volume a-t-il ?).

Réplica RDS maître

Lorsque l'esclave est prêt (son statut est passé à "disponible"), nous pouvons nous y connecter en utilisant son point de terminaison RDS.

Esclave RDS

Une fois connecté, nous arrêterons la réplication sur notre esclave - cela garantira que le maître RDS ne purgera pas les journaux binaires et ils seront toujours disponibles pour notre esclave EC2 une fois que nous aurons terminé notre processus de vidage/rechargement.

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)

Maintenant, il est enfin temps de copier les données vers EC2. Tout d'abord, nous devons installer mydumper. Vous pouvez l'obtenir sur github :https://github.com/maxbube/mydumper. Le processus d'installation est assez simple et bien décrit dans le fichier readme, nous ne le couvrirons donc pas ici. Vous devrez très probablement installer quelques packages (répertoriés dans le fichier readme) et le plus difficile est d'identifier quel package contient mysql_config - cela dépend de la version de MySQL (et parfois aussi de la version de MySQL).

Une fois que mydumper est compilé et prêt à fonctionner, vous pouvez l'exécuter :

[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
. 

Veuillez noter --lock-all-tables qui garantit que l'instantané des données sera cohérent et qu'il sera possible de l'utiliser pour créer un esclave. Maintenant, nous devons attendre que mydumper termine sa tâche.

Une étape supplémentaire est nécessaire - nous ne voulons pas restaurer le schéma mysql mais nous devons copier les utilisateurs et leurs autorisations. Nous pouvons utiliser pt-show-grants pour cela :

[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

Un exemple de subventions pt-show peut ressembler à ceci :

-- 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'@'%';

C'est à vous de choisir quels utilisateurs doivent être copiés sur votre instance MySQL/EC2. Cela n'a pas de sens de le faire pour tous. Par exemple, les utilisateurs root n'ont pas le privilège "SUPER" sur RDS, il est donc préférable de les recréer à partir de zéro. Ce que vous devez copier, ce sont les autorisations pour l'utilisateur de votre application. Nous devons également copier les utilisateurs utilisés par ProxySQL (proxysql-monitor dans notre cas).

Insertion de données dans votre instance MySQL/EC2

Comme indiqué ci-dessus, nous ne voulons pas restaurer les schémas du système. Par conséquent, nous déplacerons les fichiers liés à ces schémas hors de notre répertoire mydumper :

[email protected]:~# mkdir /tmp/rdsdump_sys/
[email protected]:~# mv /tmp/rdsdump/mysql* /tmp/rdsdump_sys/
[email protected]:~# mv /tmp/rdsdump/sys* /tmp/rdsdump_sys/

Lorsque nous en avons terminé, il est temps de commencer à charger les données dans l'instance MySQL/EC2 :

[email protected]:~/mydumper# ./myloader -d /tmp/rdsdump/ -u tpcc -p tpccpass -t 4 --overwrite-tables -h 172.30.4.238

Veuillez noter que nous avons utilisé quatre threads (-t 4) - assurez-vous de le définir sur ce qui a du sens dans votre environnement. Il s'agit de saturer l'instance MySQL cible - CPU ou E/S, selon le goulot d'étranglement. Nous voulons en tirer le meilleur parti possible pour nous assurer que nous avons utilisé toutes les ressources disponibles pour charger les données.

Une fois les données principales chargées, il reste deux étapes à suivre, toutes deux liées aux composants internes de RDS et pouvant interrompre notre réplication. Tout d'abord, RDS contient quelques tables rds_* dans le schéma mysql. Nous voulons les charger au cas où certains d'entre eux seraient utilisés par RDS - la réplication échouera si notre esclave ne les aura pas. Nous pouvons le faire de la manière suivante :

[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

Un problème similaire concerne les tables de fuseaux horaires, nous devons les charger à l'aide des données de l'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

Lorsque tout cela est prêt, nous pouvons configurer la réplication entre RDS (maître) et notre instance MySQL/EC2 (esclave).

Configuration de la réplication

Mydumper, lors de l'exécution d'un vidage cohérent, écrit une position de journal binaire. Nous pouvons trouver ces données dans un fichier appelé metadata dans le répertoire dump. Jetons-y un coup d'œil, nous utiliserons ensuite la position pour configurer la réplication.

[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

Une dernière chose qui nous manque est un utilisateur que nous pourrions utiliser pour configurer notre esclave. Créons-en un sur l'instance 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)

Il est maintenant temps d'asservir notre serveur MySQL/EC2 à l'instance 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)

La dernière étape consistera à basculer notre trafic de l'instance RDS vers MySQL/EC2, mais nous devons d'abord le laisser rattraper son retard.

Lorsque l'esclave a rattrapé, nous devons effectuer une coupure. Pour l'automatiser, nous avons décidé de préparer un court script bash qui se connectera à ProxySQL et fera ce qui doit être fait.

# 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

Quand tout est fait, vous devriez voir le contenu suivant dans la table mysql_servers :

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              |             |
+--------------+-----------------------------------------------+------+--------+--------+-------------+-----------------+---------------------+---------+----------------+-------------+

Du côté de l'application, vous ne devriez pas voir beaucoup d'impact, grâce à la capacité de ProxySQL à mettre les requêtes en file d'attente pendant un certain temps.

Avec cela, nous avons terminé le processus de déplacement de votre base de données de RDS vers EC2. La dernière étape à faire est de supprimer notre esclave RDS - il a fait son travail et il peut être supprimé.

Dans notre prochain article de blog, nous nous appuierons sur cela. Nous allons parcourir un scénario dans lequel nous déplacerons notre base de données d'AWS/EC2 vers un fournisseur d'hébergement distinct.