Cómo crear un clúster Percona XtraDB de 3 nodos para alta disponibilidad en MySQL

Aprende a crear un laboratorio de Percona XtraDB Cluster con tres nodos para practicar alta disponibilidad, quorum, replicación Galera, IST, SST, recuperación ante fallos y conceptos clave de Database Reliability Engineering.

Este post está disponible en: English

Percona XtraDB Cluster, comúnmente conocido como PXC, es una solución de alta disponibilidad para MySQL basada en Percona Server y Galera Cluster.

A diferencia de una arquitectura tradicional de MySQL con un nodo primario y réplicas, en Percona XtraDB Cluster todos los nodos mantienen una copia completa de la base de datos y participan en el estado del clúster.

Esto convierte a PXC en una excelente plataforma para practicar conceptos relacionados con DevOps, Site Reliability Engineering y Database Reliability Engineering, especialmente en escenarios donde necesitamos alta disponibilidad, recuperación ante fallos, mantenimiento sin downtime y consistencia entre nodos.

En este artículo construiremos un laboratorio de tres nodos de Percona XtraDB Cluster y, más importante aún, aprenderemos qué sucede cuando un nodo falla, se pierde el quorum, una réplica queda atrasada o existe una partición de red.

El objetivo no es solamente instalar Percona.

El objetivo es construir un entorno donde podamos romper cosas deliberadamente y entender cómo se comporta el clúster.


Arquitectura de Percona XtraDB Cluster

Una arquitectura básica de PXC puede verse así:

Aplicación
|
HAProxy
|
+---------+---------+
| | |
PXC01 PXC02 PXC03
MySQL MySQL MySQL
\ | /
\----- Galera ----/

Cada nodo contiene una copia completa de la base de datos.

Esto es diferente de una arquitectura tradicional de MySQL:

Primario
|
| replicación asíncrona
v
Réplica

En PXC, las transacciones se replican utilizando el mecanismo de write-set replication de Galera.

Aunque Percona XtraDB Cluster permite ejecutar escrituras en distintos nodos, esto no significa necesariamente que una aplicación deba distribuir todas las escrituras de forma aleatoria.

Una arquitectura común consiste en tener un nodo preferido para escritura y utilizar los demás como respaldo y failover:

HAProxy / ProxySQL
|
Escrituras
|
PXC01
/ \
PXC02 PXC03

Esto reduce la posibilidad de conflictos de certificación entre transacciones concurrentes.


Cómo funciona una transacción en PXC

Supongamos que una aplicación ejecuta la siguiente operación en pxc01:

BEGIN;
UPDATE accounts
SET balance = balance - 100
WHERE id = 10;
COMMIT;

La transacción primero se ejecuta localmente.

Antes de confirmar el cambio, Galera crea un write-set que contiene la información necesaria para replicar esa modificación al resto del clúster.

De forma simplificada:

PXC01
|
La transacción se ejecuta
|
v
Se genera un write-set
|
+------------+
| |
v v
PXC02 PXC03
| |
Certificación Certificación
\ /
\----------/
|
COMMIT

Cada nodo realiza un proceso llamado certificación.

La certificación permite determinar si otra transacción concurrente modificó los mismos registros.

Por ejemplo:

PXC01 PXC02
UPDATE users UPDATE users
SET name='Alice' SET name='Bob'
WHERE id=10 WHERE id=10
\ /
\ /
Certificación
|
Conflicto detectado
|
Una transacción aborta

Esta es una de las razones por las que el soporte multi-primary debe utilizarse con cuidado.

Si múltiples nodos realizan muchas escrituras simultáneas sobre los mismos datos, pueden aumentar los conflictos de certificación.


Por qué un clúster PXC debería tener tres nodos

Uno de los conceptos más importantes en Galera es el quorum.

Consideremos un clúster de tres nodos:

PXC01 ---- PXC02 ---- PXC03
1 1 1

Para continuar operando de forma segura, el clúster necesita mantener una mayoría de nodos disponibles.

Con tres nodos:

3 nodos disponibles → hay quorum
2 nodos disponibles → hay quorum
1 nodo disponible → no hay quorum

Si un nodo falla:

PXC01 -------- PXC02 X PXC03

Los dos nodos restantes representan la mayoría del clúster, por lo que el servicio continúa funcionando.

Sin embargo, si también falla pxc02:

PXC01
X PXC02
X PXC03

pxc01 queda aislado y ya no tiene quorum.

En ese escenario podemos encontrar estados como:

wsrep_cluster_status = Non-Primary
wsrep_ready = OFF

Aunque el proceso de MySQL siga corriendo, el nodo ya no debe considerarse saludable para tráfico de aplicación.

Este comportamiento ayuda a prevenir uno de los problemas más peligrosos en sistemas distribuidos:

split brain.

Sin protección por quorum, dos partes separadas de un clúster podrían creer que son la copia autoritativa de la base de datos y aceptar escrituras diferentes.


IST y SST

Cuando un nodo permanece offline durante cierto tiempo, necesita sincronizarse antes de volver a participar normalmente en el clúster.

Supongamos que inicialmente tenemos:

PXC01 PXC02 PXC03
✓ ✓ ✓

Después pxc03 falla:

PXC01 PXC02 PXC03
✓ ✓ X

Mientras pxc03 está fuera, las aplicaciones continúan generando transacciones.

Cuando el nodo regresa, debe actualizar su estado.

Existen dos mecanismos principales para hacerlo.


Incremental State Transfer — IST

Si otro nodo conserva en su GCache las transacciones que pxc03 perdió, únicamente se envían esos cambios.

PXC01 GCache
TX1001
TX1002
TX1003
TX1004
TX1005
|
| transacciones faltantes
v
PXC03

Esto se conoce como:

Incremental State Transfer o IST.

IST normalmente es rápido porque solamente replica las transacciones faltantes.


State Snapshot Transfer — SST

Si el nodo estuvo offline demasiado tiempo y las transacciones necesarias ya no existen en GCache, será necesario transferir una copia completa del estado de la base de datos.

PXC01
Base de datos completa
|
|
v
PXC03

Este proceso se conoce como:

State Snapshot Transfer o SST.

Un SST puede consumir considerablemente más recursos que un IST, especialmente:

CPU
Disco
I/O
Red
Tiempo

Por eso es importante entender conceptos como:

IST
SST
GCache
Donor
Joiner

cuando se administra un clúster PXC.


Diseño del laboratorio

Para este laboratorio utilizaremos cuatro máquinas virtuales:

Servidor Dirección IP Propósito
pxc01 192.168.50.11 Nodo Percona
pxc02 192.168.50.12 Nodo Percona
pxc03 192.168.50.13 Nodo Percona
mysql-proxy 192.168.50.10 HAProxy

Para cada nodo de base de datos podemos utilizar:

2 vCPU
4 GB RAM
30–50 GB de disco
Ubuntu 24.04

Utilizar máquinas virtuales en lugar de contenedores tiene una ventaja importante para este laboratorio: podemos reproducir fallos de infraestructura más realistas.

Por ejemplo:

Particiones de red
Caídas de VM
Disco lleno
Problemas de firewall
Latencia de almacenamiento
Reinicios del sistema operativo

Puertos necesarios

Los nodos PXC utilizan varios puertos para comunicarse:

3306 conexiones MySQL
4444 State Snapshot Transfer
4567 replicación Galera
4568 Incremental State Transfer

Si utilizamos UFW:

sudo ufw allow 3306/tcp
sudo ufw allow 4444/tcp
sudo ufw allow 4567/tcp
sudo ufw allow 4567/udp
sudo ufw allow 4568/tcp

En un ambiente real, estas reglas deberían limitarse únicamente a la red donde viven los nodos del clúster.


Configurar los hostnames

En el primer nodo:

sudo hostnamectl set-hostname pxc01

En el segundo:

sudo hostnamectl set-hostname pxc02

En el tercero:

sudo hostnamectl set-hostname pxc03

Después agregamos los nodos a /etc/hosts:

192.168.50.11 pxc01
192.168.50.12 pxc02
192.168.50.13 pxc03

Antes de continuar, debemos verificar que todos los nodos puedan comunicarse entre sí.


Instalar Percona XtraDB Cluster

Ejecutaremos el proceso de instalación en los tres nodos.

sudo apt update

Instalamos algunas dependencias:

sudo apt install -y \
wget \
gnupg2 \
lsb-release \
curl

Descargamos el repositorio de Percona:

wget https://repo.percona.com/apt/percona-release_latest.generic_all.deb

Lo instalamos:

sudo dpkg -i percona-release_latest.generic_all.deb

Habilitamos el repositorio de PXC:

sudo percona-release setup pxc-84-lts

Actualizamos la información de paquetes:

sudo apt update

Instalamos Percona XtraDB Cluster:

sudo apt install -y percona-xtradb-cluster

Una vez instalado, detenemos MySQL en los tres nodos:

sudo systemctl stop mysql

Ahora podemos configurar el clúster.


Configurar PXC01

Primero revisamos dónde se encuentran los archivos de configuración:

ls -la /etc/mysql/
ls -la /etc/mysql/mysql.conf.d/

Creamos:

sudo nano /etc/mysql/mysql.conf.d/pxc.cnf

Para pxc01:

[mysqld]
server-id=1
datadir=/var/lib/mysql
user=mysql
default_storage_engine=InnoDB
innodb_autoinc_lock_mode=2
wsrep_provider=/usr/lib/libgalera_smm.so
wsrep_cluster_name=pxc-lab
wsrep_cluster_address=gcomm://192.168.50.11,192.168.50.12,192.168.50.13
wsrep_node_name=pxc01
wsrep_node_address=192.168.50.11
wsrep_sst_method=clone

Los parámetros más importantes son:

wsrep_cluster_name
wsrep_cluster_address
wsrep_node_name
wsrep_node_address

Configurar PXC02

En el segundo nodo:

[mysqld]
server-id=2
datadir=/var/lib/mysql
user=mysql
default_storage_engine=InnoDB
innodb_autoinc_lock_mode=2
wsrep_provider=/usr/lib/libgalera_smm.so
wsrep_cluster_name=pxc-lab
wsrep_cluster_address=gcomm://192.168.50.11,192.168.50.12,192.168.50.13
wsrep_node_name=pxc02
wsrep_node_address=192.168.50.12
wsrep_sst_method=clone

Configurar PXC03

Finalmente:

[mysqld]
server-id=3
datadir=/var/lib/mysql
user=mysql
default_storage_engine=InnoDB
innodb_autoinc_lock_mode=2
wsrep_provider=/usr/lib/libgalera_smm.so
wsrep_cluster_name=pxc-lab
wsrep_cluster_address=gcomm://192.168.50.11,192.168.50.12,192.168.50.13
wsrep_node_name=pxc03
wsrep_node_address=192.168.50.13
wsrep_sst_method=clone

Bootstrap del clúster

El primer nodo debe arrancarse de manera diferente.

En pxc01:

sudo systemctl start mysql@bootstrap

Con esto indicamos que este nodo está creando el Primary Component inicial del clúster.

Nos conectamos:

mysql -uroot -p

Y revisamos:

SHOW STATUS LIKE 'wsrep_cluster_size';
SHOW STATUS LIKE 'wsrep_cluster_status';
SHOW STATUS LIKE 'wsrep_local_state_comment';
SHOW STATUS LIKE 'wsrep_ready';

Un nodo saludable debería mostrar:

wsrep_cluster_size 1
wsrep_cluster_status Primary
wsrep_local_state_comment Synced
wsrep_ready ON

Agregar PXC02

En el segundo nodo iniciamos MySQL normalmente:

sudo systemctl start mysql

Podemos seguir los logs con:

journalctl -u mysql -f

Cuando termine la sincronización:

SHOW STATUS LIKE 'wsrep_cluster_size';

Deberíamos obtener:

2

Y:

SHOW STATUS LIKE 'wsrep_local_state_comment';

Debería mostrar:

Synced

Agregar PXC03

En el tercer nodo:

sudo systemctl start mysql

Verificamos nuevamente:

SHOW STATUS LIKE 'wsrep_cluster_size';

Ahora deberíamos obtener:

3

También verificamos:

SHOW STATUS LIKE 'wsrep_cluster_status';

El resultado esperado es:

Primary

Nuestro clúster de tres nodos ya está funcionando.


Probar la replicación

En pxc01 creamos una base de datos:

CREATE DATABASE dbre_lab;
USE dbre_lab;

Después una tabla:

CREATE TABLE transactions (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
description VARCHAR(255),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

Insertamos un registro:

INSERT INTO transactions(description)
VALUES ('Insertado desde pxc01');

Ahora nos conectamos a pxc02:

SELECT *
FROM dbre_lab.transactions;

El registro debería existir.

Después insertamos desde pxc02:

INSERT INTO dbre_lab.transactions(description)
VALUES ('Insertado desde pxc02');

Finalmente consultamos desde pxc03:

SELECT *
FROM dbre_lab.transactions;

Ambos registros deberían aparecer.


Métricas WSREP importantes

Podemos obtener todas las métricas usando:

SHOW STATUS LIKE 'wsrep%';

Sin embargo, para troubleshooting cotidiano es mejor enfocarse en algunas variables:

SHOW STATUS WHERE Variable_name IN (
'wsrep_cluster_size',
'wsrep_cluster_status',
'wsrep_connected',
'wsrep_ready',
'wsrep_local_state_comment',
'wsrep_local_recv_queue',
'wsrep_flow_control_paused',
'wsrep_local_cert_failures',
'wsrep_local_bf_aborts'
);

Un clúster saludable de tres nodos debería aproximarse a:

wsrep_cluster_size 3
wsrep_cluster_status Primary
wsrep_connected ON
wsrep_ready ON
wsrep_local_state_comment Synced

Estas métricas deberían formar parte de cualquier sistema de monitoreo del clúster.


Ejercicio 1: perder un nodo

Detenemos pxc03:

sudo systemctl stop mysql

Desde otro nodo:

SHOW STATUS LIKE 'wsrep_cluster_size';

El resultado debería ser:

2

Todavía tenemos quorum.

Probamos una escritura:

INSERT INTO dbre_lab.transactions(description)
VALUES ('Transacción con PXC03 fuera de servicio');

La operación debería completarse correctamente.

Después levantamos nuevamente el nodo:

sudo systemctl start mysql

Y observamos:

journalctl -u mysql -f

Ejercicio 2: observar un IST

Detenemos nuevamente pxc03:

sudo systemctl stop mysql

Generamos algunas transacciones en pxc01:

INSERT INTO dbre_lab.transactions(description)
VALUES ('PXC03 está offline');

Después iniciamos nuevamente pxc03:

sudo systemctl start mysql

Revisamos:

journalctl -u mysql

Y buscamos referencias a:

IST

Si las transacciones necesarias todavía existen en GCache, el nodo podrá recuperar únicamente los cambios que perdió.


Ejercicio 3: forzar un SST

Ahora podemos intentar que IST no sea posible.

La estrategia es:

1. Detener PXC03
2. Generar una gran cantidad de datos
3. Superar el espacio disponible en GCache
4. Iniciar nuevamente PXC03

El nodo debería necesitar un State Snapshot Transfer completo.

Durante este ejercicio conviene monitorear:

Duración del SST
CPU
I/O de disco
Tráfico de red
Carga sobre el donor
Estado del joiner
Latencia de aplicación

Ejercicio 4: perder el quorum

Comenzamos con:

PXC01 ✓
PXC02 ✓
PXC03 ✓

Detenemos pxc02:

sudo systemctl stop mysql

Todavía tenemos quorum.

Después detenemos pxc03:

sudo systemctl stop mysql

Ahora solamente queda pxc01.

Consultamos:

SHOW STATUS LIKE 'wsrep_cluster_status';
SHOW STATUS LIKE 'wsrep_ready';

Este ejercicio demuestra algo importante:

MySQL está ejecutándose

no significa necesariamente que:

La base de datos está lista para recibir tráfico

Un nodo que perdió quorum no debería considerarse saludable.


Ejercicio 5: crear una partición de red

También podemos simular problemas de comunicación.

Por ejemplo, podemos bloquear temporalmente el tráfico de Galera:

sudo iptables -A INPUT -p tcp --dport 4567 -j DROP
sudo iptables -A OUTPUT -p tcp --dport 4567 -j DROP

Después observamos:

SHOW STATUS LIKE 'wsrep%';

En particular:

wsrep_cluster_size
wsrep_cluster_status
wsrep_connected
wsrep_ready

Para revertir:

sudo iptables -D INPUT -p tcp --dport 4567 -j DROP
sudo iptables -D OUTPUT -p tcp --dport 4567 -j DROP

Este escenario ayuda a comprender que una caída de proceso y una partición de red son eventos muy diferentes.


Agregar HAProxy

Nuestro siguiente paso es evitar que las aplicaciones se conecten directamente a un nodo específico.

La arquitectura puede evolucionar a:

Aplicación
|
v
+---------------+
| HAProxy |
| 192.168.50.10 |
+-------+-------+
|
+------------+------------+
| | |
v v v
+-------+ +-------+ +-------+
| PXC01 | | PXC02 | | PXC03 |
+-------+ +-------+ +-------+

Instalamos HAProxy:

sudo apt update
sudo apt install -y haproxy

Aquí existe una consideración muy importante.

Comprobar únicamente que el puerto 3306 esté abierto no es suficiente.

Podemos tener:

mysqld ejecutándose
3306 escuchando

pero al mismo tiempo:

wsrep_ready = OFF

Ese nodo no debería recibir tráfico.

Por eso el health check debería ser consciente del estado de PXC y no limitarse a verificar conectividad TCP.


Monitorear el clúster

El siguiente paso natural es agregar monitoreo.

Percona Monitoring and Management, conocido como PMM, es una opción excelente para este laboratorio.

La arquitectura final podría verse así:

Aplicación
|
HAProxy
|
+------------+------------+
| | |
PXC01 PXC02 PXC03
| | |
+------------+------------+
|
PMM

Algunas métricas importantes son:

CPU
Memoria
Latencia de disco
Buffer pool
Conexiones
Latencia de queries
Slow queries
Flow control
Replication queues
Errores de certificación
SST
IST

Particularmente:

wsrep_local_recv_queue
wsrep_flow_control_paused
wsrep_local_cert_failures
wsrep_local_bf_aborts

pueden ofrecernos mucha más información que simplemente revisar si MySQL está activo.


Convertir el laboratorio en un entorno de práctica DBRE

Instalar Percona solamente es el primer paso.

El verdadero aprendizaje comienza cuando operamos y rompemos el clúster.

Una buena ruta de aprendizaje sería:

Etapa 1 — Build

Construir los tres nodos manualmente.

Entender cada parámetro antes de automatizarlo.

Etapa 2 — Operate

Agregar:

HAProxy
ProxySQL
PMM
Backups
Monitoring
Alertas

Etapa 3 — Break

Simular:

Falla de nodo
Falla de VM
Partición de red
Disco lleno
Alta latencia
Error de firewall
Error de configuración

Etapa 4 — Recover

Practicar:

IST
SST
Recuperación de quorum
Reemplazo de nodo
Recuperación completa del clúster

Etapa 5 — Maintain

Practicar:

Rolling restarts
Actualizaciones del sistema operativo
Upgrades de base de datos
Cambios de configuración
Rotación de certificados

sin generar downtime completo.

Etapa 6 — Automate

Después de entender el proceso manual, reconstruir todo utilizando herramientas como:

Terraform
Ansible
GitHub Actions

La infraestructura debería poder ser creada nuevamente desde cero.

Etapa 7 — Observe

Crear dashboards y alertas para:

Estado WSREP
Flow control
Conflictos de certificación
Sincronización
Latencia
I/O de disco
SST
IST

Etapa 8 — Document

Finalmente crear runbooks reales.

Por ejemplo:

Nodo PXC caído
Clúster Non-Primary
Nodo no puede unirse al clúster
SST bloqueado
Disco lleno
Flow control elevado
Rolling restart
Rolling upgrade
Caída completa del clúster

Recuperación completa del clúster

Existe un escenario especialmente importante:

los tres nodos están apagados.

PXC01 X
PXC02 X
PXC03 X

No deberíamos elegir un nodo al azar y realizar el bootstrap.

Cada nodo puede contener una posición de Galera diferente dependiendo de las últimas transacciones que alcanzó a procesar.

Durante la recuperación podemos utilizar:

mysqld --wsrep-recover

para investigar la posición de cada nodo.

El objetivo es identificar el nodo con el estado más avanzado y confiable antes de iniciar nuevamente el Primary Component.

Elegir un nodo con un estado más antiguo podría provocar pérdida de transacciones.

Por esta razón, la recuperación completa de un clúster debería ser uno de los ejercicios principales de cualquier laboratorio PXC.


Qué podemos aprender con este laboratorio

Un laboratorio de Percona XtraDB Cluster permite estudiar mucho más que la instalación de MySQL.

Podemos experimentar directamente con conceptos de sistemas distribuidos como:

Quorum
Consistencia
Write-set replication
Certificación de transacciones
Prevención de split brain
Replicación síncrona
Failure domains
State synchronization
Flow control
Failover

También refuerza una idea fundamental de Reliability Engineering:

Que el proceso de MySQL esté ejecutándose no significa que el servicio de base de datos esté saludable.

Un servidor puede tener MySQL activo y el puerto 3306 disponible, pero estar desconectado del Primary Component o no encontrarse en condiciones seguras para procesar tráfico.

Por eso la observabilidad, los health checks, el quorum y los mecanismos de recuperación son componentes fundamentales de una plataforma de bases de datos altamente disponible.


Conclusión

Percona XtraDB Cluster es una excelente plataforma para aprender cómo funcionan los ambientes MySQL altamente disponibles.

Un laboratorio de tres nodos nos permite experimentar de forma segura con escenarios que serían demasiado riesgosos para reproducir directamente en producción.

Después de completar la instalación, el siguiente paso debería ser romper el clúster deliberadamente.

Detén un nodo.

Corta la comunicación de red.

Fuerza un SST.

Pierde el quorum.

Llena el disco.

Reinicia todos los servidores.

Recupera el clúster.

Después automatiza el proceso y vuelve a repetirlo.

El objetivo no es solamente aprender a instalar Percona.

El objetivo es comprender qué sucede cuando la infraestructura de base de datos deja de comportarse de la manera que esperamos.

Ahí es donde este laboratorio comienza a convertirse en una verdadera práctica de Database Reliability Engineering.

Comentarios