Galera Cluster for MySQL, 2 nodes setup
Galera Cluster is a synchronous multi-master cluster for MySQL/MariaDB. It allows you to create an active-active multi-master topology with synchronous data replication, supporting read and write operations on all nodes in the cluster.
For a production use at least 3 nodes are recommended to avoid split-brain situations, but we will test the setup with only 2 nodes in replica.
Environment preparation
For this use case I chose to install Galera Cluster on 2 AWS EC2 instances, but this is a detail, since the setup depends on the operating system in use and not on the type of machine. I chose 2 EC2 instances of the t2.micro type with the CentOS 7 (x86_64) – with Updates HVM image installed, I configured the VPC, the Security Group and the private key to enable ssh access.
In the post attention will also be paid to the security aspects also, we will see how to properly configure selinux and the firewall on the machines.
Let’s proceed with the installation on both nodes
Configure the repo file to access the Galera Cluster repository, create the file /etc/yum.repos.d/galera.repo with the following content.
[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
For galera and mysql-wsrep you can choose the versions you prefer.
Install the packages:
yum install rsync firewalld
yum install galera-4 mysql-wsrep-8.0
Repeat the same procedures on both nodes.
Configuration
First of all let’s create the folder /opt/mysql/data and assign the ownership to the mysql user on the folder /opt/mysql.
Next we configure mysql by editing the /etc/my.cnf file and adding the specific section for galera on both nodes, of course you have to use your node_name and 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
About the configuration, the main parameters to configure are the following:
- datadir is the storage directory for data files, in my case /opt/mysql/data
- port is the port the system listens on, in my case 3336
- max_connections is the maximum number of connections that the system can accept
- innodb_buffer_pool_size it depends on the characteristics of the server, usually it is configured with half of the RAM available on the server
- wsrep_cluster_name is the name to assign to the cluster, in my case galera-clusterdb
- wsrep_node_name is the name of the node, in my case galera-node-1 for the first node and galera-node-2 for the second node
- wsrep_node_address is the node address, in my case the nodes are 172.31.81.55 and 172.31.80.162
- wsrep_cluster_address the list of nodes belonging to the cluster
- wsrep_provider_options it depends on the server specs
SELinux and Firewall configuration
As mentioned at the beginning of the post, we also want to focus attention on server access security issues, so let’s see how to configure both the firewall and selinux.
Galera Cluster uses the following TCP ports: the port for MySQL (3306 the standard one or 3336 as in our case), 4444, 4567, 4568. And it uses the UDP port 4567.
SELinux
Enable the required ports on selinux with the following commands.
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
Configure the 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
Starting Galera Cluster
Starting the nodes of the cluster is the most critical aspect of the whole setup, because when starting a node by default never starts as primary node, but assumes that the primary is already running trying to connect to it and join the cluster. When starting up, the node always looks for the presence of the primary node, if it finds it, it starts database synchronization, if it doesn’t find it, it remains in a non-operational state.
The problem here is that at the first start there is no primary node, therefore the start of MySQL must be done by specifying an appropriate parameter –wsrep-new-cluster, again it must be used only for starting the first node.
So, let’s start MySQL on the first node of the cluster.
mysqld_bootstrap --wsrep-new-cluster
Note: the –wsrep-new-cluster parameter should only be used when initializing the primary node.
We need to recover the temporary password generated by the system at startup, and run the mysql_secure_installation script, following the on-screen instructions.
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'
In my case the auto generated root password is ul_aj+V5A6py.
/usr/bin/mysql_secure_installation
Before starting the second node, check the status of the cluster.
mysql -p -u root -e "SHOW STATUS LIKE 'wsrep_cluster_size'"
+--------------------+-------+
| Variable_name | Value |
+--------------------+-------+
| wsrep_cluster_size | 1 |
+--------------------+-------+
At this point we start MySQL on the second node using systemctl start mysqld
and then check the cluster status again.
mysql -p -u root -e "SHOW STATUS LIKE 'wsrep_cluster_size'"
+--------------------+-------+
| Variable_name | Value |
+--------------------+-------+
| wsrep_cluster_size | 2 |
+--------------------+-------+
As you can see, the cluster is now composed of 2 nodes.
In case we want to add other nodes to the cluster, just prepare the new nodes with the appropriate configurations and start MySQL using systemctl.
Check if cluster is operational
To verify the replica operation of the Galera cluster, let’s try to insert data into the database on the first node and check that the same data is synchronized on the second and vice versa.
On the first node we create the mydb database, the mytable table and insert a data.
create database mydb;
create table mydb.mytable (Name varchar(255), Value varchar(255));
insert into mydb.mytable (Name, Value) values ("nome1", "valore1");
Let’s move to the second node and read the data inside the mytable table.
select * from mydb.mytable;
+-------+---------+
| Name | Value |
+-------+---------+
| nome1 | valore1 |
+-------+---------+
On the second node insert a new data in the same table.
insert into mydb.mytable (Name, Value) values ("nome2", "valore2");
Let’s go back to the first node and execute the select again on the table, as we can see we find both data.
+-------+---------+
| Name | Value |
+-------+---------+
| nome1 | valore1 |
| nome2 | valore2 |
+-------+---------+
So, in this configuration Galera Cluster is working correctly by replicating the data on the 2 nodes, that works in master - master mode, and both accept read and write operations.
Restart in case of failure
As previously noted, the most critical point of a Galera cluster is the startup order of the nodes, because at startup each node would start looking for an available Primary Node, therefore it is not a good idea to enable the automatic startup of MySQL via systemcl at the servers boot. Let’s think for example of a complete restart of the servers, if on both the start of MySQL is scheduled automatically via systemctl start mysqld, the cluster will not start, because none of the nodes will bootstrap the cluster.
In case of a full reboot, due to abnormal situations or network connectivity issues, what you should do is to identify the most up-to-date node in terms of transaction commits and bootstrap this node back to primary.
To determine the most up-to-date node in terms of commits:
SHOW STATUS LIKE 'wsrep_last_committed';
So you have to stop all nodes and bootstrap the node identified as primary /usr/bin/mysqld_bootstrap --wsrep-new-cluster
, then start the other nodes. A useful solution to automate node restart is described here.