Blog

Installation de Percona Cluster XtraDB

Écrit le 23 04 2013 par Kévin MET _

Dans ce tutoriel nous allons voir comment mettre en place un Cluster Mysql Percona. Nous allons monter ce cluster sur 3 nœuds. Pour l'exemple les 3 nœuds auront ces caractéristiques :

  • node1.domaine.fr 192.168.1.1
  • node2.domaine.fr 192.168.1.2
  • node3.domaine.fr 192.168.1.3

Principes de base

Tout d'abord nous allons commencer par faire un peu de théorie. Ce qu'on appelle un Cluster Percona XtraDB n'est en fait pas un réel cluster dans le sens ou ce n'est qu'une simple réplication multi-master synchrone. Cela est différent de MySQL Cluster qui repartit les données sur les différents nœuds. Pour autant j'ai obtenu de bien meilleurs performance avec Percona et j'aimerais bien avoir vos retour à ce sujet dans les commentaires.

Un cluster Percona consiste donc en une réplication multi-master entre des serveur Percona grâce à un système de réplication utilisant Galera. Cela implique que l'on peut transformer une installation de Percona serveur traditionnelle en un cluster. Chaque nœud contient donc toutes les données et un cluster peut être composé de 2 nœuds au minimum même si ce n'est pas une configuration recommandée. Ce n'est pas recommandé car dans cette situation les risques sont beaucoup plus grands pour que le cluster tombe en split-brain.

Installation

Pour commencer, nous allons ajouter le dépôt de Percona sur nos trois nœuds :


# gpg --keyserver  hkp://keys.gnupg.net --recv-keys 1C4CBDCDCD2EFD2A
# gpg -a --export CD2EFD2A | apt-key add -

# vim /etc/apt/sources.list

# deb http://repo.percona.com/apt squeze main
# deb-src http://repo.percona.com/apt squeeze main

aptitude update

Nous pouvons passer à l'installation des paquets :


aptitude install percona-xtradb-cluster-common-5.5 percona-xtradb-cluster-client-5.5 percona-xtrabackup percona-xtradb-cluster-galera-2.x percona-xtradb-cluster-server-5.5

Il faut ensuite arrêter MySQL car nous allons devoir en modifier la configuration


# /etc/init.d/mysql stop

Une fois que cela est fait nous allons éditer le fichier /etc/hosts afin de renseigner les différents hostname de nos machines :


# vim /etc/hosts

192.168.1.1    node1.mnt-tech.fr node1
192.168.1.2    node2.mnt-tech.fr node2
192.168.1.3    node3.mnt-tech.fr node3

Pour éviter un problème de désynchronisation sur les nœuds il est très important qu'il soit synchronisé sur la même heure. Nous installons donc ntp.


aptitude install ntp

Configuration

Nous allons commencer par configurer le master. Pour cela, il va nous falloir créer le fichier /etc/mysql/my.cnf qui n'existe pas et sera donc pris en compte au prochain démarrage de Percona


vim /etc/mysql/my.cnf

Voici un fichier de configuration exemple pour un master :


[client]
port = 3306
socket = /var/run/mysqld/mysqld.sock
 
 
[mysqld]
port = 3306
datadir = /var/lib/mysql
socket = /var/run/mysqld/mysqld.sock
max_connections = 500
transaction-isolation = 'READ-COMMITTED'
 
# caching and optimisations
join_buffer_size = 128
table_cache = 96
table_open_cache = 64
thread_cache_size = 128
thread_stack = 256K
tmp_table_size = 256M
max_allowed_packet = 500M
read_buffer_size = 128K
 
slow-query-log-file = /var/log/mysql/slow-queries.log # file must first be created and owned by the MySQL user
long_query_time = 5
 
skip-external-locking
skip-name-resolve
 
#========#
# InnoDB #
#========#
 
innodb_flush_method = ALL_O_DIRECT
innodb_buffer_pool_size = 750M # it should be ~70-80% of the available RAM.
innodb_log_buffer_size = 8M
innodb_log_file_size = 50M
innodb_support_xa = 0 
innodb_import_table_from_xtrabackup = 1 # allows restoring a single table
innodb_flush_log_at_trx_commit = 0 # speeds things up, can lose 1 second worth of transactions if MySQL crashes.
innodb_doublewrite = 1 # ensures an incremental state transfer will be tried if possible
 
innodb_file_per_table
 
#================================#
# Galera synchronous replication #
#================================#
 
binlog_format = row
innodb_locks_unsafe_for_binlog = 1
server-id = 1 # change on each node! - not strictly required for Galera, but it's safer to have a unique id on each node in case normal replication is also used
innodb_autoinc_lock_mode = 2
query_cache_type = 0
query_cache_size = 0
default_storage_engine = InnoDB
wsrep_cluster_name = "Percona-XtraDB-Cluster"
wsrep_node_name = "node1" # change this on each node!
wsrep_provider = /usr/lib/libgalera_smm.so
wsrep_cluster_address = "gcomm://" # set to "gcomm://"" to reinitialise (reset) a node; otherwise ensure it is pointing to a valid node
wsrep_slave_threads = 12 # recommended: 4 slave thread per core
wsrep_retry_autocommit = 2 # how many times to retry deadlocked autocommits
wsrep_convert_LOCK_to_trx = 0 # convert locking sessions into transactions
wsrep_certify_nonPK = 1 # Generate fake primary keys for non-PK tables (required for multi-master and parallel applying operation)
  
# rsync or xtrabackup; remember to create the user 'mysql'@'localhost' on each node, with all privileges for the xtrabackup-based SST to work
wsrep_sst_method = xtrabackup
wsrep_sst_auth = mysql:password
 
[mysqldump]
quick
max_allowed_packet = 16M
 
[mysql]
no-auto-rehash
# Remove the next comment character if you are not familiar with SQL
#safe-updates
 
[myisamchk]
key_buffer_size = 20M
sort_buffer_size = 20M
read_buffer = 2M
write_buffer = 2M
 
[mysqlhotcopy]
interactive-timeout

Voici une liste des paramètres les plus importants et leur signification

  • max-connections : Ce paramètre définit le maximum de connexions possible simultanément, il dépend beaucoup de la façon dont votre applicatif utilise MySQL.
  • transaction-isolation : Cela définit le niveau d'isolation des transactions InnoDB. Par défaut MySQL est à REPEATABLE-READ mais il préférable de le passer à READ-COMMITTED car cela peut augmenter les performances.
  • skip-name-resolve : Cela permet au serveur de ne pas faire de requêtes DNS. Cela permet une augmentation sensible de performance.
  • innodb_support_xa : Ce paramètre est obligatoire pour l'utilisation de Galera.
  • innodb_import_table_from_xtrabackup : Cela permet de restaurer une seule table même à chaud.
  • innodb_file_per_table : Cela permet de mieux utiliser le disque en stockant plusieurs fichiers plutot qu'un seule énorme fichier et cela permet également de ne restaurer qu'une seule table lorsque que l'on a activé innodb_import_table_from_xtrabackup.

Voici une liste de paramètres concernant la réplication via Galera

  • server-id : Cet identifiant doit être unique sur chaque nœud. Vous pouvez utiliser ce que vous voulez.
  • wsrep_cluster_name : Cet identifiant doit être le même sur tous vos nœuds.
  • wsrep_node_name : Ce paramètre doit également être différent sur chacun de vos nœud.
  • wsrep_slave_threads : Le paramètre recommandé est de mettre 4 threads par core.
  • wsrep_cluster_address : Ce paramètre est très important car il détermine le rôle d'un nœud dans le cluster. Sur le master il doit donc être gcomm:// lorsque l'on installe le cluster. Cela permet de déterminer que ce nœud sera le master sur lequel les autres nœuds vont venir se répliquer. Une fois le cluster en place il faudra modifier ce paramètre à chaud afin de désigner les autres nœuds du cluster (ex : gcomm://node1,node2,...) Cela indique que le master pourra devenir slave avec les autres nœuds et vice-versa.
  • wsrep_sst_method : Ce paramètre détermine la méthode de synchronisation utilisée lorsqu'un nœud joint le cluster. Il existe 3 possibilités : mysqldump, rsync et xtrabackup. La méthode xtrabackup permet de continuer à utiliser le donneur lors de la synchronisation alors que les méthodes rsync et mysqldump empêchent le donneur et le receveur d'être utilisés.

Pour démarrer notre master il va donc nous falloir mettre le paramètre wsrep_cluster_address à gcomm://. Une fois cela fait nous pouvons démarrer mysql sur le master.

Vous aurez surement besoin de supprimer les fichiers ib_logfile avant de démarrer MySQL :


rm /var/lib/mysql/ib_logfile*

/etc/init.d/mysql start

Une fois démarré nous allons vérifier que le master est prêt à recevoir des connexions des autres noeuds. Pour cela il faut se connecter au CLI MySQL et taper show status like 'wsrep%';. Si tout c'est bien passé vous devriez avoir une sortie similaire à celle ci :


mysql> show status like 'wsrep%';
+----------------------------+--------------------------------------+
| Variable_name              | Value                                |
+----------------------------+--------------------------------------+
| wsrep_local_state_uuid     | ae7ae1e4-a900-11e2-0800-4e155b81b7f6 |
| wsrep_protocol_version     | 4                                    |
| wsrep_last_committed       | 2838280                              |
| wsrep_replicated           | 360058                               |
| wsrep_replicated_bytes     | 577520655                            |
| wsrep_received             | 468500                               |
| wsrep_received_bytes       | 740525323                            |
| wsrep_local_commits        | 359811                               |
| wsrep_local_cert_failures  | 17                                   |
| wsrep_local_bf_aborts      | 247                                  |
| wsrep_local_replays        | 0                                    |
| wsrep_local_send_queue     | 0                                    |
| wsrep_local_send_queue_avg | 0.000000                             |
| wsrep_local_recv_queue     | 0                                    |
| wsrep_local_recv_queue_avg | 0.000000                             |
| wsrep_flow_control_paused  | 0.000000                             |
| wsrep_flow_control_sent    | 0                                    |
| wsrep_flow_control_recv    | 0                                    |
| wsrep_cert_deps_distance   | 0.000000                             |
| wsrep_apply_oooe           | 0.000000                             |
| wsrep_apply_oool           | 0.000000                             |
| wsrep_apply_window         | 0.000000                             |
| wsrep_commit_oooe          | 0.000000                             |
| wsrep_commit_oool          | 0.000000                             |
| wsrep_commit_window        | 0.000000                             |
| wsrep_local_state          | 4                                    |
| wsrep_local_state_comment  | Synced                               |
| wsrep_cert_index_size      | 0                                    |
| wsrep_causal_reads         | 0                                    |
| wsrep_incoming_addresses   | 192.168.1.1:3306                     |
| wsrep_cluster_conf_id      | 13                                   |
| wsrep_cluster_size         | 1                                    |
| wsrep_cluster_state_uuid   | ae7ae1e4-a900-11e2-0800-4e155b81b7f6 |
| wsrep_cluster_status       | Primary                              |
| wsrep_connected            | ON                                   |
| wsrep_local_index          | 0                                    |
| wsrep_provider_name        | Galera                               |
| wsrep_provider_vendor      | Codership Oy <info@codership.com>    |
| wsrep_provider_version     | 2.5(r150)                            |
| wsrep_ready                | ON                                   |
+----------------------------+--------------------------------------+
40 rows in set (0.00 sec)

Il y a plusieurs point importants à vérifier :

  • Il faut que wsrep_ready soit sur ON afin que le master puisse accepter de nouvelles connexions.
  • Il faut que wsrep_local_state_comment soit Synced.
  • Et enfin, on peut checker wsrep_local_state_uuid qui indique l'état de la réplication, les nœuds qui vont rejoindre le cluster doivent atteindre le même état assurant que la réplication est complète.

Pour assurer le bon fonctionnement de la réplication via xtrabackup vous devez créer un compte ayant l'accès à toutes les bases en local sur votre donneur. Pour suivre notre exemple, il s'agit du compte mysql avec comme mot de passe password. Ceci est indiqué dans le my.cnf : wsrep_sst_auth = mysql:password.


GRANT ALL PRIVILEGES ON *.* TO 'mysql'@'localhost' IDENTIFIED BY 'password';

En fait, une fois que le cluster sera complètement monté il faudra que chaque nœud est cet user car chaque nœud sera maitre et pourra être donneur pour les autres. Heureusement la réplication via Galera copie les droits du donneurs vers le receveur, du coup cet user sera automatiquement ajouté sur les autres nœuds. Sur debian, cette situation engendre un léger désagrément qui peut simplement se réparer. En effet, les scripts de démarrage et autres scripts de routine de MySQL utilisent le compte debian-sys-maint qui est renseigné dans le fichier /etc/mysql/debian.cnf. Les droits étant répliqués sur tous les nœuds et l'user debian-sys-maint ayant un mot de passe aléatoire généré à l'installation de MySQL vous allez devoir copier le fichier /etc/mysql/debian.cnf du master sur les autres nœuds. Pour cela vous pouvez par exemple utiliser scp :


scp /etc/mysql/debian.cnf root@192.168.1.2:/etc/mysql/debian.cnf
scp /etc/mysql/debian.cnf root@192.168.1.3:/etc/mysql/debian.cnf

Si tout est bon vous pouvez passez au nœud suivant. Il suffit simplement d'éditer le my.cnf en prenant garde de modifier trois variables :

  • wsrep_node_name
  • server-id
  • wsrep_cluster_address

La variable wsrep_cluster_address doit pointer sur le noeud donneur, dans notre exemple node1. (ex. : wsrep_cluster_address = "gcomm://node1")

Il ne reste plus qu'à lancer vos noeuds qui vont se synchronisés sur le master. Pour vérifier que tout est bon on peut commencer par rejouer la même requête que précédemment :


mysql> show status like 'wsrep%';
+----------------------------+----------------------------------------------------+
| Variable_name              | Value                                              |
+----------------------------+----------------------------------------------------+
| wsrep_local_state_uuid     | ae7ae1e4-a900-11e2-0800-4e155b81b7f6               |
| wsrep_protocol_version     | 4                                                  |
| wsrep_last_committed       | 2838280                                            |
| wsrep_replicated           | 360058                                             |
| wsrep_replicated_bytes     | 577520655                                          |
| wsrep_received             | 468502                                             |
| wsrep_received_bytes       | 740525751                                          |
| wsrep_local_commits        | 359811                                             |
| wsrep_local_cert_failures  | 17                                                 |
| wsrep_local_bf_aborts      | 247                                                |
| wsrep_local_replays        | 0                                                  |
| wsrep_local_send_queue     | 0                                                  |
| wsrep_local_send_queue_avg | 0.000000                                           |
| wsrep_local_recv_queue     | 0                                                  |
| wsrep_local_recv_queue_avg | 0.000000                                           |
| wsrep_flow_control_paused  | 0.000000                                           |
| wsrep_flow_control_sent    | 0                                                  |
| wsrep_flow_control_recv    | 0                                                  |
| wsrep_cert_deps_distance   | 0.000000                                           |
| wsrep_apply_oooe           | 0.000000                                           |
| wsrep_apply_oool           | 0.000000                                           |
| wsrep_apply_window         | 0.000000                                           |
| wsrep_commit_oooe          | 0.000000                                           |
| wsrep_commit_oool          | 0.000000                                           |
| wsrep_commit_window        | 0.000000                                           |
| wsrep_local_state          | 4                                                  |
| wsrep_local_state_comment  | Synced                                             |
| wsrep_cert_index_size      | 0                                                  |
| wsrep_causal_reads         | 0                                                  |
| wsrep_incoming_addresses   | 192.168.1.1:3306,192.168.1.2:3306,192.168.1.3:3306 |
| wsrep_cluster_conf_id      | 15                                                 |
| wsrep_cluster_size         | 3                                                  |
| wsrep_cluster_state_uuid   | ae7ae1e4-a900-11e2-0800-4e155b81b7f6               |
| wsrep_cluster_status       | Primary                                            |
| wsrep_connected            | ON                                                 |
| wsrep_local_index          | 0                                                  |
| wsrep_provider_name        | Galera                                             |
| wsrep_provider_vendor      | Codership Oy <info@codership.com>                  |
| wsrep_provider_version     | 2.5(r150)                                          |
| wsrep_ready                | ON                                                 |
+----------------------------+----------------------------------------------------+

Notre cluster est visiblement bien formé de nos 3 noeuds comme l'indique wsrep_incoming_addresses. Vous pouvez donc commencer à vous amuser avec en créant une base depuis un nœud puis en vérifiant que celle ci est bien répliquée sur l'autre. Si vous êtes un bon admin sys vous préfèrerez sans doute faire des tests plus sérieux à l'aide de sysbench par exemple 😎

Attendez, ce n'est pas fini, il nous reste encore à changer les variables wsrep_cluster_address sur les 3 nœuds. Cela va permettre à nos 3 nodes d'êtres master pour les autres. Pour cela il faut passer sur chacun des noeuds et désigner comme master ces 2 copains. Par exemple sur le node1 nous jouerons cette requête :


SET GLOBAL wsrep_cluster_address="gcomm://node2,node3;

Il ne faudra pas oublier de faire de même pour le my.cnf pour que le paramètre soit pris en compte en cas de redémarrage. Exemple sur node1 :


wsrep_cluster_address = "gcomm://node2,node3"

Voilà, maintenant c'est fini ! Vous avez un cluster robuste sur lequel vous pouvez perdre jusqu'à deux nodes sans perdre la moindre de vos données. Dans un prochain tutoriel nous alons voir comment mettre en place un loadbalancer via HAproxy afin de tirer profit de ces 3 nœuds et encore dans un autre nous verrons comment compiler sysbench 0.5 et effectuer quelques benchs sur notre cluster tout beau tout neuf.

Inspiré de : Percona XtraDB Cluster et de pleins d'autres sources en provenance du grand méchant internet...

♥ Partage sur tes réseaux sociaux ♥
Kévin MET
Kévin MET

Auteur de ce blog et gérant de la société MNT-TECH, je publie sur ce blog lorsque le temps me le permet et lorsqu'un sujet qui me parait intéressant n'a pas encore été abordé en français. Toutes les informations techniques présentes sur cette page peuvent être réutilisées moyennant le fait de citer la source.