Instalar y configurar MySQL Percona XtraDB Clúster

23 Octubre 2013 at 10:25 by Adrián Pérez

clusterPercona XtraDB Clúster, es la solución de alta disponibilidad y balanceo de carga multi-máster del fork de MySQL "Percona". La solución recomendada para un cluster necesita un mínimo de 3 nodos, pero siempre un número impar de nodos. En este post, sin embargo, montaremos un cluster Percona con 2 nodos y un árbitro -todo con Percona XtraDB Cluster 5.5 y wsrep_provider_version 2.6(r152)- puesto que supondremos que únicamente disponemos de dos servidores dedicados para el clúster. Más adelante se explicará qué es y para qué sirve un árbitro.

Percona Master-Slave vs XtraDB Clúster

Master-slave consta de un único servidor que permite escrituras, mientras que resto de nodos (slaves) únicamente permiten lecturas. Los slaves replicarán los datos del único master, y se mantendrá así los mismos datos en todos los nodos de la solución. En este caso, si se dispone de un balanceador para los slaves, tendremos un balanceo de carga para las lecturas, lo cual hace de esta solución una solución ideal para entornos con un alto volumen de lecturas. Además, se conseguirá un entorno de alta disponibilidad para las lecturas, pero no así para las escrituras.

Con XtraDB Clúster tendremos múltiples servidores corriendo varios MySQL (basados en Percona) con los mismos datos, permitiendo a los clientes escribir en cualquiera de los servidores, y replicando los datos al resto de nodos del clúster. Ésto es ideal para entornos donde hay un gran volumen de escrituras, pero también de lecturas, o donde necesitemos tener alta disponibilidad, también de los nodos "master" (que permiten escrituras).

Limitaciones

Percona XtraDB Cluster, está basado en Galera Cluster (igual que MariaDB Cluster), y por tanto, a día de hoy, tiene un seguido de limitaciones  de entre las que destacan:

  • La replicación sólo funcionará con las tablas InnoDB. Puede haber otras tablas pero no se replicarán, lo cual significa que no se encontrarán en todo el clúster, únicamente existirán en ese nodo.
  • La velocidad de escritura de todo el clúster, viene delimitado por el nodo más lento. Si un nodo tiene problemas y se vuelve lento, el clúster entero será lento.
  • Puede darse el caso de que dos clientes estén modificando la misma celda al mismo tiempo. Si ésto ocurriera, únicamente uno de los dos tendría éxito, mientras que el otro recibiría un error de MySQL.
  • Se recomienda un mínimo de 3 nodos, aunque en la documentación oficial también confirman que se puede montar con 2 nodos. En entornos con dos nodos es altamente recomendable montar un árbitro (que es lo que haremos).
  • Ver listado completo.

Entorno

Para los testeos, se ha usado un entorno con dos instancias en Amazon EC2, por la rapidez y flexibilidad que ofrece el Cloud Computing creando entornos de test. He usado la AMI Oficial de CentOS 6.4, que se puede lanzar directamente desde esta web (habiéndote logueado con tu cuenta de Amazon AWS), creando así dos instancias tipo "small", con 1,7GB de RAM y una IP fija cada una, ambas en la región de Oregón.

Una vez creada la primera AMI, he realizado algunos cambios básicos en el sistema:

  • Deshabilitar selinux (/etc/selinux/config).
  • Cambiar el hostname de la máquina para que sea más fácil de identificar (/etc/sysconfig/network).
  • Añadir el hostname que hemos definido, en el fichero /etc/hosts de la máquina.
  • Deshabilitar iptables (/etc/init.d/iptables stop). Amazon tiene su propio firewall delante, que nos servirá para este entorno de test.

 

Instalación de XtraDB Cluster

Una vez tenemos nuestro primer servidor (al que he llamado "percona1"), tenemos que instalarle Percona. Para ello instalaremos el repositorio de Percona para CentOS, con tal de poder realizar la instalación con yum.

[root@percona1 ~]# vi /etc/yum.repos.d/Percona.repo
[percona]
name = CentOS $releasever - Percona
baseurl=http://repo.percona.com/centos/$releasever/os/$basearch/
enabled = 1
gpgkey = file:///etc/pki/rpm-gpg/RPM-GPG-KEY-percona
gpgcheck = 1
 
[root@percona1 ~]# cd /etc/pki/rpm-gpg
[root@percona1 ~]# wget http://www.percona.com/downloads/RPM-GPG-KEY-percona

Una vez instalado el repositorio, pasaremos a instalar Percona XtraDB Cluster, tanto server como cliente:

[root@percona1 ~]# yum install Percona-XtraDB-Cluster-server.x86_64 Percona-XtraDB-Cluster-client.x86_64

En este punto, he pasado a clonar la instancia para tener un clon de este server. En un arrebato de originalidad, he llamado a la nueva máquina, "percona2". Si estás trabajando con servidores físicos, símplemente configura un segundo servidor igual que hemos hecho hasta ahora, pero llámalo con un nombre diferente y asígnale una IP diferente. Recuerda, para este test, necesitaremos tener tanto selinux como iptables deshabilitados.

Si estás con AWS, puedes crear un clon símplemente seleccionando la instancia y posteriormente seleccionando "Action > Create image (EBS AMI)". Ésto nos creará un snapshot y una AMI, a través de la cual podremos crear todas las nuevas instancias que queramos. Una vez creada la nueva instancia, deberemos cambiarle el hostname para evitar confusiones.

Tal y como se ha comentado, los tests los haremos con 2 servidores y un árbitro que configuraremos posteriormente:

  • "percona1" con IP pública 80.80.80.81 e ip privada 10.253.2.1
  • "percona2" con IP pública 80.80.80.82 e ip privada 10.253.2.2

Configuración de XtraDB Cluster

En este punto, tendremos dos servidores con Percona XtraDB Cluster, listos para configurar. Estos dos servidores serán los nodos de nuestro clúster.

En primer lugar, instalaremos las bases de datos y tablas de sistema, en el primer servidor, percona1, con:

[root@percona1 ~]# mkdir /data/mysql
[root@percona1 ~]# chown mysql:mysql /data/mysql
[root@percona1 ~]# mysql_install_db --datadir=/data/mysql/ --user=mysql

A continuación, en un entorno en producción, deberíamos asegurarnos de configurar correctamente iptables. Sin embargo, en este test tendremos iptables parado, y por tanto podemos olvidarnos de este punto. A modo de apunte, por defecto, el clúster trabajará con los puertos 3306, 4444, 4567 y 4568.

Así pues, ya podemos pasar a crear el fichero my.cnf adaptado a nuestros servidores, usando el asistente online de Percona. Cuando tengas el fichero my.cnf configurado, déjalo en /etc/my.cnf, y ábrelo, que aun tocará realizar los cambios necesarios para funcionar como cluster, que nos comenta la documentación oficial. Por lo menos, deberemos asegurarnos de tener lo siguiente. Recuerda, de momento todo ésto únicamente para el primer servidor "percona1":

[mysqld]
wsrep_cluster_address=gcomm://80.80.80.81,80.80.80.82
datadir=/data/mysql
user=mysql
binlog_format=ROW
wsrep_provider=/usr/lib64/libgalera_smm.so
wsrep_cluster_name=perconatest
wsrep_sst_method=xtrabackup
wsrep_sst_auth="sstuser:s3cr3t"
wsrep_node_name=percona1
wsrep_node_address=80.80.80.81
innodb_locks_unsafe_for_binlog=1
innodb_autoinc_lock_mode=2

En este ejemplo, se han añadido a nuestro fichero my.cnf que hemos generado con el asistente online de Percona, las líneas anteriores. A destacar los valores:

  • wsrep_cluster_address donde se deberán incluir los nodos que forman nuestro cluster (en este caso, las 2 instancias de Amazon, incluyéndo la instancia actual). Se ha usado wsrep_cluster_address porqué wsrep_urls está obsoleto.
  • wsrep_cluster_name que definirá el nombre del cluster y será común en todo el cluster.
  • wsrep_sst_method, que definirá el método de transferencia del estado inicial (SST - State Snapshot Transfer) de los datos, cuando un nodo se une al cluster. Típicamente se escojerá xtrabackup o rsync. Si se opta por xtrabackup, se deberá definir también la variable "wsrep_sst_auth", que definirá el usuario:password de MySQL que se usará para realizar el backup inicial.

NOTA: De nuevo, sólo si estás con EC2 usa las IPs privadas en lugar de las públicas, tanto en "wsrep_cluster_address" como en "wsrep_node_address".

NOTA2: A modo de ejemplo, puedes descargarte el fichero completo my.cnf que he usado, aquí > my.cnf "percona1".

Con nuestro entorno listo, podremos iniciar el primer nodo. He visto que hay dos formas de hacerlo, y las dos me han funcionado, aunque en la documentación oficial únicamente recomiendan usar el siguiente método:

[root@percona1 ~]# service mysql bootstrap-pxc
Bootstrapping PXC (Percona XtraDB Cluster)Starting MySQL (Percona XtraDB Cluster)............. SUCCESS!

A modo de apunte, el segundo método (ya obsoleto) que NO USAREMOS es el siguiente:

[root@percona1 ~]# /etc/init.d/mysql start --wsrep-cluster-address="gcomm://"
Starting MySQL (Percona XtraDB Cluster)........ SUCCESS!

NOTA: Si se apagasen todos los nodos del clúster, el primer nodo debería volverse a iniciar con uno de los dos métodos explicados.

Al final de nuestros logs del fichero mysql-error.log deberíamos ver algo parecido a lo siguiente:

130829 11:37:46 [Note] WSREP: Synchronized with group, ready for connections
130829 11:37:46 [Note] WSREP: wsrep_notify_cmd is not defined, skipping notification.
130829 11:37:46 [Note] /usr/sbin/mysqld: ready for connections.

Ahora deberemos entrar en el mysql y crear el usuario de replicación "sstuser" con las credenciales definidas en el my.cnf:

[root@percona1 mysql]# mysql
mysql> UPDATE mysql.user SET password=PASSWORD("superPassword") where user='root';
mysql> FLUSH PRIVILEGES;
mysql> CREATE USER 'sstuser'@'localhost' IDENTIFIED BY 's3cr3t';
mysql> GRANT RELOAD, LOCK TABLES, REPLICATION CLIENT ON *.* TO 'sstuser'@'localhost';
mysql> FLUSH PRIVILEGES;

Ahora que tenemos el primer nodo listo, nos pondremos con el segundo nodo (percona2). En este caso, no hará falta iniciar las bases de datos y tablas del sistema, como hemos hecho antes con mysql_install_db. Símplemente nos aseguraremos de tener listo el dbpath y el my.cnf:

## Creamos el dbpath pero NO instalaremos las bases de datos y tablas de sistema
[root@percona2 ~]# mkdir /data/mysql
[root@percona2 ~]# chown mysql:mysql /data/mysql
 
## Creamos el my.cnf igual que hemos creado el del server percona1, pero cambiando las siguientes líneas:
wsrep_node_name  = percona2
wsrep_node_address=80.80.80.82

NOTA: De nuevo, si estás con Amazon EC2, usa la IP privada de la instancia.

A continuación iniciaremos el segundo nodo del clúster:

[root@percona2 ~]# /etc/init.d/mysql start
Starting MySQL (Percona XtraDB Cluster)......SST in progress, setting sleep higher. SUCCESS!

Vemos que durante la inicialización del MySQL de este segundo nodo, se ejecuta el SST para realizar el snapshot y transferencia del mismo desde el primer nodo, con tal de replicar el estado del primer nodo en este segundo nodo.

IMPORTANTE: Durante los testeos, me he encontrado que seguramente no sea buena idea configurar el servicio de mysql para que se inicie durante el boot del server, puesto que en todos los testeos, el mysql ha fallado al tratar de iniciarse durante el inicio. Así pues, mejor que no se inicie automáticamente, y tras un fallo o reinicio del servidor, iniciarlo manualmente.

Instalación y configuración del árbitro

Como se ha comentado, necesitaremos un árbitro que evite situaciones de "split-brain", pues únicamente contamos con 2 nodos en nuestro clúster. Si tuvieramos un clúster con un número impar de nodos a partir de 3, no sería necesario un árbitro. Un árbitro únicamente servirá para que un nodo pueda saber si el otro nodo ha caído, o si es él quien ha quedado aislado por un fallo en la red. Así pues, un árbitro no consumirá apenas recursos y no replicará, puesto que no guardará ningún dato de base de datos, pudiendo ser instalada en cualquier máquina que tenga otros servicios corriendo, siempre y cuando dicha máquina tenga buena conectividad con el resto de nodos del cluster. Más información sobre los árbitros aquí y aquí.

Con la instalación de Percona XtraDB Cluster viene el binario del árbitro, que bastará con copiar en la tercera máquina, que alojará el árbitro. Podremos ubicar el binario en /usr/bin/garbd. Una vez copiado el binario "garbd" en la carpeta /usr/bin del nuevo servidor, podremos iniciarlo con el siguiente comando:

garbd -d -a gcomm://80.80.80.81,80.80.80.82 -g perconatest -l /var/log/garbd.log

Como parámetros, pasaremos los dos nodos del cluster (80.80.80.81 y 80.80.80.82) pues ¡no basta sólo con pasarle un nodo!, el nombre del clúster (perconatest) y el fichero donde dejará los logs. Al iniciar el árbitro, el tamaño del clúster habrá crecido:

mysql> show status like 'wsrep%';
| wsrep_cluster_size         | 3

Como punto a tener en cuenta, el server donde se alojará el árbitro, también necesitará abrir el puerto 4567 (por defecto) de entrada, así que tenlo en cuenta en un entorno en producción.

Para hacer la configuración persistente, podremos crear un fichero de configuración para nuestro árbitro, tal y como sigue [fuente]:

[root@arbitro]# cat /etc/default/garb.cfg
#garbd -d -a gcomm://80.80.80.81,80.80.80.82 -g perconatest -l /var/log/garbd.log
 
address=gcomm://80.80.80.81:4567,80.80.80.82:4567
group=perconatest
log=/var/log/garbd.log

Para asegurarnos de que el árbitro se iniciará tras cada reinicio del servidor, bastará con añadir la instrucción en el fichero rc.local del servidor:

#Percona XtraDB Cluster arbitrator
/usr/bin/garbd --cfg /etc/default/garb.cfg --daemon

Testeos

En este momento deberíamos tener un cluster XtraDB con dos nodos. Podemos empezar a verificarlo, por ejemplo, intentando entrar en el cliente mysql en el segundo nodo con "root". Si todo va bien, sólo deberíamos poder entrar con el password que hemos seteado para root en el servidor percona1. Con esto estaremos verificando que la replicación inicial ha ido bien.

[root@percona2 ~]# mysql
ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: NO)

En ambos nodos, podemos ejecutar además, la siguiente query para verificar los parámetros de la replicación:

mysql> show status like 'wsrep%';
+----------------------------+--------------------------------------+
| Variable_name              | Value                                |
+----------------------------+--------------------------------------+
| wsrep_local_state_uuid     | c2883338-834d-11e2-0800-03c9c68e41ec |
...
| wsrep_local_state          | 4                                    |
| wsrep_local_state_comment  | Synced                               |
...
| wsrep_cluster_size         | 3                                    |
| wsrep_cluster_status       | Primary                              |
| wsrep_connected            | ON                                   |
...
| wsrep_ready                | ON                                   |
+----------------------------+--------------------------------------+
40 rows in set (0.01 sec)

Tests simples

Empezando con las pruebas serias, en el primer nodo podremos crear una nueva base de datos de test, y verificar como se ha replicado en el segundo nodo:

percona1 | mysql> create database testreplica;
percona1 | mysql> show databases;
...
testreplica
 
percona2 | mysql> show databases;
...
testreplica

Si funciona, lo lógico es probarlo de vuelta: modificar percona2 y verificar como se replica en nodo1

percona2 | mysql> use testreplica;
percona2 | mysql> create table t (c CHAR(20) CHARACTER SET utf8 COLLATE utf8_bin);
 
percona1 | mysql> use testreplica;
mysql> show tables;
t

Testeos más exhaustivos

Se podría crear un script de conexión desde un cliente, para ir repartiendo los inserts en los dos nodos. La idea es repartir los inserts entre ambos nodos del cluster, esperando obtener al final el listado de inserts ordenado en ambos nodos. Queremos verificar que efectivamente se mantiene el orden de los datos insertados a pesar de que se inserten en diferentes nodos.

#!/bin/bash
host1=80.80.80.81
host2=80.80.80.82
i=0
while [ $i -le 2000 ]
do        
        mysql -u remote -pmipass -h $host1 -D testreplica -e 'insert into t (c) VALUES('$i');'
        ((i++))
        mysql -u remote -pmipass -h $host2 -D testreplica -e 'insert into t (c) VALUES('$i');'
        ((i++))
done

Finalmente, ejecutamos el script desde el cliente. Pasados unos minutos, deberíamos tener en todos los nodos de nuestro cluster, el la base de datos "testreplica", dentro de la tabla "t", un listado del 0 al 2000 perfectamente en orden, en todos los nodos (dos nodos en este caso).

Testeos finales

Si todo va bien, deberíamos testear qué pasa cuando uno de los nodos cae. Para ello, he creado un balanceador de carga en RackSpace, y he añadido los nodos de nuestro cluster. Con RackSpace es muy fácil de hacer, y bastante barato (a partir de unos 10$ al mes). He seleccionado el algoritmo de balanceo de Round Robin para que actue como un balanceador "tonto" que símplemente vaya repartiendo las conexiones entre los nodos del cluster una a una.

Una vez lo he tenido, he modificado el script anterior, para enviar los inserts a la IP del balanceador, y que éste se encargue de balancear entre nodos, mientras se realizan pruebas controladas apagando los nodos, parando el servicio de mysqld o habilitando iptables para simular una caída.

NOTA: Los balanceadores de RackSpace pueden usar una IP diferente a la pública para conectar con los nodos. Abre el puerto 3306 en tus nodos y verifica la IP de conexión con "show processlist" para identificar la IP de conexión del balanceador.

 

A falta de testeos más exhaustivos, Percona XtraDB Clúster parece funcionar bastante bien.

Fuentes:

http://www.percona.com/doc/percona-xtradb-cluster/howtos/3nodesec2.html

http://www.percona.com/doc/percona-xtradb-cluster/howtos/cenots_howto.html

http://www.mysqlperformanceblog.com/2012/07/25/percona-xtradb-cluster-failure-scenarios-with-only-2-nodes/

http://www.mysqlperformanceblog.com/2013/09/23/percona-xtradb-cluster-setting-simple-cluster/

Flickr! Foto por Jared Smith