MariaDB Galera Cluster en Contenedores Docker

Introducción

Vamos a construir un Cluster de Galera de tres nodos, 1 Primario y 2 Nodos, usando contenedores Docker en una virtual machine de la Google Cloud Platform. Previamente habiamos instalado y configurado un MariaDB Galera Cluster en Instancias Virtuales, esta vez lo vamos a construir usando contenedores.

Prerrequisitos

Un anfitrión de Docker en cualquier Sistema Operativo. Nosotros estamos usando una virtual machine instance en Google Cloud Platform con Container Optimized OS el cual ya incluye el Docker 19.03.

Contenedores para Cluster de Galera

Crear un Red de Docker

Con el fin de facilitar la referencia entre los nodos del cluster vamos a usar nuestra propia red de docker tipo bridge previamente creada para que cada uno de los contenedores puedan encontrarse a través de su nombre:

pato@patocontainer ~ $ docker network create --driver bridge pato-net
522c4b821356e3b574f9150653adc325d5cb680b6d4fc7f64121d8fc9aa97530

pato@patocontainer ~ $ docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
...
522c4b821356        pato-net            bridge              local

Generar el Contenedor Primario a partir de nuestra Imagen Customizada

Creemos nuestro contenedor con el comando docker run usando nuestra imagen personalizada patomx/patomariadb. Para más información sobre esta imagen visita Mi Imagen y Volumen de MariaDB en Docker.

Usaremos la red previamente creada --network pato-net y le asignaremos una etiqueta al volumen para el datadir -v patovolgm:/var/lib/mysql.

pato@patocontainer ~ $ docker run -d --name patomariagm --network pato-net -v patovolgm:/var/lib/mysql patomx/patomariadb
7cb4db2c49a927404ebd26ed0a7c9cbbdae8aa07374bcc4de222249bbaa288c9

pato@patocontainer ~ $ docker stop patomariagm
patomariagm

Una vez creado replicaremos los datos de nuestra base de datos customizada hacia el volumen del nuevo contenedor:

pato@patocontainer ~ $ docker run --rm -i -t -v patovolmariadb:/origen -v patovolgm:/destino alpine sh -c "cp -avr /origen/* /destino"
'/origen/aria_log.00000001' -> '/destino/aria_log.00000001'
'/origen/aria_log_control' -> '/destino/aria_log_control'
...

Iniciemos el contenedor y validemos que esté en ejecución:

pato@patocontainer ~ $ docker start patomariagm
patomariagm

pato@patocontainer ~ $ docker container ls
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
7cb4db2c49a9        patomx/patomariadb  "docker-entrypoint.s…"   12 seconds ago      Up 10 seconds       3306/tcp            patomariagm

pato@patocontainer ~ $ docker volume ls
DRIVER              VOLUME NAME
local               patovolgm
local               patovolmariadb

Crear Objetos y Datos

Se espera que el nodo Primario sea el origen de los datos que queremos replicar en el cluster así que creemos algunos objetos y datos para nuestra prueba.

Accede a nuestro recién creado contenedor con una sesión shell usando docker exec -it patomariagm bash.

pato@patocontainer ~ $ docker exec -it patomariagm bash

y entonces conéctate a MariaDB para crear una base de datos, una tabla e insertar un registro con la identificación del hostname y la hora:

root@7cb4db2c49a9:/# mysql

MariaDB [(none)]> create database taller;
Query OK, 1 row affected (0.000 sec)

MariaDB [(none)]> use taller
Database changed
MariaDB [taller]> create table tg1 (i1 int auto_increment primary key, c2 varchar(20), d3 datetime) ;
Query OK, 0 rows affected (0.056 sec)

MariaDB [taller]> insert into tg1 (c2,d3) values (@@hostname,now());
Query OK, 1 row affected (0.005 sec)

Construir los Nodos Secundarios para el Cluster

Creemos otros dos contenedores a partir de la misma imagen y validemos que estén en ejecución y que los volúmenes fueron creados:

pato@patocontainer ~ $ docker run -d --name patomariag1 --network pato-net -v patovolg1:/var/lib/mysql patomx/patomariadb
5f9ce8bc16d24f59bc25633faf538d5fe5d4886824b51ced1fdc55b87f0c4c2a

pato@patocontainer ~ $ docker run -d --name patomariag2 --network pato-net -v patovolg2:/var/lib/mysql patomx/patomariadb
18dda3dabdf9d867ee0d0289edc8417518c9171ee4890f19f285aef56cc0dbc6

pato@patocontainer ~ $ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
18dda3dabdf9        patomx/patomariadb  "docker-entrypoint.s…"   12 seconds ago      Up 7 seconds        3306/tcp            patomariag2
5f9ce8bc16d2        patomx/patomariadb  "docker-entrypoint.s…"   22 seconds ago      Up 18 seconds       3306/tcp            patomariag1
7cb4db2c49a9        patomx/patomariadb  "docker-entrypoint.s…"   59 minutes ago      Up 59 minutes       3306/tcp            patomariagm

pato@patocontainer ~ $ docker volume ls
DRIVER              VOLUME NAME
local               patovolg1
local               patovolg2
local               patovolgm

Probar la Conectividad

Accede al contenedor Primario y haz ping a los otros usando el nombre asignado a cada contenedor:

pato@patocontainer ~ $ docker exec -it patomariagm bash
root@7cb4db2c49a9:/# ping patomariag1
PING patomariag1 (172.19.0.3) 56(84) bytes of data.
64 bytes from patomariag1.pato-net (172.19.0.3): icmp_seq=1 ttl=64 time=0.052 ms
...
root@7cb4db2c49a9:/# ping patomariag2
PING patomariag2 (172.19.0.4) 56(84) bytes of data.
64 bytes from patomariag2.pato-net (172.19.0.4): icmp_seq=1 ttl=64 time=0.079 ms

Detener los Contenedores

Envía docker stop para detener las bases de datos y valida que ninguna está en ejecución:

pato@patocontainer ~ $ docker stop patomariagm patomariag1 patomariag2
patomariagm
patomariag1
patomariag2
pato@patocontainer ~ $ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
pato@patocontainer ~ $ 

Configuración del Cluster de Galera

Ahora que tenemos nuestros 3 contenedores listos necesitamos configurarlos para la replicación de Galera.

Crear los Archivos de Configuración

Necesitamos crear un nuevo archivo de configuración de Galera para el nodo Primario:

pato@patocontainer ~/galera $ vi galera-init.cnf

y agregar las siguientes opciones:

  • Activar la replicación y definir la librería
[mariadb]
# Galera Replication Configuration
wsrep_on=ON
wsrep_provider=/usr/lib/galera/libgalera_smm.so
  • Definir el nombre del Cluster y listar las direcciones de los miembros
# Galera Cluster Name and Members
wsrep_cluster_name="patocluster"
wsrep_cluster_address="gcomm://"
# para el nodo primario la lista debe estar vacía para inicializar un nuevo cluster
  • Identificar a este miembro del Cluster
# Galera Cluster Member
wsrep_node_name="patomain"
wsrep_node_address="patomariagm"
  • Definir el método para la copia inicial de datos
# Galera State Snapshot Transfer Full Data Copy
wsrep_sst_method=rsync
  • Habilitar el Identificador Global de Transacciones
# Galera GTID Configuration
wsrep_gtid_mode=ON
wsrep_gtid_domain_id=1
  • Agregar la configuración para el Binary log y el Motor de Almacenamiento
# MariaDB Configuration
binlog_format=ROW
default-storage-engine=InnoDB
innodb_autoinc_lock_mode=2
log_slave_updates=ON
log_bin=galera-bin

Entonces crea otros 2 archivos de configuración para los otros nodos, con las mismas opciones del anterior pero cambiando los parámetros de identificación de este miembro:

pato@patocontainer ~/galera $ vi galera-node1.cnf
wsrep_cluster_address="gcomm://patomariagm,patomariag1,patomariag2"
# Galera Cluster Member
wsrep_node_name="patonode1"
wsrep_node_address="patomariag1"
pato@patocontainer ~/galera $ vi galera-node2.cnf
wsrep_cluster_address="gcomm://patomariagm,patomariag1,patomariag2"
# Galera Cluster Member
wsrep_node_name="patonode2"
wsrep_node_address="patomariag2"

Copiar los Archivos de Configuración hacia los Contenedores

Aún cuando los contenedores no estén corriendo podemos mover archivos adentro de ellos desde el anfitrión de Docker con el comando docker cp.
Vamos a copiar los archivos de configuración creados a un directorio válido para la búsqueda de configuración de mysqld en esta imagen, en nuestro caso usaremos !includedir /etc/mysql/mariadb.conf.d/:

pato@patocontainer ~/galera $ docker cp galera-init.cnf patomariagm:/etc/mysql/mariadb.conf.d/galera.cnf

pato@patocontainer ~/galera $ docker cp galera-node1.cnf patomariag1:/etc/mysql/mariadb.conf.d/galera.cnf

pato@patocontainer ~/galera $ docker cp galera-node2.cnf patomariag2:/etc/mysql/mariadb.conf.d/galera.cnf

Levantar los Contenedores

En un Cluster de Galera el nodo Primario debe ser el primero en arrancar con el fin de inicializar el cluster.
Así que vamos a levantar cada nodo en orden y validaremos cuántos miembros están registrados en el cluster:

pato@patocontainer ~ $ docker start patomariagm
patomariagm
pato@patocontainer ~ $ docker exec -it patomariagm mysql -e "SHOW STATUS LIKE 'wsrep_cluster_size'"
+--------------------+-------+
| Variable_name      | Value |
+--------------------+-------+
| wsrep_cluster_size | 1     |
+--------------------+-------+
pato@patocontainer ~ $ docker start patomariag1
patomariag1
pato@patocontainer ~ $ docker exec -it patomariag1 mysql -e "SHOW STATUS LIKE 'wsrep_cluster_size'"
+--------------------+-------+
| Variable_name      | Value |
+--------------------+-------+
| wsrep_cluster_size | 2     |
+--------------------+-------+
pato@patocontainer ~ $ docker start patomariag2
patomariag2
pato@patocontainer ~ $ docker exec -it patomariag2 mysql -e "SHOW STATUS LIKE 'wsrep_cluster_size'"
+--------------------+-------+
| Variable_name      | Value |
+--------------------+-------+
| wsrep_cluster_size | 3     |
+--------------------+-------+
pato@patocontainer ~ $

¡Perfecto, nuestro cluster de Galera está configurado y en ejecución y todos los miembros se han unido!

Modificar la Configuración para el Nodo Primario

Una vez que el cluster de Galera ha sido inicializado necesitamos modificar el parámetro wsrep_cluster_address de estar vacío (bootstrap) a tener la lista de miembros del cluster, igual que está configurado en los otros nodos:

pato@patocontainer ~/galera $ docker exec -it patomariagm bash
root@7cb4db2c49a9:/# nano /etc/mysql/mariadb.conf.d/galera.cnf
wsrep_cluster_address="gcomm://patomariagm,patomariag1,patomariag2"

Probar la Replicación en el Cluster de Galera

Ahora vamos a validar que la replicación esté funcionando.

Validar la Replicación Inicial

Primero queremos validar que los objetos y datos creados antes de la configuración del cluster se hayan replicado a los nuevos nodos:

pato@patocontainer ~ $ docker exec -it patomariag1 bash
root@5f9ce8bc16d2:/# mysql
MariaDB [(none)]> use taller
Database changed
MariaDB [taller]> select * from tg1;
+----+--------------+---------------------+
| i1 | c2           | d3                  |
+----+--------------+---------------------+
|  1 | 7cb4db2c49a9 | 2020-05-29 19:44:42 |
+----+--------------+---------------------+
1 row in set (0.000 sec)
pato@patocontainer ~ $ docker exec -it patomariag2 bash
root@18dda3dabdf9:/# mysql
MariaDB [(none)]> use taller
Database changed
MariaDB [taller]> select * from tg1;
+----+--------------+---------------------+
| i1 | c2           | d3                  |
+----+--------------+---------------------+
|  1 | 7cb4db2c49a9 | 2020-05-29 19:44:42 |
+----+--------------+---------------------+
1 row in set (0.088 sec)

Nuestros datos originales fueron replicados en cada nodo, entonces validemos la replicación futura de datos.

Validar la Replication Nodo a Nodo

Como un Cluster de Galera te permite actualizar desde todos los nodos, vamos a insertar un registro en cada nodo identificando la instancia que hace la inserción con @@hostname y el momento de la operación con now():

MariaDB [taller]> insert into tg1 (c2,d3) values (@@hostname,now());
Query OK, 1 row affected (0.398 sec)

y revisemos el resultado también en cada nodo:

pato@patocontainer ~/galera $ docker exec -it patomariagm mysql -e "select * from taller.tg1"
+----+--------------+---------------------+
| i1 | c2           | d3                  |
+----+--------------+---------------------+
|  1 | 7cb4db2c49a9 | 2020-05-29 19:44:42 |
|  4 | 7cb4db2c49a9 | 2020-05-29 20:35:19 |
|  5 | 5f9ce8bc16d2 | 2020-05-29 20:36:25 |
|  6 | 18dda3dabdf9 | 2020-05-29 20:37:01 |
+----+--------------+---------------------+
pato@patocontainer ~/galera $ docker exec -it patomariag1 mysql -e "select * from taller.tg1"
+----+--------------+---------------------+
| i1 | c2           | d3                  |
+----+--------------+---------------------+
|  1 | 7cb4db2c49a9 | 2020-05-29 19:44:42 |
|  4 | 7cb4db2c49a9 | 2020-05-29 20:35:19 |
|  5 | 5f9ce8bc16d2 | 2020-05-29 20:36:25 |
|  6 | 18dda3dabdf9 | 2020-05-29 20:37:01 |
+----+--------------+---------------------+
pato@patocontainer ~/galera $ docker exec -it patomariag2 mysql -e "select * from taller.tg1"
+----+--------------+---------------------+
| i1 | c2           | d3                  |
+----+--------------+---------------------+
|  1 | 7cb4db2c49a9 | 2020-05-29 19:44:42 |
|  4 | 7cb4db2c49a9 | 2020-05-29 20:35:19 |
|  5 | 5f9ce8bc16d2 | 2020-05-29 20:36:25 |
|  6 | 18dda3dabdf9 | 2020-05-29 20:37:01 |
+----+--------------+---------------------+

¡Como podemos ver se insertó un registro en cada nodo y los datos fueron replicados en los demás!

Conclusión

Fuimos capaces de configurar un Cluster de Galera para MariaDB usando contenedores de Docker dentro de una máquina virtual en la Google Cloud Platform y validamos que podemos actualizar cualquier tabla en cualquier nodo y esos cambios serán replicados a todos los nodos.
Esta es una manera fácil de construir un cluster multinodo cuando necesitas probar alguna configuración o replicar algún problema sin tener que lidiar con una instalación más permanente.