La plataforma raspberry pi 3 nos ofrece a un bajo coste un perfecto sistema de laboratorio ARM  para evaluar características  y arquitectura  de sistemas y conceptos antes de implementar estos en otros grandes ámbitos.
En este caso, vamos a montar una alta disponibilidad con replicación de disco por red mediante drbd y con alternancia de carga con pacemaker y corosync.

Sobre este entorno, ejecutaremos los servicios que queramos, desde un servidor nfs hasta un gestor de backups en red.

Hardware.
Empleamos dos raspberry pi 3 model b+, que incorporan interfaz ethernet giga. Hay que mencionar que está integrado con usb 2, con lo que no llega al máximos teórico de su velocidad de red, y se queda en unos 300Mbs. Además, dos discos externos de 2,5 pulgadas, que se van a alimentar de la propia raspberry. Y por supuesto, conectados a dos bocas de red giga. Es un sistema muy económico pero muy versátil y capaz.

Software.
Instalamos en esta ocasión la versión de debian para raspberry, en concreto raspbian 9 stretch, versión de 32 bits, con una probada estabilidad.
Instalaremos drbd, pacemaker, corosync, mariadb, apache, y el software de backup en red bacula.

Instalación del sistema operativo.
La instalación del sistema operativo es sencilla. Simplemente descargamos la imagen de raspbian y la pasamos a una tarjeta microsd con el programa etcher. Este programa está disponible bajo windows y linux. He utilizado la imagen lite, contiene simplemente el sistema operativo y lo que necesitemos más adelante lo descargaremos de los repositorios.
Una vez que esté en la tarjeta el SO, montamos su contenido en el pc y creamos un fichero vacío de nombre ssh bajo /boot. Metemos la tarjeta en la pi y arrancamos. Tras un rato, el sistema arrancará, con una ip bajo dhcp. Tenemos que identificar el nuevo lease en el servidor de dhcpd o en nuestro router y ya podremos conectar por ssh a la ip que haya adquirido, con credenciales raspberry/pi.
Como primer paso, a través del comando raspi-config cambiamos el nombre, la dirección ip y el enrutamiento. En mi caso, he elegido los nombres según el color de la carcasa, tengo a piblanca (192.168.0.31) y pinegra (192.168.0.32). La ruta por defecto sale por 192.168.0.1. Los datos de red también los puedo añadir editando el fichero /boot/dhcpd.conf:

interface eth0
static ip_address=192.168.0.31/24
static routers=192.168.0.1
static domain_name_servers=8.8.8.8 80.58.0.33

 

Y antes que nada, ya que vamos a hacer uso intensivo de disco alimentados por usb, hay que decirle a la pi que de toda la alimentación disponible. Para ello, hay que añadir la línea

max_usb_current=1
al fichero /boot/config.txt

echo “max_usb_current=1” >> /boot/config.txt

Tras este paso, hay que reiniciar las pi para que apliquen los cambios.

Software para la replicación de disco por red drbd:
Instalo el soft necesario:

#apt-get install drbd8-utils drbd-doc

Para configurar drbd, simplemente creamos dos particiones de igual tamaño en los discos usb conectados y en el directorio /etc/drbd.d creamos el fichero r0.res para definir el recurso:

#global { usage-count yes; }
#common { syncer { rate 10M; } }
resource r0 {
net {
protocol C;
cram-hmac-alg sha1;
shared-secret “mypassword”;
}
disk {
resync-rate 20M;
}
on piblanca {
volume 0 {
device minor 1;
disk /dev/sda2;
meta-disk internal;
}
address 192.168.0.31:7789;
}
on pinegra {
volume 0 {
device minor 1;
disk /dev/sda1;
meta-disk internal;
}
address 192.168.0.32:7789;
}
}

Después, vamos a limpiar el disco por si acaso y preparamos el drbd.
En ambas pi:

dd if=/dev/zero of=/dev/sda2 bs=1M count=10
drbdadm -c /etc/drbd.conf create-md all
drbdadm up all

Elegimos un primario y en ese nodo ejecutamos:

drbdadm — –overwrite-data-of-peer primary all

Empieza a sincronizar los datos del disco. El progreso se ve:

watch cat /proc/drbd

Este paso es lento, para una partición de 3T tarda más de 48h.

Una vez sincronizado, creamos una partición en el dispositivo y la montamos. Podemos ponerlo bajo control de lvm si queremos, aunque no lo vamos a hacer en este caso.

root@pinegra:~# cat /proc/drbd
version: 8.4.10 (api:1/proto:86-101)
srcversion: 7EBAF95052116ECFD37C687

1: cs:Connected ro:Secondary/Primary ds:UpToDate/UpToDate C r—–
ns:0 nr:86696620 dw:86696620 dr:0 al:0 bm:0 lo:0 pe:0 ua:0 ap:0 ep:1 wo:f oos:0
root@pinegra:~#

Desde el primario creamos un filesystem y montamos la particion;

piblanca:/ # mkfs.ext4 /dev/drbd1

piblanca:/ # mount /dev/drbd1 /srv

Ya tenemos montado bajo /srv un filesystem que está sincronizado entre ambos discos duros a través de red. En cada momento habrá un primario que lo tendrá montado y un secundario que estará a la espera de montarlo cuando se le indique. Esto lo vamos a poner bajo control de corosync y pacemaker. Es importante no activar el arranque automático con el sistema ya que se va a encargar de esto corosync:

update-rc.d -f drbd remove

Instalación de corosync y pacemaker
Paquetes necesarios:
apt-get install pacemaker corosync crmsh
Generamos en un nodo la clave:
corosync-keygen

Y la copiamos al otro nodo:

scp /etc/corosync/authkey pinegra:/etc/corosync/authkey

Hay que editar el fichero /etc/corosync/corosync.conf y modificar el valor de bindnetaddr bajo interface con la red donde está la HA:

piblanca:/etc/corosync # cat corosync.conf
# Totem Protocol Configuration
totem {
version: 2
cluster_name: hakase-cluster
transport: udpu

# Interface configuration for Corosync
interface {
ringnumber: 0
bindnetaddr: 192.168.0.0
broadcast: yes
mcastport: 5407
}
}

# Nodelist – Server List
nodelist {
node {
ring0_addr: piblanca
}
node {
ring0_addr: pinegra
}
}

# Quorum configuration
quorum {
provider: corosync_votequorum
}

# Corosync Log configuration
logging {
to_logfile: yes
logfile: /var/log/corosync/corosync.log
to_syslog: yes
timestamp: on
}

service {
name: pacemaker
ver: 0
}

Ahora en los dos nodos levantamos el servicio y lo dejamos listo para los reinicios:

systemctl start corosync
systemctl start pacemaker

systemctl enable corosync
systemctl enable pacemaker

Queda preparado el cluster:
pinegra:/var/log # crm status
Stack: corosync
Current DC: piblanca (version 1.1.18+20180430.b12c320f5-lp150.1.2-b12c320f5) – partition with quorum
Last updated: Sat Jun 23 19:00:35 2018
Last change: Sat Jun 23 18:55:24 2018 by hacluster via crmd on piblanca

2 nodes configured
0 resources configured

Online: [ piblanca pinegra ]

No resources

pinegra:/var/log #

Quitamos el stonith e ignoramos quorum:
#crm configure property stonith-enabled=false
#crm configure property no-quorum-policy=ignore

Servicios
Instalamos el servidor de nfs en ambos nodos:

apt-get install nfs-kernel-server

Empezamos a definir primitivas dentro del cluster. Para ello, desde la shell de root ejecutamos comandos de crm, que es la shell propia de la ha, o entramos en esta shell directamente.
Definimos la ip flotante que nos va a a dar el servicio:

#crm configure primitive ip_srv ocf:heartbeat:IPaddr2 params ip=”192.168.0.30″ nic=”eth0″ op monitor interval=”10s” meta is-managed=”true”

Ahora desde crm ponemos drbd bajo su control:

root@pinegra:~# crm configure

crm(live)configure# primitive drbd_srv ocf:linbit:drbd \
params drbd_resource=r0 \
op monitor interval=15s role=Master \
op monitor interval=30 role=Slave
crm(live)configure# ms ms_drbd_srv drbd_srv \
meta master-max=1 master-node-max=1 clone-max=2 clone-node-max=1 notify=true
crm(live)configure#

También definimos el filesystem viajero:

crm(live)configure# primitive fs_srv Filesystem \
params device=”/dev/drbd1″ directory=”/srv” fstype=ext4 \
op monitor interval=10s

Agrupamos recursos para que se levanten en el mismo nodo:

crm(live)configure# group g-nfs fs_srv ip_srv

Ahora queremos que el filesystem se exporte por nfs en la ip flotante:

crm(live)configure# primitive nfsserver systemd:nfs-server \
op monitor interval=30s
crm(live)configure# clone cl-nfsserver nfsserver
crm(live)configure# primitive exportfs_srv exportfs \
params directory=”/srv” options=”rw,mountpoint” clientspec=”192.168.0.0/24″ wait_for_leasetime_on_stop=true fsid=1 \
op start timeout=60 interval=0 \
op stop timeout=120 interval=0 \
op monitor interval=30s \
meta target-role=Started

Se lo añado al grupo:

 

crm(live)configure# modgroup g-nfs add exportfs_srv

Defino el orden y las ubicaciones para que los servicios se levanten ordenadamente:

crm(live)configure# order filesystem_after_drbd inf: ms_drbd_srv:promote g-nfs:start
crm(live)configure# colocation filesystem_on_drbd inf: g-nfs ms_drbd_srv:Master

Para que los cambios tengan efecto hay que hacer un ‘commit’ dentro de la shell y ya tenemos definido un servicio de nfs totalmente funcional que cambiará de nodo de forma transparente en caso de caída de uno de los integrantes.

Para ver lo que tenemos definido por el momento:

pinegra:~ # crm status
Stack: corosync
Current DC: pinegra (version 1.1.18+20180430.b12c320f5-lp150.1.2-b12c320f5) – partition with quorum
Last updated: Thu Jun 28 09:17:44 2018
Last change: Thu Jun 28 09:16:54 2018 by root via crm_attribute on piblanca

2 nodes configured
7 resources configured

Online: [ piblanca pinegra ]

Full list of resources:

Master/Slave Set: ms_drbd_srv [drbd_srv]
Masters: [ pinegra ]
Slaves: [ piblanca ]
Resource Group: g-nfs
fs_srv (ocf::heartbeat:Filesystem): Started pinegra
ip_srv (ocf::heartbeat:IPaddr2): Started pinegra
exportfs_srv (ocf::heartbeat:exportfs): Started pinegra
Clone Set: cl-nfsserver [nfsserver]
Started: [ piblanca pinegra ]

pinegra:~ #

Con esta estructura podemos hacer un servidor de nfs sobre una ip flotante que permite hacer sincronizaciones entre directorios con rsync desde cualquier cliente y que además es capaz de cambiar de nodo el servicio de forma transparente.

Para completar el grupo de recursos g-nfs vamos a añadir una base de datos que también viajará entre nodos.
Además, creo el grupo de servicios que se levantarán cuando el grupo anterior esté plenamente funcional. Aquí empezamos poniendo el servidor web.
Primero instalamos software:

#aptitude install apache2 mysql-server mysql-client php

Definimos en crm:

root@pinegra:~# crm configure
crm(live)configure# primitive p_mariadb lsb:mysql \
> op start interval=0 trace_ra=1 \
> op stop interval=0 trace_ra=1 \
> op monitor interval=15s trace_ra=1 \
> meta target-role=Started
crm(live)configure# modgroup g-nfs add p_mariadb

Y con el apache creamos un nuevo grupo y definimos su orden de arranque y colocación, en este caso después del grupo nfs y en el mismo nodo donde esté el filesystem bajo drbd:

primitive apache lsb:apache2 \
> meta target-role=Started
group g-servicios apache \
meta target-role=Started
order servicios_after_filesystem g-nfs:start g-servicios:start
colocation servicios_on_filesystem inf: g-servicios ms-drbd_srv:Master

Configuración de mysql (mariadb)
Queremos que mysql se levante y se apague con el cluster y que los datos estén en el filesystem viajero.
En el punto de montaje srv creo un directorio /srv/config/mysql donde irá la configuración del software. Si el nodo en slave no estará montado y no arrancará el servicio; tampoco se lo va a ordenar corosync por las restricciones de colocación.
Copio el directorio /etc/mysql a /srv/config/mysql y le hago un link
root@pinegra:/etc# mv mysql mysql.orig
root@pinegra:/etc# ln -s /srv/config/mysql mysql
root@pinegra:/etc#

Añadimos al fichero de configuración /srv/config/mysql/mariadb.conf.d/50-server.cnf
[mysqld]

#añadido para HA
symbolic-links=0
bind_address = 0.0.0.0
datadir = /srv/config/mysql/datadirectory
#datadir = /var/lib/mysql
pid_file = /var/run/mysqld/mysqld.pid
socket = /var/run/mysqld/mysqld.sock

Y también en el mismo path la fichero 50-mysqld_safe.cnf

[mysqld_safe]

#Añadido para HA
bind_address = 0.0.0.0
#datadir = /srv/config/mysql/datadirectory
datadir = /var/lib/mysql
pid_file = /var/run/mysqld/mysqld.pid
socket = /var/run/mysqld/mysqld.sock

Creamos en /srv/config/mysql el directorio datadirectory y lo hacemos propiedad de mysql:mysql para que pueda escribir ahí las bbdd.
Al igual que en drbd, hay que sacar el arranque del servicio mysql de los rc o systemd porque se va a encargar de ello la HA.

Backup en red con bacula
Instalamos el paquete bacula, ya sea desde el repositorio de raspbian o compilándolo desde el código. La configuración del gestor de backup es otro tema aparte que se tratará en otro momento, por ahora decir que hay que editar los ficheros bacula-dir.conf, bacula-sd.conf y bconsole.conf en el servidor y el bacula-fd.conf en cada cliente.
Definimos dentro del grupo de servicios las primitivas para bacula utilizando el recurso genérico. Al igual que con la base de datos, colocamos los ficheros de configuración en el disco replicado para que se levante con el nodo que lo monte.
primitive p_bacula-dir anything \
params binfile=”/sbin/bacula-dir” cmdline_options=”-f -c /srv/config/bacula/bacula-dir.conf” pidfile=”/var/run/bacula-dir.9101.pid” \
op start timeout=20 interval=0 \
op stop timeout=30 interval=0 \
op monitor interval=20
primitive p_bacula-sd anything \
params binfile=”/sbin/bacula-sd” cmdline_options=”-f -c /srv/config/bacula/bacula-sd.conf” pidfile=”/var/run/bacula-sd.9103.pid” \
op start timeout=20 interval=0 \
op stop timeout=30 interval=0 \
op monitor interval=20
modgroup g-servicios add p_bacula-dir
modgroup g-servicios add p_bacula-sd

Hay que editar los ficheros de configuración de bacula:
piblanca:/etc/bacula # ln -s /srv/config/bacula/bacula-dir.conf bacula-dir.conf
piblanca:/etc/bacula # ln -s /srv/config/bacula/bacula-sd.conf bacula-sd.conf
piblanca:/etc/bacula # mv bconsole.conf bconsole.conf.orig
piblanca:/etc/bacula # ln -s /srv/config/bacula/bconsole.conf bconsole.conf
piblanca:/etc/bacula #

El estado final del cluster es:
root@piblanca:~# crm status
Stack: corosync
Current DC: piblanca (version 1.1.16-94ff4df) – partition with quorum
Last updated: Mon Oct 15 15:23:57 2018
Last change: Fri Oct 12 10:36:20 2018 by root via cibadmin on piblanca

2 nodes configured
11 resources configured

Online: [ piblanca pinegra ]

Full list of resources:

Clone Set: cl-nfsserver [nfsserver]
Started: [ piblanca pinegra ]
Master/Slave Set: ms-drbd_srv [drbd_srv]
Masters: [ piblanca ]
Slaves: [ pinegra ]
Resource Group: g-nfs
fs_srv (ocf::heartbeat:Filesystem): Started piblanca
exportfs_srv (ocf::heartbeat:exportfs): Started piblanca
ip_srv (ocf::heartbeat:IPaddr2): Started piblanca
p_mariadb (lsb:mysql): Started piblanca
Resource Group: g-servicios
apache (lsb:apache2): Started piblanca
p_bacula-dir (ocf::heartbeat:anything): Started piblanca
p_bacula-sd (ocf::heartbeat:anything): Started piblanca

root@piblanca:~#

Si en cualquier momento reiniciamos un nodo o le quitamos la alimentación o lo que es más importante, tenemos una avería de hardware, todos los servicios pasarán automáticamente al otro sin intervención.
El rendimiento de este sistema se ve limitado por la integración del interfaz ethernet bajo usb 2, aún así, es perfectamente válido para tareas de baja carga pero con total disponibilidad.