MariaDB Galera Cluster¶
Introduction¶
Galera Cluster fournit la réplication synchrone multi-master pour MariaDB. Tous les nœuds sont égaux et peuvent accepter lectures et écritures, avec garantie de cohérence forte (RPO = 0).
Prérequis¶
- Concepts HA
- Compréhension de MySQL/MariaDB
- Cluster de 3 controllers minimum (nombre impair pour le quorum)
Points à apprendre¶
Architecture Galera¶
graph TB
openstack["OpenStack Services<br/>Nova, Neutron,<br/>Keystone, etc."]
haproxy["HAProxy<br/>Load Balancer<br/>Round-robin"]
subgraph galera["Galera Cluster"]
node1["MariaDB Node 1<br/>Controller-1<br/>Primary<br/>wsrep_local_state = 4"]
node2["MariaDB Node 2<br/>Controller-2<br/>Primary<br/>wsrep_local_state = 4"]
node3["MariaDB Node 3<br/>Controller-3<br/>Primary<br/>wsrep_local_state = 4"]
end
openstack -->|"SQL<br/>3306"| haproxy
haproxy -->|Distribute| node1
haproxy --> node2
haproxy --> node3
node1 <-->|"Galera Replication<br/>wsrep (4567)"| node2
node2 <-->|"Galera Replication"| node3
node3 <-->|"Galera Replication"| node1
Réplication synchrone¶
sequenceDiagram
participant Client
participant N1 as Node 1<br/>(Writer)
participant N2 as Node 2
participant N3 as Node 3
Client->>N1: BEGIN; INSERT...
N1->>N1: Write to local binlog
N1->>N2: Writeset broadcast
N1->>N3: Writeset broadcast
N2->>N2: Certification check
N3->>N3: Certification check
N2-->>N1: ACK
N3-->>N1: ACK
N1->>N1: COMMIT
N1-->>Client: OK
Note over N1,N3: Réplication synchrone:<br/>Commit seulement après ACK de tous les nœuds
Configuration Kolla¶
# /etc/kolla/globals.yml
# Galera activé par défaut en HA
enable_mariadb: "yes"
# Nombre de réplicas (doit correspondre au nombre de controllers)
# Pas de configuration explicite, Kolla utilise les hosts du groupe "control"
Configuration MariaDB/Galera générée¶
# /etc/mysql/conf.d/galera.cnf
[mysqld]
# Galera settings
wsrep_on = ON
wsrep_provider = /usr/lib/galera/libgalera_smm.so
wsrep_cluster_name = "openstack"
wsrep_cluster_address = "gcomm://10.0.0.11,10.0.0.12,10.0.0.13"
wsrep_node_name = "controller-1"
wsrep_node_address = "10.0.0.11"
# Replication
wsrep_sst_method = mariabackup
wsrep_sst_auth = "sstuser:sstpassword"
# Performance
wsrep_slave_threads = 4
wsrep_log_conflicts = ON
wsrep_certify_nonPK = ON
# Mode strict
binlog_format = ROW
default_storage_engine = InnoDB
innodb_autoinc_lock_mode = 2
# Timeouts
wsrep_provider_options = "gcache.size=1G; gmcast.segment=0"
États du cluster¶
| wsrep_local_state | État | Description |
|---|---|---|
| 1 | Joining | Nœud en cours de jonction |
| 2 | Donor/Desynced | Fournit SST à un autre nœud |
| 3 | Joined | Synchronisé, pas encore prêt |
| 4 | Synced | Pleinement opérationnel |
# Vérifier l'état de chaque nœud
docker exec mariadb mysql -e "SHOW STATUS LIKE 'wsrep_%';" | \
grep -E "(wsrep_local_state|wsrep_cluster_size|wsrep_ready|wsrep_connected)"
Quorum et split-brain¶
graph TB
subgraph healthy["Cluster sain (3 nœuds)"]
h1["Node 1"]
h2["Node 2"]
h3["Node 3"]
end
subgraph partial["1 nœud down (2/3 = Quorum OK)"]
p1["Node 1"]
p2["Node 2"]
p3["Node 3 ✗"]
style p3 fill:#f66
end
subgraph split["Partition (split-brain évité)"]
s1["Majorité (2)<br/>Continue"]
s2["Minorité (1)<br/>S'arrête"]
style s1 fill:#6f6
style s2 fill:#f66
end
split -.->|"La minorité refuse les requêtes:<br/>WSREP has not yet prepared node for application use"| split
State Snapshot Transfer (SST)¶
# Méthodes SST disponibles
# 1. mariabackup (recommandé) - Non bloquant
# 2. rsync - Bloquant mais rapide
# 3. mysqldump - Lent, non recommandé
# Vérifier une SST en cours
docker exec mariadb mysql -e "SHOW STATUS LIKE 'wsrep_local_state_comment';"
# Affiche "Donor/Desynced" si SST en cours
# Logs SST
docker logs mariadb | grep -i sst
Monitoring Galera¶
#!/bin/bash
# galera-status.sh
echo "=== Cluster Status ==="
docker exec mariadb mysql -e "
SHOW STATUS WHERE Variable_name IN (
'wsrep_cluster_size',
'wsrep_cluster_status',
'wsrep_ready',
'wsrep_connected',
'wsrep_local_state_comment'
);"
echo -e "\n=== Node Status ==="
docker exec mariadb mysql -e "
SHOW STATUS WHERE Variable_name IN (
'wsrep_node_name',
'wsrep_local_recv_queue',
'wsrep_local_send_queue',
'wsrep_flow_control_paused'
);"
echo -e "\n=== Replication Health ==="
docker exec mariadb mysql -e "
SHOW STATUS WHERE Variable_name LIKE 'wsrep_cert%'
OR Variable_name LIKE 'wsrep_commit%'
OR Variable_name LIKE 'wsrep_apply%';"
Récupération après panne¶
# Cas 1: Un nœud redémarre
# → Rejoint automatiquement via IST (Incremental State Transfer)
# Cas 2: Cluster complet down
# → Identifier le nœud le plus avancé
for host in controller-{1,2,3}; do
echo "$host:"
ssh $host "docker exec mariadb cat /var/lib/mysql/grastate.dat" | grep seqno
done
# Bootstrap depuis le nœud avec le seqno le plus élevé
ssh controller-1 "docker exec mariadb galera_new_cluster"
# Démarrer les autres nœuds normalement
ssh controller-2 "docker restart mariadb"
ssh controller-3 "docker restart mariadb"
# Cas 3: Quorum perdu (tous les nœuds down sauf un)
# Forcer le bootstrap sur le nœud restant
docker exec mariadb mysql -e "SET GLOBAL wsrep_provider_options='pc.bootstrap=YES';"
Configuration HAProxy pour Galera¶
# Configuration HAProxy pour Galera (générée par Kolla)
listen mariadb
bind 10.0.0.10:3306
mode tcp
option tcplog
option clitcpka
option srvtcpka
balance leastconn
option httpchk GET /
# Check via xinetd mysqlchk (port 9200)
server controller-1 10.0.0.11:3306 check port 9200 inter 5s rise 2 fall 3
server controller-2 10.0.0.12:3306 check port 9200 inter 5s rise 2 fall 3 backup
server controller-3 10.0.0.13:3306 check port 9200 inter 5s rise 2 fall 3 backup
Performance et optimisation¶
# Paramètres de performance importants
# Taille du cache de certification
wsrep_provider_options = "gcache.size=1G"
# Threads pour appliquer les writesets
wsrep_slave_threads = 4 # Typiquement 2-4x nombre de CPU
# Flow control (éviter saturation)
wsrep_provider_options = "gcs.fc_limit=64; gcs.fc_factor=0.8"
# Retry en cas de deadlock
wsrep_retry_autocommit = 3
Exemples pratiques¶
Test de réplication¶
# Sur Node 1: créer une table de test
docker exec mariadb mysql -e "
CREATE DATABASE IF NOT EXISTS test_galera;
USE test_galera;
CREATE TABLE IF NOT EXISTS repl_test (
id INT AUTO_INCREMENT PRIMARY KEY,
data VARCHAR(100),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
INSERT INTO repl_test (data) VALUES ('test from node 1');
"
# Sur Node 2: vérifier la réplication
ssh controller-2 "docker exec mariadb mysql -e 'SELECT * FROM test_galera.repl_test;'"
# Nettoyer
docker exec mariadb mysql -e "DROP DATABASE test_galera;"
Simulation de panne¶
# Arrêter un nœud
ssh controller-3 "docker stop mariadb"
# Vérifier que le cluster continue
docker exec mariadb mysql -e "SHOW STATUS LIKE 'wsrep_cluster_size';"
# Doit afficher: 2
# Insérer des données (doit fonctionner)
docker exec mariadb mysql -e "INSERT INTO keystone.test VALUES(1);" || echo "OK expected to fail - table doesn't exist"
# Redémarrer le nœud
ssh controller-3 "docker start mariadb"
# Vérifier le rejoin
sleep 30
ssh controller-3 "docker exec mariadb mysql -e \"SHOW STATUS LIKE 'wsrep_local_state_comment';\""
# Doit afficher: Synced
Backup Galera¶
# Backup à chaud avec mariabackup
docker exec mariadb mariabackup --backup \
--target-dir=/backup/$(date +%Y%m%d) \
--user=root --password=$MYSQL_ROOT_PASSWORD
# Ou depuis l'extérieur
mysqldump --single-transaction --all-databases \
-h 10.0.0.10 -u root -p > galera_backup.sql
Ressources¶
Checkpoint¶
- Cluster Galera avec 3 nœuds en état "Synced"
- wsrep_cluster_size = 3
- Réplication synchrone fonctionnelle
- Failover testé (arrêt d'un nœud)
- Rejoin automatique après redémarrage
- HAProxy distribue les requêtes correctement