MySQL Group Replication es el mecanismo de alta disponibilidad integrado en MySQL. Varios servidores forman un grupo que acuerda el orden de cada transacción mediante un protocolo de consenso, de modo que todos aplican los mismos cambios en el mismo orden. En modo primario único, un miembro acepta escrituras y el resto son réplicas de solo lectura; si el primario cae, el grupo elige otro automáticamente.

En este tutorial configurarás un grupo de tres nodos con MySQL 8.0 desde los repositorios de Ubuntu 24.04, en modo primario único. Comprobarás la replicación, provocarás un failover, cambiarás el primario a mano y verás cómo añadir y retirar miembros.

Requisitos previos

  • Tres servidores con Ubuntu 24.04 LTS, por ejemplo VPS de CubePath, con al menos 2 GB de RAM y un usuario no root con sudo.
  • Red privada con baja latencia entre ellos. Cada transacción necesita el acuerdo de la mayoría del grupo antes de confirmarse.
  • Tres miembros como mínimo (el grupo tolera la caída de uno) y nueve como máximo.

Valores usados en los ejemplos:

NodoIP privada
mysql110.0.0.11
mysql210.0.0.12
mysql310.0.0.13

Puertos entre nodos:

PuertoUso
3306/tcpClientes y recuperación de datos entre miembros
33061/tcpComunicación interna del grupo

Paso 1: Instalar MySQL en los tres nodos

Ejecuta en cada nodo:

sudo apt update
sudo apt install mysql-server

Comprueba la versión y que el plugin de Group Replication está disponible:

mysql --version
ls /usr/lib/mysql/plugin/group_replication.so
mysql  Ver 8.0.43-0ubuntu0.24.04.1 for Linux on x86_64 ((Ubuntu))
/usr/lib/mysql/plugin/group_replication.so

Abre los puertos solo para la red privada:

sudo ufw allow from 10.0.0.0/24 to any port 3306,33061 proto tcp

Paso 2: Configurar cada servidor para Group Replication

Genera un UUID que identificará al grupo. Hazlo una sola vez y usa el mismo valor en los tres nodos:

cat /proc/sys/kernel/random/uuid
aaaaaaaa-bbbb-4ccc-8ddd-eeeeeeeeeeee

MySQL en Ubuntu lee los ficheros de /etc/mysql/mysql.conf.d/ en orden alfabético, y los posteriores prevalecen. Crea un fichero que se cargue después de mysqld.cnf para que su bind-address sustituya al valor por defecto (127.0.0.1). En mysql1:

sudo nano /etc/mysql/mysql.conf.d/zz-group-replication.cnf
[mysqld]
# Identidad del servidor
server_id                          = 1
bind-address                       = 0.0.0.0
report_host                        = 10.0.0.11

# Requisitos de Group Replication
gtid_mode                          = ON
enforce_gtid_consistency           = ON
disabled_storage_engines           = "MyISAM,BLACKHOLE,FEDERATED,ARCHIVE,MEMORY"

# Plugin y grupo
plugin_load_add                    = group_replication.so
group_replication_group_name       = "aaaaaaaa-bbbb-4ccc-8ddd-eeeeeeeeeeee"
group_replication_start_on_boot    = OFF
group_replication_local_address    = "10.0.0.11:33061"
group_replication_group_seeds      = "10.0.0.11:33061,10.0.0.12:33061,10.0.0.13:33061"
group_replication_bootstrap_group  = OFF

# Cifrado del tráfico del grupo y de la recuperación
group_replication_ssl_mode         = REQUIRED
group_replication_recovery_use_ssl = ON

Qué hace cada bloque:

  • server_id debe ser distinto en cada nodo. report_host es la dirección con la que el miembro se anuncia al resto; usar la IP evita depender de la resolución de nombres.
  • Group Replication exige GTID e InnoDB. disabled_storage_engines impide crear por error tablas en motores que no se replican. El log binario, el formato ROW y log_replica_updates ya vienen activos por defecto en MySQL 8.0.
  • group_replication_group_seeds son los miembros a los que un nodo intenta conectarse para unirse. group_replication_local_address es la dirección de este nodo para la comunicación interna (puerto 33061, distinto del 3306).
  • group_replication_start_on_boot = OFF evita que el nodo intente unirse antes de que el grupo exista. Lo activarás en el paso 7.
  • Las dos opciones de SSL usan los certificados que MySQL genera automáticamente al instalarse, suficiente para cifrar el tráfico dentro de una red privada.

En mysql2 y mysql3 crea el mismo fichero cambiando solo estas tres líneas:

# En mysql2
server_id                          = 2
report_host                        = 10.0.0.12
group_replication_local_address    = "10.0.0.12:33061"
# En mysql3
server_id                          = 3
report_host                        = 10.0.0.13
group_replication_local_address    = "10.0.0.13:33061"

Reinicia MySQL en cada nodo y comprueba que el plugin está activo:

sudo systemctl restart mysql
sudo mysql -e "SELECT PLUGIN_NAME, PLUGIN_STATUS FROM information_schema.PLUGINS WHERE PLUGIN_NAME = 'group_replication';"
+-------------------+---------------+
| PLUGIN_NAME       | PLUGIN_STATUS |
+-------------------+---------------+
| group_replication | ACTIVE        |
+-------------------+---------------+

Paso 3: Crear el usuario de recuperación

Cuando un miembro se une, copia de otro las transacciones que le faltan a través del canal group_replication_recovery, con un usuario de replicación. Créalo en cada nodo con el log binario desactivado para esta sesión, de modo que la creación del usuario no genere transacciones locales distintas en cada servidor:

sudo mysql
SET SQL_LOG_BIN = 0;
CREATE USER 'rpl_user'@'%' IDENTIFIED BY 'your_strong_password';
GRANT REPLICATION SLAVE, CONNECTION_ADMIN, BACKUP_ADMIN, GROUP_REPLICATION_STREAM ON *.* TO 'rpl_user'@'%';
SET SQL_LOG_BIN = 1;

CHANGE REPLICATION SOURCE TO
  SOURCE_USER = 'rpl_user',
  SOURCE_PASSWORD = 'your_strong_password'
  FOR CHANNEL 'group_replication_recovery';
EXIT;

Usa la misma contraseña en los tres nodos y sustituye your_strong_password por una segura.

Paso 4: Arrancar el grupo en el primer nodo

El primer miembro debe crear el grupo (bootstrap). Esto se hace solo en un nodo y solo una vez; después se desactiva para que no pueda crear un segundo grupo por accidente. En mysql1:

sudo mysql
SET GLOBAL group_replication_bootstrap_group = ON;
START GROUP_REPLICATION;
SET GLOBAL group_replication_bootstrap_group = OFF;

SELECT MEMBER_HOST, MEMBER_STATE, MEMBER_ROLE
FROM performance_schema.replication_group_members;
+-------------+--------------+-------------+
| MEMBER_HOST | MEMBER_STATE | MEMBER_ROLE |
+-------------+--------------+-------------+
| 10.0.0.11   | ONLINE       | PRIMARY     |
+-------------+--------------+-------------+

Paso 5: Unir los otros dos nodos

En mysql2 y después en mysql3:

sudo mysql -e "START GROUP_REPLICATION;"

El nodo pasa por el estado RECOVERING mientras copia las transacciones que le faltan y después queda ONLINE. Comprueba el grupo desde cualquier miembro:

sudo mysql -e "SELECT MEMBER_HOST, MEMBER_STATE, MEMBER_ROLE FROM performance_schema.replication_group_members;"
+-------------+--------------+-------------+
| MEMBER_HOST | MEMBER_STATE | MEMBER_ROLE |
+-------------+--------------+-------------+
| 10.0.0.11   | ONLINE       | PRIMARY     |
| 10.0.0.12   | ONLINE       | SECONDARY   |
| 10.0.0.13   | ONLINE       | SECONDARY   |
+-------------+--------------+-------------+

Paso 6: Probar la replicación

En el primario (mysql1), crea una base de datos y una tabla. Group Replication exige que todas las tablas tengan clave primaria:

sudo mysql -e "CREATE DATABASE gr_test; CREATE TABLE gr_test.t (id INT PRIMARY KEY, origen VARCHAR(20)); INSERT INTO gr_test.t VALUES (1, 'mysql1');"

Léela en un secundario (mysql3):

sudo mysql -e "SELECT * FROM gr_test.t;"
+----+--------+
| id | origen |
+----+--------+
|  1 | mysql1 |
+----+--------+

Intenta escribir en el secundario. Debe fallar, porque en modo primario único los secundarios se ponen en super_read_only:

sudo mysql -e "INSERT INTO gr_test.t VALUES (2, 'mysql3');"
ERROR 1290 (HY000) at line 1: The MySQL server is running with the --super-read-only option so it cannot execute this statement

Paso 7: Unirse automáticamente al arrancar

Ahora que el grupo existe, activa group_replication_start_on_boot para que un nodo que se reinicie vuelva a unirse solo. En cada nodo, cambia la línea en /etc/mysql/mysql.conf.d/zz-group-replication.cnf:

group_replication_start_on_boot    = ON

El cambio se aplicará en el próximo reinicio de cada nodo; no hace falta reiniciar ahora.

Paso 8: Probar el failover automático

Para MySQL en el primario (mysql1):

sudo systemctl stop mysql

Desde mysql2, comprueba el grupo. En pocos segundos uno de los secundarios pasa a primario:

sudo mysql -e "SELECT MEMBER_HOST, MEMBER_STATE, MEMBER_ROLE FROM performance_schema.replication_group_members;"
+-------------+--------------+-------------+
| MEMBER_HOST | MEMBER_STATE | MEMBER_ROLE |
+-------------+--------------+-------------+
| 10.0.0.12   | ONLINE       | PRIMARY     |
| 10.0.0.13   | ONLINE       | SECONDARY   |
+-------------+--------------+-------------+

El nuevo primario desactiva super_read_only y acepta escrituras. Vuelve a arrancar MySQL en mysql1:

sudo systemctl start mysql

Con group_replication_start_on_boot = ON, se une de nuevo como SECONDARY y recupera las transacciones que se perdió.

Paso 9: Cambiar el primario y gestionar miembros

Para devolver el rol de primario a mysql1 (por ejemplo, porque tiene más recursos), obtén su UUID de miembro:

sudo mysql -e "SELECT MEMBER_ID, MEMBER_HOST, MEMBER_ROLE FROM performance_schema.replication_group_members;"

Y ejecuta desde cualquier miembro, con el MEMBER_ID de mysql1:

sudo mysql -e "SELECT group_replication_set_as_primary('member_id_de_mysql1');"

Para retirar un miembro de forma ordenada (por ejemplo, antes de actualizarlo), ejecuta en ese nodo:

sudo mysql -e "STOP GROUP_REPLICATION;"

El resto del grupo sigue funcionando. Cuando termines, vuelve a unirlo con START GROUP_REPLICATION;. Para añadir un cuarto nodo, repite los pasos 1 a 3 en él con un server_id nuevo, añade su dirección a group_replication_group_seeds en todos los nodos y ejecuta START GROUP_REPLICATION;.

Paso 10: Monitorizar el grupo

Además del estado de los miembros, performance_schema.replication_group_member_stats muestra si algún nodo acumula transacciones pendientes de aplicar o de certificar:

sudo mysql -e "SELECT MEMBER_ID, COUNT_TRANSACTIONS_IN_QUEUE, COUNT_TRANSACTIONS_REMOTE_IN_APPLIER_QUEUE, COUNT_CONFLICTS_DETECTED FROM performance_schema.replication_group_member_stats\G"

Valores de cola que crecen de forma sostenida indican que un miembro va más lento que el resto. Si se retrasa demasiado, el control de flujo del grupo frena las escrituras en el primario para que pueda alcanzarlo.

Solución de problemas

This member has more executed transactions than those present in the group: el nodo tiene transacciones locales que el grupo no conoce, normalmente por crear usuarios o bases de datos sin SET SQL_LOG_BIN = 0. En un nodo recién instalado y sin datos propios, puedes limpiar su historial GTID con RESET MASTER; y volver a ejecutar START GROUP_REPLICATION;. No lo hagas en un nodo con datos que no existan en el grupo.

El nodo se queda en RECOVERING o pasa a ERROR: revisa el log de errores con sudo tail -n 50 /var/log/mysql/error.log. Las causas habituales son credenciales incorrectas en el canal group_replication_recovery, el puerto 3306 cerrado entre nodos o un report_host al que los demás no llegan.

START GROUP_REPLICATION falla con errores de conexión al puerto 33061: comprueba el firewall y que group_replication_local_address usa la IP de ese nodo. Por defecto, MySQL solo acepta miembros desde redes privadas (group_replication_ip_allowlist = AUTOMATIC); si tus nodos usan IP públicas, define esa variable con las direcciones de los miembros.

Error al crear tablas sin clave primaria: Group Replication rechaza las tablas InnoDB sin clave primaria o única no nula. Añade una clave primaria a la tabla.

Conclusión

Tienes un grupo MySQL de tres nodos en Ubuntu 24.04 en modo primario único, con tráfico cifrado entre miembros, failover automático y los procedimientos para cambiar el primario y retirar o añadir nodos. Como siguientes pasos puedes:

  • Instalar MySQL Router delante del grupo para que las aplicaciones se conecten siempre al primario actual sin cambiar su configuración.
  • Programar copias de seguridad desde un secundario con mysqldump --single-transaction o Percona XtraBackup.
  • Añadir alertas sobre los miembros que no estén ONLINE y sobre el crecimiento de las colas de transacciones.