En este post, exploraremos cómo implementar un clúster utilizando MySQL y Percona para lograr alta disponibilidad, recuperación ante desastres y distribución de carga en entornos de producción. A través de ejemplos prácticos, veremos cómo configurar la replicación de datos, manejar la caída de un servidor con failover, reintegrar un servidor caído y distribuir la carga de consultas de manera eficiente.
Los ejemplos descritos a continuación se realizarón basandose en el siguiente diagrama:
La replicación es la base de cualquier clúster de bases de datos. En nuestro setup, utilizamos Percona XtraDB Cluster para mantener los datos sincronizados entre los nodos.
1.1 Conectarse a cualquier servidor:
# NODO 1
ssh root@_ip_servidor_nodo1
# NODO 2
ssh root@_ip_servidor_nodo2
# NODO 3
ssh root@_ip_servidor_nodo3
# REPLICA
ssh root@_ip_servidor_replica
# PROXYSQL
ssh root@_ip_servidor_proxysql
Pass
xxxxxxxxxxxxxxxx
1.2 Conectarse a mysql admin de ProxySQL
mysql -u admin -padmin -h 127.0.0.1 -P 6032 --prompt='ProxySQLAdmin> '
1.3 Desde el proxy podemos ver nuestros nodos(servidores):
select * from mysql_servers;
1.4 Para comprobar que todos los nodos estan conectados podemos hacer lo siguiente:
1.4.1 Conectarse a mysql en los nodos
1.4.2 Conectarse a la base de datos
1.4.3 Verificamos en todos los nodos el ultimo ID:
select * from Companies order by CompanyID DESC LIMIT 10 \G
1.4.4 Hacer inserciones en el nodo 1 (se puede realizar desde cualquier nodo):
mysql> INSERT INTO Companies (CompanyID, Name, Address, Phone, Email) VALUES (NULL, 'Other Company', 'Unit 5699 Box 8173 DPO AP 62802', '+1-635-243-2015x623', '[email protected]');
mysql> INSERT INTO Companies (CompanyID, Name, Address, Phone, Email) VALUES (NULL, 'Other Company', 'Unit 5699 Box 8173 DPO AP 62802', '+1-635-243-2015x623', '[email protected]');
mysql> INSERT INTO Companies (CompanyID, Name, Address, Phone, Email) VALUES (NULL, 'Other Company', 'Unit 5699 Box 8173 DPO AP 62802', '+1-635-243-2015x623', '[email protected]');
Creamos 3 registros en el nodo 1, al crearlo, la información debería reflejarse en los otros nodos:
Volvemos a verificar los últimos IDs
1.5 En todos los nodos, verificamos que la información se este replicando correctamente:
select * from Companies order by CompanyID DESC LIMIT 10 \G
Podemos observar que la información se ha replicado correctamente:
La replicación de información depende de los nodos, no depende del proxy. El proxy solamente decide a quien mandarle la consulta en especifico.
2. Servidor Caído/Failover
La alta disponibilidad es crítica en sistemas de producción. Si un nodo del clúster se cae, Percona maneja automáticamente el failover para garantizar que las operaciones continúen sin interrupción.
Ejemplo de Escenario: Imaginemos que el Nodo 3 del clúster se cae. Los demás nodos detectarán esta caída y continuarán manejando las peticiones sin necesidad de intervención manual.
2.1Verificamos nuestros nodos
Desde el servidor de proxySQL ejecutamos lo siguiente:
ProxySQLAdmin> select * from mysql_galera_hostgroups;
ProxySQLAdmin> select * from runtime_mysql_servers;
Tomamos captura de nuestro grupo de escritura, para este ejemplo tenemos un nodo en linea, el cual es el nodo 3:
Verificamos que nuestros nodos funcionan correctamente, incluyendo el de escritura:
2.2 Verificamos el estado de todos nuestros nodos
En todos los nodos ejecutamos:
2.3 Detenemos mysql en nuestro nodo de escritura
En el nodo marcado como de escritura ejecutamos el siguiente comando:
2.4 Verificamos que nodo ahora es el de escritura:
En el servidor de proxySQL ejecutamos lo siguiente:
select * from runtime_mysql_servers;
Una vez hecho esto podemos notar que ha tomado otro nodo de los pertenecientes al grupo de escritura, en este caso el nodo 2:
2.5 Levantamos nuevamente nuestro servidor caído:
En el servidor caido ejecutamos lo siguiente:
Finalmente al levantar nuevamente nuestro servidor podemos observar que lo vuelve a tomar como el servidor principal de escritura (Esto puede variar dependiendo si se ha terminado de sincronizar):
3. Reintegro de un Servidor Caído/Failover
Una vez que el servidor caído está de vuelta en línea, se debe reintegrar al clúster y ponerse al día con la replicación de datos.
Ejemplo de reintegración:
3.1 Detenemos mysql en cualquiera de nuestros nodos
En cualquier nodo ejecutamos:
3.2 Insertamos registros
Hacer inserciones en cualquiera de los nodos sobrantes (Se pueden realizar desde cualquier nodo):
mysql> INSERT INTO Companies (CompanyID, Name, Address, Phone, Email) VALUES (NULL, 'Other Company', 'Unit 5699 Box 8173 DPO AP 62802', '+1-635-243-2015x623', '[email protected]');
mysql> INSERT INTO Companies (CompanyID, Name, Address, Phone, Email) VALUES (NULL, 'Other Company', 'Unit 5699 Box 8173 DPO AP 62802', '+1-635-243-2015x623', '[email protected]');
mysql> INSERT INTO Companies (CompanyID, Name, Address, Phone, Email) VALUES (NULL, 'Other Company', 'Unit 5699 Box 8173 DPO AP 62802', '+1-635-243-2015x623', '[email protected]');
mysql> INSERT INTO Companies (CompanyID, Name, Address, Phone, Email) VALUES (NULL, 'Other Company', 'Unit 5699 Box 8173 DPO AP 62802', '+1-635-243-2015x623', '[email protected]');
mysql> INSERT INTO Companies (CompanyID, Name, Address, Phone, Email) VALUES (NULL, 'Other Company', 'Unit 5699 Box 8173 DPO AP 62802', '+1-635-243-2015x623', '[email protected]');
Para esta prueba, teniendo un servidor detenido realizamos una inserción de 5 registros, la cual se replico hacia nuestro servidor que esta en linea:
3.3 Levantamos el nodo faltante
En el nodo caido ejecutamos:
Levantamos nuevamente nuestro nodo y al hacerlo se nos replica la información que se agrego previamente:
Levantamos nuevamente nuestro nodo y al hacerlo se nos replica la información que se agrego previamente:
select * from Companies order by CompanyID DESC LIMIT 10 \G
Extrabackup tiene una forma de conocer el ultimo hash de los registros insertados, con esto se guiá de que tan sincronizado esta con las otras bases de datos.
4. Distribución de Carga de Consultas
Para optimizar el rendimiento, es importante distribuir las consultas entre los nodos disponibles en el clúster. Esto se puede lograr utilizando un balanceador de carga como ProxySQL o configurando adecuadamente la lógica de la aplicación.
Ejemplo de balanceo de carga:
4.1 Conectarse al mysql admin de ProxySQL
ssh root@_ip_servidor_proxysql
4.2 Consultar el host que responde una consulta:
Ejecutamos:
mysql -u apps -p"It_is_so_Secure1" -h 127.0.0.1 -P 3306 -e "SELECT @@hostname;"
Al realizar una consulta podemos observar como nos responde un nodo diferente cada vez:
4.3 Insertamos un registro con el nombre del host
Hacemos una consulta para insertar y una para el select del host en la misma linea:
mysql -u apps -p"It_is_so_Secure1" -h 127.0.0.1 -P 3306 -e "INSERT INTO companydb.Companies (CompanyID, Name, Address, Phone, Email) VALUES (NULL, CONCAT('Empresa por: ', @@hostname), 'Unit 5699 Box 8173 DPO AP 62802', '+1-635-243-2015x623', '[email protected]');SELECT @@hostname;"
Si hacemos inserciones y consultas simultaneamente, podemos observar como por cada consulta nos responde un nodo diferente, sin embargo, las inserciones siempre se hacen en el nodo 3 el cual es nuestro nodo de escritura:
4.4 Comprobamos los últimos registros
mysql -u apps -p"It_is_so_Secure1" -h 127.0.0.1 -P 3306 -e "select * from companydb.Companies order by CompanyID DESC LIMIT 10 \G"
5. Levantar un nuevo Nodo
5.1 Instalar PXC
Desde una instancia nueva de ubuntu 20.04:
apt update \
&& wget https://repo.percona.com/apt/percona-release_latest.generic_all.deb \
&& dpkg -i percona-release_latest.generic_all.deb \
&& percona-release setup pxc57 \
&& apt install -y libdbi-perl libdbd-mysql-perl libmecab2 socat libcurl4-openssl-dev libev4 \
&& apt install -y percona-xtrabackup-24 qpress \
&& apt install -y percona-xtradb-cluster-full-57=5.7.40-31.63-1.focal percona-xtradb-cluster-server-5.7=5.7.40-31.63-1.focal percona-xtradb-cluster-client-5.7=5.7.40-31.63-1.focal percona-xtradb-cluster-test-5.7=5.7.40-31.63-1.focal percona-xtradb-cluster-5.7-dbg=5.7.40-31.63-1.focal percona-xtradb-cluster-server-debug-5.7=5.7.40-31.63-1.focal percona-xtradb-cluster-garbd-5.7=5.7.40-31.63-1.focal percona-xtradb-cluster-garbd-debug-5.7=5.7.40-31.63-1.focal percona-xtradb-cluster-common-5.7=5.7.40-31.63-1.focal \
&& systemctl start mysql
5.2 Editar el archivo
nano /etc/mysql/percona-xtradb-cluster.conf.d/wsrep.cnf
Agregar la configuración del nodo
GNU nano 4.8 /etc/mysql/percona-xtradb-cluster.conf.d/wsrep.cnf
[mysqld]
# Path to Galera library
wsrep_provider=/usr/lib/galera3/libgalera_smm.so
# Cluster connection URL contains IPs of nodes
#If no IP is found, this implies that a new cluster needs to be created,
#in order to do that you need to bootstrap this node
# Agregar las ip's incluidas la del nuevo nodo
wsrep_cluster_address=gcomm://192.168.12.3,192.168.12.4,192.168.12.5,192.168.12.6
# In order for Galera to work correctly binlog format should be ROW
binlog_format=ROW
# MyISAM storage engine has only experimental support
default_storage_engine=InnoDB
# Slave thread to use
# El numero de nucleos "nproc"
wsrep_slave_threads=4
wsrep_log_conflicts
# This changes how InnoDB autoincrement locks are managed and is a requirement for Galera
innodb_autoinc_lock_mode=2
# Node IP address
# IP del nodo actual
wsrep_node_address=192.168.12.6
# Cluster name
wsrep_cluster_name=pxc-cluster
#If wsrep_node_name is not specified, then system hostname will be used
# Nombre para identificar al nodo
wsrep_node_name=Node-4
#pxc_strict_mode allowed values: DISABLED,PERMISSIVE,ENFORCING,MASTER
pxc_strict_mode=PERMISSIVE
# SST method
wsrep_sst_method=xtrabackup-v2
#Authentication for SST method
#wsrep_sst_auth="sstuser:s3cretPass"
wsrep_sst_auth="sstuser:xxxxxxxxxxxx"
Detener el servicio de mysql
Iniciar de nuevo el servicio de mysql
Comprobamos que el servidor mysql se inicio correctamente:
5.3 Entrar a mysql
mysql -u root -p
# Utilizar la contraseña de root
Listamos las bases de datos, las bases de datos de los otros nodos deben estar presentes:
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| companydb |
| mughees |
| mysql |
| performance_schema |
| sys |
+--------------------+
6 rows in set (0.00 sec)
Seleccionamos la base de datos:
Consultamos los últimos registros, deben estar sincronizados con los otros nodos:
mysql> select * from Companies order by CompanyId DESC limit 10 \G
*************************** 1. row ***************************
CompanyID: 10078
Name: Empresa por: Node-3
Address: Unit 5699 Box 8173 DPO AP 62802
Phone: +1-635-243-2015x623
Email: jaredgill@miller.com
*************************** 2. row ***************************
CompanyID: 10075
Name: Empresa por: Node-3
Address: Unit 5699 Box 8173 DPO AP 62802
Phone: +1-635-243-2015x623
Email: jaredgill@miller.com
6. Realizar Backups y Restaurarlos
6.1 Realizar un backup
Para realizar el backup con extrabackup es necesario, generar el backup y prepararlo con las marcas de tiempo.
Crear un directorio:
Generar el backup con xtrabackup
root@Replica:/var/backupc# xtrabackup --backup --target-dir=/var/backupc
Preparar el backup con xtrabackup:
root@Replica:/var/backupc# xtrabackup --prepare --target-dir=/var/backupc
Levantamos un nuevo nodo para probar la restauración
Copiar el backup al nodo:
scp Node-5:/backup /var/backups/backupc
Nos conectamos al servidor:
En el servidor nos dirigimos a la carpeta que contiene el backup:
Detenemos mysql:
Aplicamos el comando rsync como root:
rsync -rvt --exclude 'xtrabackup_checkpoints' --exclude 'xtrabackup_logfile' ./ /var/lib/mysql
Asignamos permisos a la carpeta
chown -R mysql:mysql /var/lib/mysql/
Iniciamos mysql y comprobamos que el backup funcione:
systemctl start mysql
mysql -u root -p
Listamos las bases de datos:
Seleccionar la base de datos y consultar los ultimos registros:
mysql> use companydb;
mysql> SELECT * FROM Companies ORDER BY CompanyId DESC LIMIT 10 \G
*************************** 1. row ***************************
CompanyID: 10078
Name: Empresa por: Node-3
Address: Unit 5699 Box 8173 DPO AP 62802
Phone: +1-635-243-2015x623
Email: jaredgill@miller.com
*************************** 2. row ***************************
CompanyID: 10075
Name: Empresa por: Node-3
Address: Unit 5699 Box 8173 DPO AP 62802
Phone: +1-635-243-2015x623
Email: jaredgill@miller.com
La diferencia entre mysqldump y xtrabackup es que mysqldump generara un archivo .sql que contiene todas las consultas que se tienen que ejecutar para llegar al punto actual, xtrabackup copia el contenido de la capeta donde mysql guarda los datos y le agrega checkpoints para tener un punto de restauracion.
Mas informacion: https://docs.percona.com/percona-xtrabackup/innovation-release/about-xtrabackup.html
7. Levantar un nodo con xtrabackup y unirlo al cluster
7.1 Agregar el nuevo nodo con backup al cluster
Editar el archivo de configuración.
nano /etc/mysql/percona-xtradb-cluster.conf.d/wsrep.cnf
Agregar la configuración:
GNU nano 4.8 /etc/mysql/percona-xtradb-cluster.conf.d/wsrep.cnf
[mysqld]
# Path to Galera library
wsrep_provider=/usr/lib/galera3/libgalera_smm.so
# Cluster connection URL contains IPs of nodes
#If no IP is found, this implies that a new cluster needs to be created,
#in order to do that you need to bootstrap this node
wsrep_cluster_address=gcomm://192.168.12.3,192.168.12.4,192.168.12.5,192.168.12.6,192.168.12.7
# In order for Galera to work correctly binlog format should be ROW
binlog_format=ROW
# MyISAM storage engine has only experimental support
default_storage_engine=InnoDB
# Slave thread to use
wsrep_slave_threads=4
wsrep_log_conflicts
# This changes how InnoDB autoincrement locks are managed and is a requirement for Galera
innodb_autoinc_lock_mode=2
# Node IP address
wsrep_node_address=192.168.12.7
# Cluster name
wsrep_cluster_name=pxc-cluster
#If wsrep_node_name is not specified, then system hostname will be used
wsrep_node_name=Node-5
#pxc_strict_mode allowed values: DISABLED,PERMISSIVE,ENFORCING,MASTER
pxc_strict_mode=PERMISSIVE
# SST method
wsrep_sst_method=xtrabackup-v2
#Authentication for SST method
#wsrep_sst_auth="sstuser:s3cretPass"
wsrep_sst_auth="sstuser:xxxxxxxxxxx"
Reiniciar mysql
service mysql stop
service mysql start
Comprobamos que se estén replicando los datos los datos:
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| companydb |
| mughees |
| mysql |
| performance_schema |
| sys |
+--------------------+
6 rows in set (0.01 sec)
mysql> use companydb;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql> select * from Companies order by CompanyID DESC LIMIT 10 \G
*************************** 1. row ***************************
CompanyID: 10081
Name: Other Company 3
Address: Unit 5699 Box 8173 DPO AP 62802
Phone: +1-635-243-2015x623
Email: jaredgill@miller.com
*************************** 2. row ***************************
CompanyID: 10078
Name: Empresa por: Node-3
Address: Unit 5699 Box 8173 DPO AP 62802
Phone: +1-635-243-2015x623
Email: jaredgill@miller.com
Al finalizar los pasos realizados, nos quedaria de la siguiente manera: