clustering

Replicación, recuperación en linea y alta disponibilidad con pgpool2 (3.1.1)

Recientemente he profundizado en la instalación y puesta en marcha de pgpool-II. Y, por lo que he visto en Internet, en mi opinión, la documentación no acaba de quedar clara en algunos puntos. Me ha tocado pelearme bastante con ello y, por eso, voy a publicar un modesto “manual” basado en la configuración real que he realizado yo. Compilando información de un montón de sitios pero, principalmente, siguiendo esta guía (bastante completa solo que ambigua en algunos puntos).

Todos los scripts y ficheros de configuración mencionados en esta entrada están disponibles aquí.

Antes de empezar


En mi caso he instalado y configurado pgpool-II en dos servidores físicos. Aunque previamente configuré una maqueta con máquinas virtuales. La configuración es independiente del escenario que elijáis, siempre que sean servidores y no lavadoras, claro. Como ejemplo usaremos:

  • master.eltioemil.com -> 192.168.1.2
  • esclavo.eltioemil.com -> 192.168.1.3
  • pgpool.eltioemil.com -> 192.168.1.4

Lo más sencillo para juguetear es que añadáis los datos anteriores al fichero /etc/hosts de vuestras máquinas. Si pretendéis utilizarlo en un entorno serio o, ¿qué se yo, en producción?, convendría que utilizarais bind o cualquier tinglado de resolución de nombres, en mi caso uso bind. Como sistema operativo se ha utilizado Debian 7.1 en ambos nodos. Locales en en_US.UTF-8 por necesidades especificas de mi entorno, aunque de igual manera, como si lo ponéis en Chino. Funciona prácticamente igual con cualquier distribución de Linux normal, y seguramente con alguna no tan normal… Cada cual con sus manías.

Para los scripts que usaremos más adelante necesitaremos las siguientes herramientas. Instalaremos las que nos falten:

apt-get install ntp openssl file psmisc sysstat bzip2 unzip nmap dstat rsync wget ccze tcpdump pciutils dnsutils host

Lo primero que haremos, por organizarnos, es instalar postgreSQL 9.3, en mi caso (O la versión que queramos por encima de la 8.2, que está descontinuada y, ¿por qué la usaríais teniendo la 9.3 y casi la 9.4?). Pero os recomiendo esta versión puesto que los amiguetes de postgreSQL le han dado un buen empujón a las últimas dos versiones en cuanto a prestaciones. Para ello, después de añadirnos los repositorios de postgreSQL que están en su weben ambos nodos realizaremos lo siguiente:

apt-get update
apt-get install libpq-dev postgresql-server-dev-9.3 bison build-essential
apt-get install postgresql-9.3 postgresql-contrib-9.3 postgresql-doc-9.3 uuid libdbd-pg-perl

Esto nos dejará en cada nodo una instalación funcional de postgreSQL a la que haremos unas pequeñas modificaciones básicas en la configuración (Ya nos encargaremos de afinar el rendimiento cuando la empecemos a usar en serio). En ambos nodos he optado por guardar las bases de datos en un directorio llamado /data por cuestiones de arquitectura de hardware, en dicho directorio monto un RAID 1 de discos SSD para que la escritura vaya fina, fina. Modificaremos en la configuración de postgreSQL (Ubicada en /etc/postgresql/9.3/main/postgresql.conf) el siguiente valor:

#——————————————————–
# FILE LOCATIONS
#——————————————————–
# …

data_directory = ‘/data’

# …

El resto de valores que modificaremos serán comunes a todos los nodos, dejando en el esclavo el directorio data por defecto. Solo modificaremos esto de momento.

listen_addresses = ‘*’

Crearemos el usuario pgpool2:

su – postgres
createuser –superuser pgpool2

Editaremos ahora el fichero /etc/postgresql/9.3/main/pg_hba.conf y añadimos el acceso para todos los usuarios en el segmento de red (si vamos a utilizar esto en un entorno de producción tendremos que limitar el acceso solo a los nodos y a los usuarios que utilicemos, ya sea postgres o pgpool2):

# TYPE  DATABASE        USER            ADDRESS                 METHOD
local   all             postgres                                peer
local   all             all                                     peer
host    all             all             127.0.0.1/32            md5
host    all             all             ::1/128                 md5
host    all             all             192.168.1.1/24        trust 

Ahora vamos a configurar write ahead log, editamos de nuevo /etc/postgresql/9.3/main/postgresql.conf y cambiamos los siguientes parámetros:

archive_mode = on
archive_command = ‘exit 0’
wal_level = archive

Y creamos el siguiente directorio en ambos nodos:

mkdir –mode=700 /data/pg_xlog_archive
chown postgres:postgres /data/pg_xlog_archive

Reiniciamos PostgreSQL para activar los cambios:

/etc/init.d/postgresql restart

Una vez instalado y configurado postgreSQL en ambos nodos. Instalamos pgpool2 solamente en el nodo maestro (master.eltioemil.com). Lo descargamos de la web en /opt (hemos instalado la versión 3.3.1), lo descomprimimos y lo instalamos:

./configure –prefix=/opt/pgpool2
make
make install
cd /opt/pgpool-II-3.3.1/sql/pgpool-recovery
make
make install
su – postgres -c “psql -f /usr/local/src/pgpool-II-3.1/sql/pgpool-recovery/pgpool-recovery.sql template1”

Con esto configuraremos la aplicación en el directorio /opt/pgpool2 para su instalación. Después, creamos un template con las propiedades de replicación y recuperación que heredarán el resto de bases de datos.

Vamos a configurar pgpool2 en el nodo maestro. Copiamos el fichero /opt/pgpool2/etc/pcp.conf del repositorio y generaremos una clave para el usuario root con el siguiente comando:

/opt/pgpool2/bin/pg_md5 -p
password:
1dd0e5bfc954bc46fe800952be7e2a69

Editaremos el fichero y colocaremos esa linea “root:1dd0e5bfc954bc46fe800952be7e2a69” en lugar la que se encuentra en el fichero del repositorio. (Evidentemente con vuestra propia clave y no con esta que os invito a que le hagáis un reverse md5 lookup 😉 ).

Copiaremos /opt/pgpool2/etc/pgpool.conf el cual se encuentra aquí para el nodo maestro. Os aconsejo explorar todas las carpetas del repositorio. Disponéis de las configuraciones para maestro y esclavo.

Nos copiaremos también del repositorio el script de inicio, personalizado para este ejemplo (abridlo y echadle un vistazo) y lo añadiremos al arranque. Modificaremos :

update-rc.d pgpool2 defaults

Si podemos, reiniciaremos para comprobar que arranca correctamente. Y pediremos un status del script para comprobar que todo funciona correctamente.

Si lo hemos hecho bien deberá mostrar algo así:

root@master:~$ /etc/init.d/pgpool2 status
Status of pgpool-II:
/usr/bin/psql -h master.eltioemil.com -p  9999 -U pgpool2 -d postgres -c ‘show pool_nodes’
 node_id |    hostname     | port | status | lb_weight |  role
———+—————–+——+——–+———–+——–
 0       | 192.168.1.2 | 5432 | 2      | 0.500000  | master
 1       | 192.168.1.3 | 5432 | 2      | 0.500000  | slave
(2 filas)

El status debe de ser 2 para asegurarnos de que esta en modo de replicación y balanceo. 

Si es 3 significa que no está conectado el nodo al pool.

Si queréis saber más de esto, os leéis la documentación oficial de pgpool que ahí os contarán hasta lo que han desayunado.

Con esto dispondremos de replicación (podemos realizar las pruebas propuestas en la guía que hemos ido siguiendo para comprobar que todo funciona como debe).

Recuperación en linea


Lo primero que haremos, puesto que lo necesitaremos para mandar comandos por ssh, es añadir las claves publicas de un nodo en otro y viceversa. De este modo podrán acceder de forma segura entre ellos. Si no hacemos esto, no podremos levantar postgreSQL de un nodo en otro.

Cambiaremos en la configuración de postgreSQL del nodo maestro la siguiente información:

archive_command = ‘exit 0’

por

archive_command = ‘/bin/cp %p /data/pg_xlog_archive/%f’

Y recargaremos postgresql (reload) en ambos nodos.

Hay un script que se llama wal_archiving (es distinto para cada nodo) que cambia automáticamente estos valores en la configuración de postgreSQL. Lo usaremos más adelante puesto que los pasos a seguir ante la caída de un nodo son el de activar wal_archiving, realizar la recuperación y volver a desactivar wal_archiving. Lo copiaremos en /etc/init.d y lo invocaremos mediante los comandos start, stop y status.

En la guía están bien explicada la configuración necesaria para realizar la recuperación en línea. Hay una serie de scripts pero no son exactamente los que hemos usado, los hemos adaptado ligeramente a nuestra maqueta. Están todos en el repositorio, en la carpeta data_scripts. Deben de estar en el nodo que tenga pgpool instalado, en la carpeta /data. Aunque, si configuramos en el futuro la alta disponibilidad, deberán estar en ambos, pero eso ya lo veremos. Nos aseguraremos de que los permisos son de ejecución y para el usuario postgres.

Ante la caida del nodo esclavo lo primero será activar el archivado:

/etc/init.d/wal_archiving start

Seguidamente invocaremos el siguiente comando (que realizará automáticamente los tres pasos descritos en la guía):

/opt/pgpool2/bin/pcp_recovery_node 5 master.eltioemil.com 9898 root 1

Desactivaremos el archivado si todo ha ido como debe:

/etc/init.d/wal_archiving stop

El comando pcp_recovery_node encadenará una serie de pasos implicando los scripts que hemos copiado en /data con los parámetros adecuados. Si se ha escrito algo en la base de datos mientras el nodo estaba caído, lo rescatará y lo copiará en la base de datos esclava para evitar incoherencias en los datos. Es capaz de levantar el servicio postgreSQL y añadir el nodo al pool de pgpool. Para más detalle de esto, la guía. Si la falla consiste en un apagado físico de la máquina no podrá hacer nada, evidentemente, hasta que la levantemos.

Lo siguiente que vamos a intentar configurar es la automatización de estos tres comandos, para que se ejecuten automáticamente ante la caída del nodo esclavo. Seguidamente intentaremos configurar la alta disponibilidad para que puedan caer indistintamente ambos nodos y no se pierdan datos.

Alta disponibilidad


Para la alta disponibilidad necesitaremos, antes que nada, instalar pgpool2 en todos los nodos. En nuestro caso, en esclavo.eltioemil.com

Simplemente cambiaremos algunos valores (el concepto es “instalarlo a la inversa”) en el fichero pgpool.conf del nodo esclavo.eltioemil.com:

# – Backend Connection Settings –
backend_hostname0 = ‘192.168.1.3’
backend_port0 = 5432
backend_weight0 = 1
backend_data_directory0 = ‘/data’
#backend_flag0 = ‘ALLOW_TO_FAILOVER’
backend_hostname1 = ‘192.168.1.2’
backend_port1 = 5432
backend_weight1 = 1
backend_data_directory1 = ‘/data’
#backend_flag1 = ‘ALLOW_TO_FAILOVER’
# …
pgpool2_hostname = ‘esclavo’


Configuración de heartbeat


En mi caso particular, mis dos servidores tienen bonding. ¿Qué es lo que es?, pues esto. Voy a explicarlo aquí como si tuvieramos una sola tarjeta de red, pero voy a copiar un ejemplo de como se haría en bonding por si a alguien le viene bien. De todas formas es muy sencillo. Pero como siempre me quejo de que en la documentación de internet se dan cosas por supuestas, no voy a ser hipócrita y lo voy a ejemplificar.

Como se explica en el guión, heartbeat se sirve de una IP de servicio sobre la IP real de los nodos. De modo que para quien tenga que usar el clúster (Nuestros amigos los devops o los developers más osados) solamente tendrán que dirigirse a esta IP de servicio puesto que a ellos les da igual cuantos nodos hay detrás, como balancean o si son un señor de Cuenca. Por tanto, en cada nodo, tenemos que crear dicha IP que heartbeat gestionará.

Como hemos dicho, el guión que hemos seguido no contempla que tengamos bonding en las tarjetas de red, como es nuestro caso, para configurar heartbeat. Pero no difiere mucho crear una IP de servicio aunque tengamos un bonding. Después de instalar heartbeat, antes de configurarlo, pararemos la red y modificaremos el fichero /etc/network/interfaces de una forma similar a esta (Ojito con parar networking si estamos conectados remotamente, amigos)

# The loopback network interface
auto lo bond0
iface lo inet loopback
# The primary network interface
allow-hotplug eth0
allow-hotplug eth1
iface bond0 inet static
    slaves eth0 eth1
    address     192.168.1.2
    netmask     255.255.255.0
    gateway     192.168.1.1
    network     192.168.1.0
    broadcast   192.168.1.255
    bond_mode active-backup
    bond_miimon 100
    bond_downdelay 150
iface bond0:0 inet static
    address     192.168.1.4
    netmask     255.255.255.0
    network     192.168.1.0
    broadcast   192.168.1.255

Simplemente hemos añadido una interfaz bond0:0 en lugar de la eth0:0 que propone el tutorial. Pero es lo mismo.

Con eth0 quedaría así:

# The loopback network interface
auto lo
iface lo inet loopback
# The primary network interface
auto eth0
iface eth0 inet static
    address 192.168.1.2
    network 192.168.1.0
    netmask 255.255.255.0
    gateway 192.168.1.1
iface eth0:0 inet static
    address 192.168.1.4
    network 192.168.1.0
    netmask 255.255.255.0
    broadcast 192.168.1.255

Así que lo podéis hacer como queráis. Esto es una mera cuestión de red, vamos al lío.

Antes de nada, modificaremos la configuración de pgpool para que en lugar de escuchar en todas las interfaces, solamente escuche en la IP de servicio:

# Pasará de ser así
listen_addresses = ‘*’
# A ser así
listen_addresses = ‘192.168.1.4’

Instalaremos heartbeat (apt-get install heartbeat). 

Una vez instalado encontraremos los ficheros de configuración de Heartbeat en /etc/ha.d. Necesitamos crear tres ficheros que normalmente están en /usr/share/doc/heartbeat como samples, pero que tenéis en el repositorio con las modificaciones pertinentes hechas.

  • ha.cf: fichero de configuración principal. Disponible aquí 
  • haresources: fichero de configuración de recursos. Disponible aquí
  • authkeys: información de autenticación. Disponible aquí. Generaremos el password que nosotros queramos.

CUIDADO: Modificaremos en ha.cf el nombre de la interfaz de red según corresponda. Como hemos explicado, si usamos bonding o no, o nuestra interfaz se llama pepito0.

También añadiremos esto al fichero /etc/hosts si no estamos usando otra cosa para resolver nombres:

192.168.1.1   router
192.168.1.2   master.eltioemil.com        master
192.168.1.3   esclavo.eltioemil.com       esclavo
192.168.1.4   pgpool.eltioemil.com        pgpool

El fichero haresources tiene diferencias con respecto al propuesto en el tutorial. Puesto que las versiones de heartbeat utilizadas en dicha guía son distintas a las utilizadas por nosotros. Debemos de especificar la dirección IP de servicio con mascara de subred (/24) y nombre de la interfaz de red. Por tanto será distinta en función de nuestra interfaz de red.

También difiere el link simbólico a realizar en /etc/ha.d/resource.d. Que será así:

ln -s /etc/init.d/pgpool2

El resto de pasos serán igual que en la guía. Se han copiado los ficheros de configuración finales después de configurar heartbeat. Por ejemplo el script de init de pgpool tiene variaciones en la opción status. Ya que apuntamos a la dirección IP de servicio y no a la del nodo en sí.

El fichero /etc/logd.cf no presenta ninguna diferencia con el propuesto en el tutorial.

Con esto, si todo ha ido bien, dispondréis de un clúster de postgreSQL que presenta ciertas ventajas con respecto a otras opciones. Al que añadir nodos es facilisimo, es un proyecto vivo que está avanzando bastante y muy estable. En el futuro iré afinando la configuración del mismo y veremos como rinde en producción.