Adopte un… cluster MySQL Group Replication
Autant le dire tout de suite, rien avoir avec un site internet de rencontre en ligne! 🙂
C’est bel et bien un nouvel article dans la série, Haute Disponibilité avec MySQL.
Au menu d’aujourd’hui : comment passer de l’administration « manuelle » de votre solution HA MySQL Group Replication à une administration plus simple, plus fun mais surtout facilement automatisable avec le pack MySQL InnoDB Cluster. En clair, on va voir comment utiliser MySQL Shell pour l’administration et l’orchestration du cluster et MySQL Router pour rediriger automatiquement les transactions de l’application vers le noeud primaire du cluster.
Quelques pré-requis sont nécessaire pour optimiser ta compréhension de cet article, je te conseille donc la lecture préalable des articles suivants:
- Déployer un cluster MySQL Group Replication
- FAQ Webinar MySQL Group Replication
- Tester MySQL InnoDB Cluster
- Configurer ProxySQL pour MySQL Group Replication
Note:
L’article traite de MySQL InnoDB Cluster, HA natif de MySQL Server, solution à ne pas confondre avec MySQL NDB Cluster.
Le contexte
Pour ce PoC, j’ai un cluster MySQL Group Replication de 3 nœuds, fonctionnel, en mode « Single Primary » (déployé avec Docker Compose):
- Instance 1 : mysql_node1 (172.19.0.2)
- Instance 2 : mysql_node2 (172.19.0.4)
- Instance 3 : mysql_node3 (172.19.0.3)
$ docker inspect mysql_node1 | grep IPAddress | tail -1 "IPAddress": "172.19.0.2", $ docker inspect mysql_node2 | grep IPAddress | tail -1 "IPAddress": "172.19.0.4", $ docker inspect mysql_node3 | grep IPAddress | tail -1 "IPAddress": "172.19.0.3",
MySQL Router et mon application (simulée avec le client texte mysql) sont sur la machine host (par commodité). C’est également le cas de MySQL Shell.
En ce qui concerne les versions des softs:
- MySQL Server 5.7.17
- MySQL Router 2.1.2 rc
- MySQL Shell 1.0.8-rc
Docker 1.12.6 & Docker-compose 1.11.2. Docker est hors du cadre de cet article, mais tu trouveras à la fin de cet article le fichier docker-compose.yml utilisé.
Ah oui, j’ai failli oublier :
TL;DR
Tu as un cluster MySQL Group Replication configuré/administré manuellement et qui tourne. Tu peux l’administrer / le configurer avec MySQL Shell et gérer le routage des requêtes applicatives avec MySQL Router, ces 3 composants forment MySQL InnoDB Cluster.
MySQL Group Replication
Les étapes de déploiement du cluster Group Replication ont déjà été traitées ici.
Voici mes 3 instances MySQL 5.7
$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES f4c4aa2d3726 mysql:5.7 "docker-entrypoint.sh" About a minute ago Up About a minute 0.0.0.0:14002->3306/tcp mysql_node2 2304f0e44d4c mysql:5.7 "docker-entrypoint.sh" About a minute ago Up About a minute 0.0.0.0:14003->3306/tcp mysql_node3 fb6ae3c76a06 mysql:5.7 "docker-entrypoint.sh" About a minute ago Up About a minute 0.0.0.0:14001->3306/tcp mysql_node1
MySQL 5.7.17 plus précisément.
$ docker exec -it mysql_node1 mysql -uroot -p -e"SELECT version();" Enter password: +------------+ | version() | +------------+ | 5.7.17-log | +------------+
A quoi ressemble mon cluster Group Replication ?
Je peux avoir la description de l’architecture avec la table performance_schema.replication_group_members :
docker exec -it mysql_node1 mysql -uroot -p -e"SELECT * FROM performance_schema.replication_group_members\G" Enter password: *************************** 1. row *************************** CHANNEL_NAME: group_replication_applier MEMBER_ID: d300be14-1797-11e7-a22e-0242ac130002 MEMBER_HOST: mysql_node1 MEMBER_PORT: 3306 MEMBER_STATE: ONLINE *************************** 2. row *************************** CHANNEL_NAME: group_replication_applier MEMBER_ID: d3100569-1797-11e7-a278-0242ac130004 MEMBER_HOST: mysql_node2 MEMBER_PORT: 3306 MEMBER_STATE: ONLINE *************************** 3. row *************************** CHANNEL_NAME: group_replication_applier MEMBER_ID: d321b2db-1797-11e7-a16f-0242ac130003 MEMBER_HOST: mysql_node3 MEMBER_PORT: 3306 MEMBER_STATE: ONLINE
L’identification du noeud primaire peut se faire de la manière suivante :
$ docker exec -it mysql_node1 mysql -uroot -p -e"SELECT MEMBER_ID, MEMBER_HOST, MEMBER_STATE FROM performance_schema.replication_group_members INNER JOIN performance_schema.global_status ON (MEMBER_ID = VARIABLE_VALUE) WHERE VARIABLE_NAME='group_replication_primary_member'\G" Enter password: *************************** 1. row *************************** MEMBER_ID: 8b5bad71-1720-11e7-94f0-0242ac130002 MEMBER_HOST: mysql_node1 MEMBER_STATE: ONLINE
Le noeud mysql_node1 est donc en mode lecture écriture aka le noeud primaire (cette info nous sera utile pour la suite) et les 2 autres en lecture seule (super read only activé):
$ docker exec -it mysql_node2 mysql -uroot -p -e"CREATE SCHEMA gr_test" Enter password: ERROR 1290 (HY000) at line 1: The MySQL server is running with the --super-read-only option so it cannot execute this statement
$ docker exec -it mysql_node1 mysql -uroot -p -e"CREATE SCHEMA gr_test;" Enter password: $ docker exec -it mysql_node2 mysql -uroot -p -e"SHOW SCHEMAS;" Enter password: +--------------------+ | Database | +--------------------+ | information_schema | | gr_test | | mysql | | performance_schema | | sys | +--------------------+
On a donc un cluster MySQL Group Replication avec 3 nœuds online.
Le membre mysql_node1 est (pour le moment) le primaire, mysql_node2 et mysql_node3 sont les secondaires.
L’étape suivant consistera à gérer le cluster avec MySQL Shell.
MySQL Shell, interface pour gérer son cluster
On va se connecter avec le client MySQL Shell au noeud primaire :
$ # Connect to the primary node : mysql_node1 $ mysqlsh --uri=root@mysql_node1 Creating a Session to 'root@mysql_node1'super read only Enter password: Classic Session successfully established. No default schema selected. Welcome to MySQL Shell 1.0.8-rc Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type '\help', '\h' or '\?' for help, type '\quit' or '\q' to exit. Currently in JavaScript mode. Use \sql to switch to SQL mode and execute queries. mysql-js>
Ensuite, je « crée » mon cluster, en fait je vais rendre persistante les informations relatives à l’architecture du groupe dans mon cluster (plus d’info sur ce sujet plus bas).
mysql-js> var cluster=dba.createCluster('pocCluster', {adoptFromGR: true, ipWhitelist:'172.19.0.0/16'}) A new InnoDB cluster will be created on instance 'root@mysql_node1:3306'. Creating InnoDB cluster 'pocCluster' on 'root@mysql_node1:3306'... Adding Seed Instance... Cluster successfully created. Use Cluster.addInstance() to add MySQL instances. At least 3 instances are needed for the cluster to be able to withstand up to one server failure.
La méthode createCluster() prends comme paramètres, le nom du cluster (pocCluster) ainsi que des paramètres optionnels comme ipWhitelist (172.19.0.0/16)…
Pour plus de détails connecte toi à MySQL Shell (mysqlsh) et tape : dba.help(‘createCluster’)
$ mysqlsh *Welcome to MySQL Shell 1.0.8-rc Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type '\help', '\h' or '\?' for help, type '\quit' or '\q' to exit. Currently in JavaScript mode. Use \sql to switch to SQL mode and execute queries. mysql-js> dba.help('createCluster')
Vérifions l’état du cluster
mysql-js> /* Check cluster status */ mysql-js> cluster.status() { "clusterName": "pocCluster", "defaultReplicaSet": { "name": "default", "primary": "mysql_node1:3306", "status": "OK", "statusText": "Cluster is ONLINE and can tolerate up to ONE failure.", "topology": { "mysql_node1:3306": { "address": "mysql_node1:3306", "mode": "R/W", "readReplicas": {}, "role": "HA", "status": "ONLINE" }, "mysql_node2:3306": { "address": "mysql_node2:3306", "mode": "R/O", "readReplicas": {}, "role": "HA", "status": "ONLINE" }, "mysql_node3:3306": { "address": "mysql_node3:3306", "mode": "R/O", "readReplicas": {}, "role": "HA", "status": "ONLINE" } } } }
MySQL Shell nous confirme ce que nous savions déjà:
je m’auto cite; un grand (1m89) DBA à dit un jour :
« On a donc un cluster MySQL Group Replication déployé avec 3 nœuds online. Le membre mysql_node1 est (pour le moment) le primaire, mysql_node2 et mysql_node3 sont les secondaires. »
En zoomant dans les entrailles du groupe, on constate que la méthode createCluster() a écrit des données dans le cluster :
$ docker exec -it mysql_node1 mysql -uroot -p -e"SHOW SCHEMAS; SHOW TABLES IN mysql_innodb_cluster_metadata;" Enter password: +-------------------------------+ | Database | +-------------------------------+ | information_schema | | gr_test | | mysql | | mysql_innodb_cluster_metadata | | performance_schema | | sys | +-------------------------------+ +-----------------------------------------+ | Tables_in_mysql_innodb_cluster_metadata | +-----------------------------------------+ | clusters | | hosts | | instances | | replicasets | | routers | | schema_version | +-----------------------------------------+
Le schéma mysql_innodb_cluster_metadata a donc été créé pour contenir les informations relatives au cluster.
Le nom des tables est assez explicite :
hosts
$ docker exec -it mysql_node1 mysql -uroot -p -e" SELECT * FROM mysql_innodb_cluster_metadata.hosts\G" Enter password: *************************** 1. row *************************** host_id: 6 host_name: mysql_node1 ip_address: public_ip_address: NULL location: attributes: NULL admin_user_account: NULL *************************** 2. row *************************** host_id: 13 host_name: mysql_node2 ip_address: public_ip_address: NULL location: attributes: NULL admin_user_account: NULL *************************** 3. row *************************** host_id: 20 host_name: mysql_node3 ip_address: public_ip_address: NULL location: attributes: NULL admin_user_account: NULL
clusters
$ docker exec -it mysql_node1 mysql -uroot -p -e" SELECT * FROM mysql_innodb_cluster_metadata.clusters\G" Enter password: *************************** 1. row *************************** cluster_id: 6 cluster_name: pocCluster default_replicaset: 6 description: Default Cluster mysql_user_accounts: NULL options: {"adminType": "local"} attributes: {"default": true}
replicasets
$ docker exec -it mysql_node1 mysql -uroot -p -e" SELECT * FROM mysql_innodb_cluster_metadata.replicasets\G" Enter password: *************************** 1. row *************************** replicaset_id: 6 cluster_id: 6 replicaset_type: gr topology_type: pm replicaset_name: default active: 1 attributes: {"adopted": "true", "group_replication_group_name": "4e0f05b7-d9d0-11e6-87cf-002710cccc64"} description: NULL
instances
$ docker exec -it mysql_node1 mysql -uroot -p -e" SELECT * FROM mysql_innodb_cluster_metadata.instances\G" Enter password: *************************** 1. row *************************** instance_id: 6 host_id: 6 replicaset_id: 6 mysql_server_uuid: d300be14-1797-11e7-a22e-0242ac130002 instance_name: mysql_node1:3306 role: HA weight: NULL addresses: {"mysqlX": "mysql_node1:33060", "grLocal": "mysql_node1:4999", "mysqlClassic": "mysql_node1:3306"} attributes: NULL version_token: NULL description: NULL *************************** 2. row *************************** instance_id: 13 host_id: 13 replicaset_id: 6 mysql_server_uuid: d3100569-1797-11e7-a278-0242ac130004 instance_name: mysql_node2:3306 role: HA weight: NULL addresses: {"mysqlX": "mysql_node2:33060", "grLocal": "mysql_node2:4999", "mysqlClassic": "mysql_node2:3306"} attributes: NULL version_token: NULL description: NULL *************************** 3. row *************************** instance_id: 20 host_id: 20 replicaset_id: 6 mysql_server_uuid: d321b2db-1797-11e7-a16f-0242ac130003 instance_name: mysql_node3:3306 role: HA weight: NULL addresses: {"mysqlX": "mysql_node3:33060", "grLocal": "mysql_node3:4999", "mysqlClassic": "mysql_node3:3306"} attributes: NULL version_token: NULL description: NULL
Déploiement de MySQL Router
Le déploiement du router est trivial, il faut pour commencer le bootstrapper au cluster, c’est à dire le lier au cluster en le connectant aux méta-données :
$ mysqlrouter --bootstrap root@mysql_node1:3306 --directory routerDocker --name routerDocker [sudo] password for daz: Please enter MySQL password for root: Bootstrapping MySQL Router instance at /home/daz/routerDocker... MySQL Router 'routerDocker' has now been configured for the InnoDB cluster 'pocCluster'. The following connection information can be used to connect to the cluster. Classic MySQL protocol connections to cluster 'pocCluster': - Read/Write Connections: localhost:6446 - Read/Only Connections: localhost:6447 X protocol connections to cluster 'pocCluster': - Read/Write Connections: localhost:64460 - Read/Only Connections: localhost:64470
Les paramètres directory et name sont optionnels.
Lancer MySQL Router :
$ mysqlrouter -c ~/routerDocker/mysqlrouter.conf &
L’application doit se connecter (par défaut) au port 6446 (écritures et lectures vers le noeud primaire). En cas de besoin de read scalability, les lectures peuvent être dirigées vers le port 6447.
Inspectons de nouveau les méta données :
routers
$ docker exec -it mysql_node1 mysql -uroot -p -e" SELECT * FROM mysql_innodb_cluster_metadata.routers\G" Enter password: *************************** 1. row *************************** router_id: 6 router_name: routerDocker host_id: 27 attributes: NULL
hosts
$ docker exec -it mysql_node1 mysql -uroot -p -e" SELECT * FROM mysql_innodb_cluster_metadata.hosts\G" Enter password: *************************** 1. row *************************** host_id: 6 host_name: mysql_node1 ip_address: public_ip_address: NULL location: attributes: NULL admin_user_account: NULL *************************** 2. row *************************** host_id: 13 host_name: mysql_node2 ip_address: public_ip_address: NULL location: attributes: NULL admin_user_account: NULL *************************** 3. row *************************** host_id: 20 host_name: mysql_node3 ip_address: public_ip_address: NULL location: attributes: NULL admin_user_account: NULL *************************** 4. row *************************** host_id: 27 host_name: ip_address: NULL public_ip_address: NULL location: attributes: {"registeredFrom": "mysql-router"} admin_user_account: NULL
Voilà, mon cluster Group Replication paramétré « à la main » fait maintenant partie intégrante de mon InnoDB Cluster, je peux donc l’administrer avec MySQL Shell et je peux vous assurer que c’est vraiment pratique.
Mais ceci est une autre histoire et fera l’objet d’un autre article 🙂
Annexe
Le fichier docker-compose est le suivant :
version: '2' services: node1: container_name: mysql_node1 image: "mysql:5.7" volumes: - ~/Documents/Docker/confdir/mysql1:/etc/mysql/conf.d ports: - "14001:3306" environment: - MYSQL_ROOT_PASSWORD=root networks: app_net: ipv4_address: 172.19.0.2 node2: container_name: mysql_node2 image: "mysql:5.7" volumes: - ~/Documents/Docker/confdir/mysql2:/etc/mysql/conf.d ports: - "14002:3306" environment: - MYSQL_ROOT_PASSWORD=root networks: app_net: ipv4_address: 172.19.0.4 node3: container_name: mysql_node3 image: "mysql:5.7" volumes: - ~/Documents/Docker/confdir/mysql3:/etc/mysql/conf.d ports: - "14003:3306" environment: - MYSQL_ROOT_PASSWORD=root networks: app_net: ipv4_address: 172.19.0.3 networks: app_net: driver: bridge ipam: driver: default config: - subnet: 172.19.0.0/24
Thanks for using MySQL!
Architecte Solution Cloud chez Oracle
MySQL Geek, Architecte, DBA, Consultant, Formateur, Auteur, Blogueur et Conférencier.
—–
Blog: www.dasini.net/blog/en/
Twitter: https://twitter.com/freshdaz
SlideShare: www.slideshare.net/freshdaz
Youtube: https://www.youtube.com/channel/UC12TulyJsJZHoCmby3Nm3WQ
—–
Bonjour Olivier,
Tout d’abord merci pour ce post très bien détaillé !
Je viens de mettre en place un Group Replication, et ça fonctionne très bien.
Par contre, je n’arrive pas à le transformer en un InnoDB cluster… ma commande de createCluster (avec option adoptFromGR )échoue :
Dba.createCluster: Dba.checkInstanceConfiguration: The instance ‘root@localhost:3331’ is already part of a Replication Group (RuntimeError)
Effectivement, l’instance fait partie d’un Group Replication mais il me semblait qu’il fallait que ce soit justement le cas pour ajouter l’option « adoptFromGR » ?
Je n’ai rien dans le mysql.err, j’avoue que je sèche un peu. Aurais tu une idée d’expert sur la question ?
J’aurais bien aimé administrer mon cluster avec MySQL Shell, je trouve les commandes bien pratiques…
J’oubliais, ma config est la suivante : MySQL 5.7.19 sur SLES12sp1 ; Group replication sur 3 noeuds avec un seul maitre ; j’effectue la création du cluster en me connectant sur le noeud maitre de mon archi.
D’avance merci pour ton retour.
Cordialement
Karl
Bonjour Olivier,
Je reviens vers toi avec une solution à mon problème… en fait, j’avais démarré le Group Replication avant de lancer la creation du Cluster.
Ceci bloque la création, il faut que le Group Replication soit configuré mais non actif pour que le cluster soit créé.
Encore merci pour tes posts !
Cordialement
Karl
Bonjour,
Je ne comprends pas pourquoi il faut (re) »créé » un cluster avec MySQL Shell alors que le but de l’article est de gérer un cluster déjà existant.
Question complémentaire, si je suis votre méthode, est-ce que je vais « casser » le cluster déjà existant.
Pour approfondir, mise à part l’automatisation facilitée, quelles sont les différences à gérer mon cluster via MySQL Shell plutôt que manuellement (comme ici http://dasini.net/blog/2016/11/08/deployer-un-cluster-mysql-group-replication/) ?
Il y a sûrement des subtilités qui m’échappe entre « group replication » et « innodb cluster ». Pour moi, innodb cluster c’est la combinaison de group_replication et mysql router.
Encore merci pour vos articles.
Bonjour,
MySQL ne me semble vraiment pas prêt pour un usage en production. Je n’arrive pas à suivre ce tutoriel alors que j’ai quelque chose de très simple.
Point de départ: un group_replication créé à la main en suivant votre article « deployer-un-cluster-mysql-group-replication ».
Je rencontre 2 erreurs (ou bugs)
– Erreur de port
Si l’URI rentré ne contient pas le port, la fonction createCluster renvoie une erreur
——————–
[root@mysql-test-01 ~]# mysqlsh –uri=mysqlshell_user@mysql-test-02
Creating a Session to ‘mysqlshell_user@mysql-test-02’
Enter password:
Your MySQL connection id is 41
Server version: 5.7.19-log MySQL Community Server (GPL)
No default schema selected; type \use to set one.
MySQL Shell 1.0.10
Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type ‘\help’ or ‘\?’ for help; ‘\quit’ to exit.
Currently in JavaScript mode. Use \sql to switch to SQL mode and execute queries.
mysql-js> var cluster=dba.createCluster(‘testCluster’, {adoptFromGR: true})
A new InnoDB cluster will be created on instance ‘mysqlshell_user@mysql-test-02:’.
Dba.createCluster: Missing port number (ArgumentError)
——————–
le contre exemple est plus bas, l’uri utilisé comprend cette fois-ci le port. Mais l’erreur cette fois-ci.
– Erreur, « the instance is already part of group replication » alors que j’ai bien spécifié l’option « adoptFromGR »
——————–
[root@mysql-test-01 ~]# mysqlsh –uri=mysqlshell_user@mysql-test-02:3306
Creating a Session to ‘mysqlshell_user@mysql-test-02:3306’
Enter password:
Your MySQL connection id is 43
Server version: 5.7.19-log MySQL Community Server (GPL)
No default schema selected; type \use to set one.
MySQL Shell 1.0.10
Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type ‘\help’ or ‘\?’ for help; ‘\quit’ to exit.
Currently in JavaScript mode. Use \sql to switch to SQL mode and execute queries.
mysql-js>
mysql-js> A new InnoDB cluster will be created on instance ‘mysqlshell_user@mysql-test-02:’.
SyntaxError: Unexpected token new
mysql-js> var cluster=dba.createCluster(‘testCluster’, {adoptFromGR: true})
A new InnoDB cluster will be created on instance ‘mysqlshell_user@mysql-test-02:3306’.
Dba.createCluster: Dba.checkInstanceConfiguration: The instance ‘mysqlshell_user@mysql-test-02:3306’ is already part of a Replication Group (RuntimeError)
——————–
Je ne peux donc pas convertir mon « group_replication » déjà existant en un « innodb cluster », je ne comprends donc pas comment cela peut fonctionner chez vous.
Info:
centos 7 à jour
mysql-community-server 5.7.19
mysql-shell 1.0.10
Cela semble être un bug
https://bugs.mysql.com/bug.php?id=87599
J’ai confirmé le bug en expliquant mon setup.
Cdt,
[…] MySQL Group Replication, comprendre et tester MySQL InnoDB Cluster ainsi que comment gérer aisément un cluster Group Replication déja déployé avec MySQL […]