Logo de Béjean Développement

Installer un cluster MySQL 8.0 pour Magento 2

MySQL InnoDB Cluster est une des solutions de haute disponibilité pour les bases de données MySQL qui utilise la technologie de réplication de données pour assurer la redondance des données et la disponibilité continue des services.

MySQL InnoDB Cluster est composé de plusieurs composants :

  • Les nœuds
  • Le groupe de réplication (MySQL Group Replication)
  • L'orchestrateur (MySQL Shell & Orchestration Tooling)
  • Le routeur (MySQL Router)

Installation de MySQL

Pour déployer un cluster, il faut au minimum 3 nœuds. Utilisant Proxmox pour la gestion de mes machines virtuelles, je vous invite à suivre mon article sur la création de templates avec Cloud-init. Cet article vous permettra de déployer rapidement des machines virtuelles.

Une fois les machines virtuelles déployées, il est nécessaire de télécharger le paquet .deb depuis le site MySQL Community Downloads, puis de l'installer.

wget https://dev.mysql.com/get/mysql-apt-config_0.8.25-1_all.deb
apt install ./mysql-apt-config_0.8.25-1_all.deb

Lors de l'installation, vous devrez choisir la version de MySQL que vous souhaitez installer. Pour la mise en place du cluster, j'ai choisi la version mysql-cluster-8.0. Une fois que vous avez sélectionné la version, vous pouvez valider votre choix et lancer l'installation de MySQL en lançant la commande suivante :

apt-get update && apt-get install mysql-server

Suivez les instructions selon vos envies. N'hésitez pas à renseigner un mot de passe root assez complexe. Vérifier la version installée en lançant la commande : mysql --version. Vous devriez avoir, en retour, ceci :

mysql  Ver 8.0.33-cluster for Linux on x86_64 (MySQL Cluster Community Server - GPL)

Installation de MySQL Shell

Afin de configurer notre cluster, nous allons utiliser l'outil mysqlsh qui est fourni avec MySQL. Cet outil permet de configurer et de gérer le cluster. Pour ce faire, je déploie, à partir d'un template, une machine virtuelle avec Ubuntu 22.04 et j'installe le paquet mysql-shell :

wget https://dev.mysql.com/get/mysql-apt-config_0.8.25-1_all.deb
apt install ./mysql-apt-config_0.8.25-1_all.deb

Lors de l'installation, vous devrez désactiver la version de MySQL. Puis vous pouvez valider votre choix et lancer l'installation de MySQL Shell en lançant la commande suivante :

apt-get update && apt-get install mysql-shell

Vérifier la version installée en lançant la commande : mysqlsh --version. Vous devriez avoir, en retour, ceci :

mysqlsh   Ver 8.0.33 for Linux on x86_64 - for MySQL 8.0.33 (MySQL Community Server (GPL))

Configuration des serveurs MySQL

Avant de procéder la mise à en place du cluster, nous avons besoin de configurer chaque serveur MySQL afin qu'il autorise les connexions entrantes, pour cela, nous allons éditer le fichier de configuration de MySQL :

nano /etc/mysql/mysql.conf.d/mysqld.cnf

Vous trouverez, ci-dessous, les modifications que j'ai apportées au fichier de configuration :

bind-address = *
log-bin-trust-function-creators = 1
binlog_transaction_dependency_tracking = WRITESET
enforce_gtid_consistency = ON
gtid_mode = ON
server_id = 1

Voici le détail de mon fichier de configuration :

  • bind-address : ce paramètre permet d'indiquer à MySQL l'interface réseau ou l'adresse IP qu'il doit écouter, lien vers la documentation.
  • log-bin-trust-function-creators : permet d'autoriser la création ou la modification des fonctions stockées. Par défaut, ce paramètre est à 0. Il faut donc le passer à 1 pour éviter des erreurs. Lien vers la documentation de MySQL.
  • binlog_transaction_dependency_tracking : permet d'enregistrer chaque transaction dans un fichier afin d'aider les réplicas du cluster à déterminer les transactions qui devront être exécutées. Lien vers la documentation de MySQL.
  • enforce_gtid_consistency : ce paramètre permet de garantir que toutes les transactions sont exécutées de manière cohérente dans le but de limiter les risques de conflits et de garantir une réplication fiable des données. Lien vers la documentation de MySQL.
  • gtid_mode : ce paramètre permet d'activer le mode GTID. Lien vers la documentation de MySQL.
  • server_id : permet d'identifier chaque serveur du cluster. La valeur doit être unique. La valeur par défaut est 1. Lien vers la documentation de MySQL.

Une fois modifié, sur chaque serveur, il faut lancer le service et vérifier que le service est bien actif :

systemctl restart mysql && systemctl status mysql

Avant de mettre en place le cluster, nous allons créer un nouvel utilisateur, sur chaque serveur MySQL, qui aura la possibilité de se connecter depuis notre machine MySQL Shell. Commençons par se connecter au serveur MySQL :

mysql -uroot -p

Renseigner ensuite le mot de passe que vous avez défini lors de l'installation de MySQL. Puis lancer la commande ci-dessous an ayant, au préalable, modifier les valeurs de l'utilisateur ainsi que le mot de passe :

CREATE USER 'remote'@'%' IDENTIFIED BY 'password';GRANT ALL ON *.* TO 'remote'@'%' WITH GRANT OPTION;FLUSH PRIVILEGES;

Modification du fichier hosts

Lors de la mise en place du cluster, j'ai été confronté à cette erreur : [GCS] There is no local IP address matching the one configured for the local node....

Pour cela, j'ai dû faire des modifications dans le fichier hosts de chaque serveur MySQL. Pour ce faire, j'ai modifié le fichier /etc/cloud/templates/hosts.debian.tmplcar j'ai déployé mes serveurs avec une image Ubuntu Cloud.

Dans ce fichier, j'ai ajouté la ligne <ip-du-serveur> {{fqdn}} {{hostname}} après le bloc :

127.0.1.1 {{fqdn}} {{hostname}}
127.0.0.1 localhost

Le résultat est le suivant :

## template:jinja
{#
This file (/etc/cloud/templates/hosts.debian.tmpl) is only utilized
if enabled in cloud-config.  Specifically, in order to enable it
you need to add the following to config:
   manage_etc_hosts: True
-#}
# Your system has configured 'manage_etc_hosts' as True.
# As a result, if you wish for changes to this file to persist
# then you will need to either
# a.) make changes to the master file in /etc/cloud/templates/hosts.debian.tmpl
# b.) change or remove the value of 'manage_etc_hosts' in
#     /etc/cloud/cloud.cfg or cloud-config from user-data
#
{# The value '{{hostname}}' will be replaced with the local-hostname -#}
127.0.1.1 {{fqdn}} {{hostname}}
127.0.0.1 localhost
<ip-du-serveur> {{fqdn}} {{hostname}}

# The following lines are desirable for IPv6 capable hosts
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters

La modification doit être réalisée sur chaque serveur MySQL de la grappe InnoDB. Avant d'enregistrer, n'oubliez pas de préciser l'adresse IP du serveur MySQL où vous réalisez la modification.

Configuration du cluster

Désormais, nous avons déployé et configuré nos serveurs, nous allons créer notre cluster MySQL InnoDB. Depuis la machine virtuelle où nous avons installé MySQL Shell, nous allons lancer la commande suivante sur le premier serveur MySQL :

mysqlsh remote@<ip>:3306

Vous pouvez également lancer la commande : mysqlsh, puis la commande shell.connect("remote@<ip>:3306").

Une fois connecté, vous pouvez lancer la création du cluster avec la commande ci-dessous, en ayant modifier le nom du cluster avant d'exécuter la commande :

var cluster = dba.createCluster('cluster-name')

Si tout se passe bien, vous devriez avoir le résultat suivant :

A new InnoDB Cluster will be created on instance '<nom-du-serveur>:3306'.

Validating instance configuration at <ip-du-serveur>:3306...

This instance reports its own address as <nom-du-serveur>:3306

Instance configuration is suitable.
NOTE: Group Replication will communicate with other members using '<nom-du-serveur>:3306'. Use the localAddress option to override.

* Checking connectivity and SSL configuration...

Creating InnoDB Cluster 'c000-production' on '<nom-du-serveur>: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.

Puis, tout en restant sur le serveur "principal", lancer la commande :

cluster.addInstance('remote@<ip-du-second-serveur>:3306', {recoveryMethod: 'clone'});

En précisant, dans la commande la valeur de recoveryMethod, le second serveur sera en lecture seule (R/O). C'est la méthode de clonage automatique (clone provisioning), qui est la plus sûre et la plus pratique. La méthode de clonage automatique remplacera complètement l'état de l'instance cible avec une snapshot physique d'un autre nœud existant du cluster. Cela garantit que l'état de l'instance cible est cohérent avec celui des autres nœuds du cluster.

Le résultat de la commande doit être :

Clone based recovery selected through the recoveryMethod option

Validating instance configuration at <ip-du-serveur>:3306...

This instance reports its own address as <nom-du-serveur>:3306

Instance configuration is suitable.
NOTE: Group Replication will communicate with other members using '<ip-du-serveur>:3306'. Use the localAddress option to override.

* Checking connectivity and SSL configuration...
A new instance will be added to the InnoDB Cluster. Depending on the amount of
data on the cluster this might take from a few seconds to several hours.

Adding instance to the cluster...

Monitoring recovery process of the new cluster member. Press ^C to stop monitoring and let it continue in background.
Clone based state recovery is now in progress.

NOTE: A server restart is expected to happen as part of the clone process. If the
server does not support the RESTART command or does not come back after a
while, you may need to manually start it back.

* Waiting for clone to finish...
NOTE: <nom-du-serveur>:3306 is being cloned from <nom-du-serveur>:3306
** Stage DROP DATA: Completed
** Clone Transfer  
    FILE COPY  ############################################################  100%  Completed
    PAGE COPY  ############################################################  100%  Completed
    REDO COPY  ############################################################  100%  Completed

NOTE: <nom-du-serveur>:3306 is shutting down...

* Waiting for server restart... ready 
* <nom-du-serveur>:3306 has restarted, waiting for clone to finish...
** Stage RESTART: Completed
* Clone process has finished: 73.65 MB transferred in about 1 second (~73.65 MB/s)

Il faut faire de même sur le troisième et dernier serveur MySQL.

Vérifier la mise en place du cluster

Pour vérifier que les serveurs sont bien rattachés au cluster, nous allons lancer la commande suivante, dans MySQL Shell :

cluster.status();

Si vous avez perdu l'objet cluster, vous pouvez le retrouver en lançant : cluster = dba.getCluster();.

Le résultat de la commande cluster.status(); doit être :

{
    "clusterName": "<nom-du-cluster>", 
    "defaultReplicaSet": {
        "name": "default", 
        "primary": "<nom-du-serveur-primaire>:3306", 
        "ssl": "REQUIRED", 
        "status": "OK", 
        "statusText": "Cluster is ONLINE and can tolerate up to ONE failure.", 
        "topology": {
            "<nom-du-premier-serveur>:3306": {
                "address": "<adresse-du-premier-serveur>:3306",
                "memberRole": "PRIMARY",
                "mode": "R/W",
                "readReplicas": {},
                "replicationLag": "applier_queue_applied",
                "role": "HA",
                "status": "ONLINE",
                "version": "8.0.33"
            }, 
            "<nom-du-second-serveur>:3306": {
                "address": "<adresse-du-second-serveur>:3306", 
                "memberRole": "SECONDARY", 
                "mode": "R/O", 
                "readReplicas": {}, 
                "replicationLag": "applier_queue_applied", 
                "role": "HA", 
                "status": "ONLINE", 
                "version": "8.0.33"
            }, 
            "<nom-du-troisieme-serveur>:3306": {
                "address": "<adresse-du-troisieme-serveur>:3306",
                "memberRole": "SECONDARY",
                "mode": "R/O",
                "readReplicas": {},
                "replicationLag": "applier_queue_applied",
                "role": "HA",
                "status": "ONLINE",
                "version": "8.0.33"
            }
        }, 
        "topologyMode": "Single-Primary"
    }, 
    "groupInformationSourceMember": "<nom-du-serveur-primaire>:3306"
}

Désormais, lors d'une action (création, suppression, mise à jour...) sur le serveur MySQL principal, d'un utilisateur, d'une base de données, d'une table... sera répercutée sur chaque serveur secondaire.

Vous pouvez vérifier cela en vous connectant à chaque serveur afin de vérifier si la donnée est bien répercutée. Vous pouvez également tester la mise à jour de donnée depuis un des serveurs secondaires. Les requêtes d'écriture seront refusées.

Aussi, si un des serveurs tombe en panne, le cluster va automatiquement basculer le serveur primaire sur un des 2 serveurs restants. Il ne faut pas oublier qu'un cluster de 3 nœuds a une tolérance d'un seul nœud.

Déploiement de MySQL Router

Maintenant que nous possédons un cluster MySQL, il est nécessaire de déployer MySQL Router, car dans la vie d'un cluster le nœud principal peut être amené à changer. Il faut donc déployer un routeur pour nous permettre de toujours écrire sur le nœud primaire, celui qui a les droits d'écriture. MySQL Router va également nous permettre de répartir la charge entre les serveurs.

Pour les besoins de l'article, j'ai déployé MySQL Router dans une nouvelle machine virtuelle. MySQL recommande d'installer MySQL Router sur le même hôte que l'application pour des raisons de droits sur le socket, pour réduire la latence... Cela ne change en rien aux étapes à suivre pour l'installation de MySQL Router.

wget https://dev.mysql.com/get/mysql-apt-config_0.8.25-1_all.deb
apt install ./mysql-apt-config_0.8.25-1_all.deb

Lors de l'installation, vous devrez désactiver la version de MySQL. Puis vous pouvez valider votre choix et lancer l'installation de MySQL Shell en lançant la commande suivante :

apt-get update && apt-get install mysql-router

Vérifier la version installée en lançant la commande : mysqlrouter --version. Vous devriez avoir, en retour, ceci :

MySQL Router  Ver 8.0.33 for Linux on x86_64 (MySQL Community - GPL)

Une fois installé, il faut lancer la commande ci-dessous qui permettra de créer la connexion entre MySQL Router et le cluster MySQL :

mysqlrouter --bootstrap remote@<ip-serveur-mysql> --user=mysqlrouter --disable-rest

Cette commande va créer un fichier de configuration contenant les informations nécessaires au bon fonctionnement de notre routeur. Il est nécessaire de spécifier le nom d'utilisateur que nous avons créé précédemment. L'option --user permet de spécifier l'utilisateur mysqlrouteur de la machine où le service MySQL Router est en cours d'exécution. L'option --disable-rest permet de désactiver l'API REST et le serveur HTTPS de MySQL Router.

Une fois exécutée, la commande vous retournera ceci :

# Reconfiguring system MySQL Router instance...

- Fetching password for current account (<current-account>) from keyring
- Creating account(s) (only those that are needed, if any)
- Using existing certificates from the '/var/lib/mysqlrouter' directory
- Verifying account (using it to run SQL queries that would be run by Router)
- Storing account in keyring
- Adjusting permissions of generated files
- Creating configuration /etc/mysqlrouter/mysqlrouter.conf

# MySQL Router configured for the InnoDB Cluster '<nom-du-cluster>'

After this MySQL Router has been started with the generated configuration

    $ /etc/init.d/mysqlrouter restart
or
    $ systemctl start mysqlrouter
or
    $ mysqlrouter -c /etc/mysqlrouter/mysqlrouter.conf

InnoDB Cluster '<nom-du-cluster>' can be reached by connecting to:

## MySQL Classic protocol

- Read/Write Connections: localhost:6446
- Read/Only Connections:  localhost:6447

## MySQL X protocol

- Read/Write Connections: localhost:6448
- Read/Only Connections:  localhost:6449

Pour vérifier que la connexion est correcte, je vous invite à exécuter la commande cluster.listRouters(); dans MySQL Shell, le résultat devrait être le suivant :

{
    "clusterName": "<nom-du-cluster>", 
    "routers": {
        "c000-u2204lts-mysql-router-1::system": {
            "hostname": "<hostname-du-routeur>", 
            "lastCheckIn": "2023-05-08 12:40:58", 
            "roPort": "6447", 
            "roXPort": "6449", 
            "rwPort": "6446", 
            "rwXPort": "6448", 
            "version": "8.0.33"
        }
    }
}

Vous pouvez désormais vous connecter à votre cluster MySQL en passant par le routeur.

Création de la base de données

Pour créer la base de données, lancer la commande mysql --host=127.0.0.1 --port=6446 --user=remote -p et renseigner le mot de passe du compte remote précédemment créé.

Ayant installé MySQL Router uniquement, il vous sera sûrement demandé d'installer un client MySQL :

apt install mysql-client-core-8.0

Puis lancer les commandes suivantes pour créer la base de données magento et l'utilisateur magento en spécifiant bien de limité la connexion au routeur MySQL :

CREATE DATABASE IF NOT EXISTS magento;CREATE USER 'magento'@'<ip-du-routeur>' IDENTIFIED BY '<mot-de-passe-complexe>';GRANT ALL ON magento.* TO 'magento'@'<ip-du-routeur>';FLUSH PRIVILEGES;

Pensez à remplacer les valeurs <ip-du-routeur> et <mot-de-passe-complexe>. Vous pouvez désormais vous connecter à votre base de données MySQL en passant par le MySQL Router.

Autres actions possibles avec MySQL Shell

Changer le serveur primaire

Si vous souhaitez, vous pouvez changer de serveur primaire, il faut lancer, depuis une connexion sur le serveur depuis le MySQL Shell, la commande :

cluster.setPrimaryInstance('<adresse-du-nouveau-serveur-primaire>:3306');

Si vous avez perdu l'objet cluster, vous pouvez le retrouver en lançant : cluster = dba.getCluster();.

Supprimer un nœud du cluster

Dans le cas où vous souhaiteriez supprimer un nœud du cluster, il faut lancer, depuis une connexion sur le serveur principale depuis le MySQL Shell, la commande :

cluster.removeInstance('remote@<adresse-du-serveur>:3306');

Si vous avez perdu l'objet cluster, vous pouvez le retrouver en lançant : cluster = dba.getCluster();.