Galera Cluster per MySQL, setup su 2 nodi

Galera Cluster è un framework free per l’attivazione di cluster sincrono multi-master per MySQL/MariaDB. In poche parole, permette di realizzare una topologia multi-master active-active con replica sincrona dei dati, abilitando le operazioni di lettura e scrittura su tutti i nodi del cluster.

In contesti di produzione si raccomandato l’utilizzo di almeno 3 nodi per evitare situazioni di split-brain, ma noi proveremo il setup con soli 2 nodi in replica.

Predisposizione ambiente

Per questo use case ho scelto di installare Galera Cluster su 2 istanze AWS EC2, ma questo è un dettaglio, dato che il setup dipende dal sistema operativo in uso e non dal tipo di macchina. Non entro nel dettaglio delle configurazioni AWS, ma io ho scelto 2 istanze EC2 di tipo t2.micro con installata l’immagine di CentOS 7 (x86_64) – with Updates HVM, per le quali ho configurato opportunamente la VPC, il Security Group e la private key per abilitare l’accesso ssh.

Altra cosa da dire è che verrà posta attenzione anche agli aspetti di sicurezza, infatti vedremo come configurare opportunamente selinux ed il firewall delle macchine.

Procediamo con l’installazione su entrambi i nodi

Configuriamo il repo file per accedere al repository di Galera Cluster, creiamo il file /etc/yum.repos.d/galera.repo con il contenuto seguente.

	[galera]
	name = Galera
	baseurl = https://releases.galeracluster.com/galera-4.10/centos/7/x86_64
	gpgkey = https://releases.galeracluster.com/GPG-KEY-galeracluster.com
	gpgcheck = 1

	[mysql-wsrep]
	name = MySQL-wsrep
	baseurl = https://releases.galeracluster.com/mysql-wsrep-8.0.23-26.6/centos/7/x86_64
	gpgkey = https://releases.galeracluster.com/GPG-KEY-galeracluster.com
	gpgcheck = 1

Per galera e mysql-wsrep potete scegliere le versioni che più vi fanno comodo.

Installiamo quindi i pacchetti:

	yum install rsync firewalld
	yum install galera-4 mysql-wsrep-8.0

Le stesse operazioni vanno effettuate su entrambi i nodi.

Configurazione

Come prima cosa andiamo a creare il folder /opt/mysql/data e ad assegnare l’ownership all’utente mysql sul folder /opt/mysql.
A seguire configuriamo mysql andando a modificare il file /etc/my.cnf aggiungendo la parte relativa a galera su entrambi i nodi, ovviamente variando node_name e node_address.

	datadir=/opt/mysql/data
	socket=/var/lib/mysql/mysql.sock
	port=3336
	max_connections=100

	binlog_format=row
	default_storage_engine=InnoDB
	innodb_autoinc_lock_mode=2
	innodb_io_capacity=1000
	innodb_buffer_pool_size=512M
	innodb_log_buffer_size=32M
	innodb_log_file_size=128M
	bind-address=0.0.0.0

	wsrep_on=ON
	wsrep_provider=/usr/lib64/galera-4/libgalera_smm.so
	wsrep_cluster_name='galera-clusterdb'
	wsrep_node_name='galera-node-1'
	wsrep_node_address="172.31.81.55"
	wsrep_cluster_address="gcomm://172.31.81.55,172.31.80.162"
	wsrep_provider_options="gcache.size=256M; gcache.page_size=256M"
	wsrep_slave_threads=4
	wsrep_sst_method=rsync

	log-error=/var/log/mysqld.log
	pid-file=/var/run/mysqld/mysqld.pid

A proposito di configurazione, i parametri principali da configurare sono i seguenti:

  • datadir è la directory usata come storage per i file di dati, nel mio caso /opt/mysql/data
  • port è la porta su cui il sistema ascolta, nel mio caso 3336
  • max_connections è il numero massimo di connessioni che il sistema può accettare
  • innodb_buffer_pool_size dipende dalle caratteristiche del server, in genere si configura con la metà della RAM disponibile sul server
  • wsrep_cluster_name è il nome da assegnare al cluster, nel mio caso galera-clusterdb
  • wsrep_node_name è il nome del nodo, nel mio caso galera-node-1 per il primo nodo e galera-node-2 per il secondo nodo
  • wsrep_node_address è l’indirizzo del nodo, nel mio caso i nodi sono 172.31.81.55 e 172.31.80.162
  • wsrep_cluster_address riporta la lista dei nodi appartenenti al cluster
  • wsrep_provider_options dipende dalle caratteristiche del server

Configurazione SELinux e Firewall

Come detto all’inizio dell’articolo, vogliamo porre l’attenzione anche sulle questioni di sicurezza negli accessi ai server, pertanto vediamo come configurare sia il firewall sia selinux su entrambi i server.
Galera Cluster usa le seguenti porte TCP: la porta per MySQL (3306 la standard oppure 3336 come nel nostro caso), 4444, 4567, e 4568. Alle quali si aggiunge la porta UDP 4567.

SELinux
Abilitiamo le porte elencate su selinux con i seguenti comandi.

	semanage port -a -t mysqld_port_t -p tcp 3306
	semanage port -a -t mysqld_port_t -p tcp 3336
	semanage port -a -t mysqld_port_t -p tcp 4444
	semanage port -a -t mysqld_port_t -p tcp 4567
	semanage port -a -t mysqld_port_t -p udp 4567
	semanage port -a -t mysqld_port_t -p tcp 4568
	semanage permissive -a mysqld_t

Firewall
Facciamo la stessa configurazione per il firewall.

	systemctl enable firewalld
	systemctl start firewalld

	firewall-cmd --zone=public --add-service=mysql --permanent
	firewall-cmd --zone=public --add-port=3306/tcp --permanent
	firewall-cmd --zone=public --add-port=3336/tcp --permanent
	firewall-cmd --zone=public --add-port=4444/tcp --permanent
	firewall-cmd --zone=public --add-port=4567/tcp --permanent
	firewall-cmd --zone=public --add-port=4567/udp --permanent
	firewall-cmd --zone=public --add-port=4568/tcp --permanent

	firewall-cmd --reload

Avvio del Cluster Galera

L’avvio dei nodi che compongono il cluster è l’aspetto più delicato di tutto il processo di setup, perché all’avvio un nodo di default non parte mai come nodo primario, ma assume che il primario sia già in esecuzione cercando di connettersi ad esso ed effettuare il join al cluster. Il nodo all’avvio va sempre a cercare la presenza del nodo primario, se lo trova avvia la sincronizzazione del database, se invece non lo trova resta in uno stato non-operational.
Il problema è che al primo avvio non esiste un nodo primario, pertanto lo start di MySQL va effettuato specificando un opportuno parametro –wsrep-new-cluster, che ripeto va usato solo per l’avvio del primo nodo.

Procediamo quindi con l’avvio di MySQL sul primo nodo del cluster.

	mysqld_bootstrap --wsrep-new-cluster

Attenzione: il parametro –wsrep-new-cluster deve essere usato solo quando si inizializza il nodo primario, mai per connettere un nuovo nodo ad un cluster esistente.

Recuperiamo poi la password temporanea generata dal sistema allo startup, ed eseguiamo lo script mysql_secure_installation, seguendo le istruzioni a video.

	grep password /var/log/mysqld.log
	[MY-010454] [Server] A temporary password is generated for root@localhost: ul_aj+V5A6py
	[MY-010733] [Server] Shutting down plugin 'caching_sha2_password'
	[MY-010733] [Server] Shutting down plugin 'sha256_password'
	[MY-010733] [Server] Shutting down plugin 'mysql_native_password'
	[MY-010733] [Server] Shutting down plugin 'caching_sha2_password'
	[MY-010733] [Server] Shutting down plugin 'sha256_password'
	[MY-010733] [Server] Shutting down plugin 'mysql_native_password'

Nel nostro caso la password dell’utente root generata in automatico è ul_aj+V5A6py.

	/usr/bin/mysql_secure_installation

Prima di avviare il secondo nodo controlliamo lo stato del cluster.

	mysql -p -u root -e "SHOW STATUS LIKE 'wsrep_cluster_size'"
+--------------------+-------+
| Variable_name      | Value |
+--------------------+-------+
| wsrep_cluster_size | 1     |
+--------------------+-------+

A questo punto avviamo MySQL sul secondo nodo tramite systemctl start mysqld e poi ricontrolliamo lo stato del cluster.

	mysql -p -u root -e "SHOW STATUS LIKE 'wsrep_cluster_size'"
+--------------------+-------+
| Variable_name      | Value |
+--------------------+-------+
| wsrep_cluster_size | 2     |
+--------------------+-------+

Come si nota dall’output ottenuto, il cluster adesso è composto da 2 nodi.

Nel caso in cui volessimo aggiungere altri nodi al cluster, basta predisporre i nuovi nodi con le opportune configurazioni ed avviare MySQL tramite systemctl.

Verifica funzionamento del Cluster

Per verificare il funzionamento di replica del cluster Galera proviamo ad inserire dati nel database connettendoci al primo nodo e verifichiamo che gli stessi dati siano sincronizzati sul secondo e viceversa.

Sul primo nodo andiamo a creare il database mydb, la tabella mytable ed inseriamo un dato.

	create database mydb;
	create table mydb.mytable (Name varchar(255), Value varchar(255));
	insert into mydb.mytable (Name, Value) values ("nome1", "valore1");

Spostiamoci sul secondo nodo e leggiamo il dato all’interno della tabella mytable.

	select * from mydb.mytable;
+-------+---------+
| Name  | Value   |
+-------+---------+
| nome1 | valore1 |
+-------+---------+

Sempre sul secondo nodo inseriamo un nuovo dato nella stessa tabella.

	insert into mydb.mytable (Name, Value) values ("nome2", "valore2");

Torniamo sul primo nodo ed eseguiamo nuovamente la select sulla tabella, come vediamo ritroviamo entrambi i dati.

+-------+---------+
| Name  | Value   |
+-------+---------+
| nome1 | valore1 |
| nome2 | valore2 |
+-------+---------+

In questa configurazione quindi il Cluster Galera sta funzionando correttamente replicando i dati sui 2 nodi, che lavorano in modalità master - master ed accettano entrambi operazioni di lettura e scrittura.

Riavvio in caso di problemi

Come sottolineato in precedenza il punto più delicato di un cluster Galera è l’ordine di avvio dei nodi, perché allo startup ogni nodo partirebbe andando a cercare un Primary Node disponibile, pertanto non è consigliabile abilitare l’avvio automatico di MySQL tramite systemcl all’avvio dei server che compongono il cluster. Pensiamo ad esempio ad un restart completo dei server, se su entrambi l’avvio di MySQL è schedulato in automatico tramite systemctl start mysqld alla fine il cluster non verrà avviato, perché nessuno dei nodi effettuerà il bootstrap del cluster.
In caso di riavvio completo, a seguito di situazioni anomale o problematiche di connettività di rete, la cosa da fare per un riavvio controllato è identificare il nodo più avanti in termini di commit di transazione ed effettuare il bootstrap di questo nodo, facendolo tornare primario.

Per identificare il nodo più avanti in termini di commit:

	SHOW STATUS LIKE 'wsrep_last_committed';

Quindi fermare tutti i nodi ed eseguire il bootstrap del nodo identificato come primario /usr/bin/mysqld_bootstrap --wsrep-new-cluster, in seguito avviare gli altri nodi. Una comoda soluzione per automatizzare il reboot dei nodi è descritta qui.