background success stories

Backuper InnoDB Cluster proprement en version communautaire !

Après plusieurs interventions pour la mise en place d’InnoDB Cluster pour monter des environnements de tests, un de nos clients nous a demandé de mettre en production un cluster InnoDB en 5.7 en version communautaire, donc sans possibilité d’utiliser MySQL Enterprise Backup. Après quelques recherches, nous n’avons trouvé aucunes références pour la mise en place de backup production ready pour InnoDB Cluster. Cependant, Après quelques tests nous avons trouvé une solution qui semblerait convenir sans avoir à sortir un noeud du cluster.

Certains suggèrent d’utiliser des dumps, mais pour moi ce n’est pas une solution de sauvegarde valable dès qu’il y a du volume ou de l’activité et encore moins pour recouvrer un noeud.

Le cluster utilisé pour l’exemple est en mode single master à 3 noeuds, une configuration très classique.
Nous allons utiliser Percona XtraBackup et le script innobackupex.

Le but est de n’avoir qu’un seul backup et qui puisse être restauré sur chaque noeud sans différentiation.


Nous précisons que cette méthode n’a été validé qu’en version 5.7, nous n’avons pas pu tester cette solution en 8.0 pour le moment.

Sur InnoDB Cluster, il est important de conserver binlog et relay log car ils sont utilisés lors d’un recovery.

Les trois noeuds :
c7-innodb-01 (noeud primaire)
c7-innodb-02
c7-innodb-03
MySQL est en version 5.7.22, et mysqlsh en version 8.0.11

Le noeud dans notre cas est KO est doit être restauré depuis un backup issu du noeud 3.
Nous sauvegardons le fichier auto.cnf et les certificats/clés de chiffrement

Notre premier test consiste à faire un simple backup du noeud 3
avec la commande suivante :
[root@c7-innodb-03 ~]# innobackupex --user=root --password=***** /tmp/bck
Copie sur le noeud 2 :
scp -r /tmp/bck/2018-05-28_11-43-45 root@c7-innodb-02:/tmp/backup

Préparation du backup percona :

Sur le noeud 2 que nous allons restaurer :
[root@c7-innodb-02 tmp]# systemctl stop mysqld
[root@c7-innodb-02 tmp]# rm -rf /var/lib/mysql/*

‘0eae27f5-4f0f-11e8-9593-000c29ea79e7:1-76,
870433a8-4f0e-11e8-8665-000c29ea79e7:1-26’

[root@c7-innodb-02 2018-05-28_11-43-45]# cd /tmp/backup/2018-05-28_11-43-45/
Préparation du backup :
[root@c7-innodb-02 2018-05-28_11-43-45]# innobackupex --apply-log .
Copy back :
[root@c7-innodb-02 2018-05-28_11-43-45]# innobackupex --copy-back .

[root@c7-innodb-02 2018-05-28_11-43-45]# cp /tmp/backup/*.pem /var/lib/mysql/
[root@c7-innodb-02 2018-05-28_11-43-45]# cp /tmp/backup/auto.cnf /var/lib/mysql/
[root@c7-innodb-02 2018-05-28_11-43-45]# chown -R mysql:mysql /var/lib/mysql
[root@c7-innodb-02 2018-05-28_11-43-45]# systemctl start mysqld

Vérification du log au démarrage :
Les erreurs visibles (non génantes pour le service)
[ERROR] Plugin group_replication reported: 'The group name option is mandatory'
[ERROR] Plugin group_replication reported: 'Unable to start Group Replication on boot'

Les warnings plus intéressant sur les positions des fichiers, MySQL cherche le relay log du noeud 3 et nous sommes sur le noeud 2 :
[Warning] Recovery from master pos 236 and file for channel 'group_replication_applier'. Previous relay log pos and relay log file had been set to 15918, ./c7-innodb-03-relay-bin-group_replication_applier.000006 respectively.

[Warning] Recovery from master pos 4 and file for channel ‘group_replication_recovery’. Previous relay log pos and relay log file had been set to 4, ./c7-innodb-03-relay-bin-group_replication_recovery.000001 respectively.

Connexion au master :
[root@c7-innodb-02 ~]# mysqlsh innodbcluster@c7-innodb-01:3306
MySQL c7-innodb-01:3306 ssl JS > dba.getCluster().rejoinInstance('innodbcluster@c7-innodb-02:3306');
The instance 'c7-innodb-02:3306' was successfully rejoined on the cluster.

MySQL c7-innodb-01:3306 ssl JS > dba.getCluster().status()
{
« clusterName »: « myCluster »,
« defaultReplicaSet »: {
« name »: « default »,
« primary »: « c7-innodb-01:3306 »,
« ssl »: « REQUIRED »,
« status »: « OK_NO_TOLERANCE »,
« statusText »: « Cluster is NOT tolerant to any failures. 1 member is not active »,
« topology »: {
« c7-innodb-01:3306 »: {
« address »: « c7-innodb-01:3306 »,
« mode »: « R/W »,
« readReplicas »: {},
« role »: « HA »,
« status »: « ONLINE »
},
« c7-innodb-02:3306 »: {
« address »: « c7-innodb-02:3306 »,
« mode »: « R/O »,
« readReplicas »: {},
« role »: « HA »,
« status »: « RECOVERING »
},
« c7-innodb-03:3306 »: {
« address »: « c7-innodb-03:3306 »,
« mode »: « R/O »,
« readReplicas »: {},
« role »: « HA »,
« status »: « ONLINE »
}
}
},
« groupInformationSourceMember »: « mysql://innodbcluster@c7-innodb-01:3306 »
}

Le noeud apparait en recovering mais ne passe jamais en ONLINE

Allons voir le log d’erreur du noeud 2 :
018-05-28T10:46:55.505442Z 20 [ERROR] Slave SQL for channel 'group_replication_recovery': Error 'Can't create database 'toto'; database exists' on query. Default database: 'toto'. Query: 'create database toto', Error_code: 1007
2018-05-28T10:46:55.505470Z 20 [Warning] Slave: Can't create database 'toto'; database exists Error_code: 1007
2018-05-28T10:46:55.505478Z 20 [ERROR] Error running query, slave SQL thread aborted. Fix the problem, and restart the slave SQL thread with "SLAVE START". We stopped at log 'binlog.000005' position 4502

On peut constater qu’il cherche à prendre la plus ancienne instruction GTID à savoir le CREATE DATABASE pour mon jeu de données de test.
Il ne parvient donc pas à conserver la position GTID via Percona XtraBackup.

Nouveau test :
Nous allons récuperer binlog et relay log et nous allons intégrer des données entre la fin du backup percona et la copie des binlogs/relay logs
Avec pour chaque étape la vérification du GTID pour découvrir quelques (mauvaises surprises :))

Avec toujours un noeud KO :

0eae27f5-4f0f-11e8-9593-000c29ea79e7:1-78,
870433a8-4f0e-11e8-8665-000c29ea79e7:1-26

J’insère une valeur dans toto.mytable :
MySQL c7-innodb-01:3306 ssl SQL > insert into toto.mytable values (1);
Query OK, 1 row affected (0.0359 sec)

MySQL c7-innodb-01:3306 ssl SQL > select * from toto.mytable;
+—-+
| id |
+—-+
| 1 |
+—-+
1 row in set (0.0007 sec)

0eae27f5-4f0f-11e8-9593-000c29ea79e7:1-79,
870433a8-4f0e-11e8-8665-000c29ea79e7:1-26

Le GTID a bien avancé avec pour référence le noeud primaire
Il est temps de faire un backup sur le noeud 3

[root@c7-innodb-03 ~]# innobackupex --user=root --password=***** /tmp/bck

Nous allons simuler un peu de charge en ajoutant la valeur 2 dans notre table :
MySQL c7-innodb-01:3306 ssl SQL > insert into toto.mytable values (2);

Le nouveau GTID :
0eae27f5-4f0f-11e8-9593-000c29ea79e7:1-80,
870433a8-4f0e-11e8-8665-000c29ea79e7:1-26

[root@c7-innodb-03 ~]# cp /var/lib/mysql/binlog.* /tmp/bck/
[root@c7-innodb-03 ~]# cp /var/lib/mysql/c7-innodb-03-relay-bin-group_replication_* /tmp/bck/

Transfert sur le noeud 2 :
[root@c7-innodb-03 ~]# scp -r /tmp/bck/binlog.* root@c7-innodb-02:/tmp/backup
root@c7-innodb-02's password:
binlog.000002 100% 20KB 11.0MB/s 00:00
binlog.000003 100% 230 326.4KB/s 00:00
binlog.000004 100% 249 300.7KB/s 00:00
binlog.000005 100% 20KB 17.0MB/s 00:00
binlog.index 100% 64 96.0KB/s 00:00
[root@c7-innodb-03 ~]# scp -r /tmp/bck/c7-innodb-03-relay-bin-group_replication_* root@c7-innodb-02:/tmp/backup
root@c7-innodb-02's password:
c7-innodb-03-relay-bin-group_replication_applier.000005 100% 232 276.3KB/s 00:00
c7-innodb-03-relay-bin-group_replication_applier.000006 100% 17KB 13.8MB/s 00:00
c7-innodb-03-relay-bin-group_replication_applier.index 100% 116 164.6KB/s 00:00
c7-innodb-03-relay-bin-group_replication_recovery.000001 100% 233 276.1KB/s 00:00
c7-innodb-03-relay-bin-group_replication_recovery.000002 100% 269 345.6KB/s 00:00
c7-innodb-03-relay-bin-group_replication_recovery.index

On reproduit les mêmes étapes de restauration en rajoutant les binlogs/relay logs

[Warning] Recovery from master pos 233 and file for channel 'group_replication_applier'. Previous relay log pos and relay log file had been set to 17116, ./c7-innodb-03-relay-bin-group_replication_applier.000006 respectively.
2018-05-28T11:16:50.989078Z 0 [Note] Relay log recovery skipped for group replication channel.
2018-05-28T11:16:50.989098Z 0 [Warning] Recovery from master pos 4 and file for channel 'group_replication_recovery'. Previous relay log pos and relay log file had been set to 4, ./c7-innodb-03-relay-bin-group_replication_recovery.000001 respectively.

MySQL c7-innodb-01:3306 ssl JS > dba.getCluster().rejoinInstance('innodbcluster@c7-innodb-02:3306')

The instance ‘c7-innodb-02:3306’ was successfully rejoined on the cluster.

Le noeud apparait bien online :
MySQL c7-innodb-01:3306 ssl JS > dba.getCluster().status();
{
"clusterName": "myCluster",
"defaultReplicaSet": {
"name": "default",
"primary": "c7-innodb-01:3306",
"ssl": "REQUIRED",
"status": "OK",
"statusText": "Cluster is ONLINE and can tolerate up to ONE failure.",
"topology": {
"c7-innodb-01:3306": {
"address": "c7-innodb-01:3306",
"mode": "R/W",
"readReplicas": {},
"role": "HA",
"status": "ONLINE"
},
"c7-innodb-02:3306": {
"address": "c7-innodb-02:3306",
"mode": "R/O",
"readReplicas": {},
"role": "HA",
"status": "ONLINE"
},
"c7-innodb-03:3306": {
"address": "c7-innodb-03:3306",
"mode": "R/O",
"readReplicas": {},
"role": "HA",
"status": "ONLINE"
}
}
},
"groupInformationSourceMember": "mysql://innodbcluster@c7-innodb-01:3306"
}

Sur le noeud 1 :

MySQL c7-innodb-01:3306 ssl SQL > select * from toto.mytable;
+----+
| id |
+----+
| 1 |
| 2 |
+----+

Sur le noeud 2 que l’on vient de restaurer :
MySQL c7-innodb-01:3306 ssl SQL > \connect innodbcluster@c7-innodb-02:3306

MySQL c7-innodb-02:3306 ssl SQL > select * from toto.mytable;
+—-+
| id |
+—-+
| 1 |
+—-+

La valeur 2 n’a pas été insérée, le GTID a bien avancé sans erreur ni warning mais l’enregistrement a été perdu.
0eae27f5-4f0f-11e8-9593-000c29ea79e7:1-81,
870433a8-4f0e-11e8-8665-000c29ea79e7:1-26

Cette solution n’est donc pas valable car elle entraine une perte de données.
Nous avons ensuite essayé de mettre en « pause » le noeud sans pour autant le sortir du cluster :
FLUSH TABLES WITH READ LOCKS semble être un bon candidat pour faire le backup et copier les binlogs/relay logs de manière intègre :

La solution que nous avons trouvé est de lancer sur un noeud secondaire la commande FLUSH TABLES WITH READ LOCKS en maintenant la session ouverte le temps du backup.
En parallèle dans un autre thread on lance la commande classique de backup avec innobackupex cité précédemment, et on copie binlogs/relaylogs présent sur le serveur.
une fois ces opérations terminées, il est possible de faire un UNLOCK TABLES sur la session laissée ouverte ou simplement de la fermer ce qui le fera automatiquement. Le noeud rattrape ensuite son retard sans problèmes.
Cependant ce dernier apparaît toujours ONLINE et il est probable que les connexions aille toujours sur ce noeud. Il est donc indispensable de le faire sur une période d’activité creuse au risque d’avoir des données « périmées ». Il est aussi possible de sortir le noeud du cluster.

C’est une solution en attente d’une vrai procédure de sauvegarde InnoDB Cluster en mode Communautaire.
Si vous disposez de la souscription Enterprise, l’outil MySQL Enterprise Backup fonctionne très bien et n’a pas les soucis rencontrés avec la solution de Percona.