IP flotante con BGP en CentOS/RHEL/SL 7

ip_flotante_bgp

Configurar una direccion IP flotante entre 2 equipos en redes diferentes

Introduccion

Las direcciones IP flotantes son muy utiles para ofrecer alta disponibilidad a servicios dentro de un mismo rango de red, esto lo podemos
implementar haciendo uso de keepalived, pero cuando los nodos no estan en el mismo rango de red no podemos utilizar keepalived debido a que este
utilizar el protocolo VRRP que funciona sobre L2
En caso de que necesitemos configurar una IP flotante entre equipos que estan en redes diferentes, es decir en rangos de red diferentes, lo podemos
utilizar haciendo uso del protocolo de ruteo BGP
La forma en la que esto funciona es la siguiente:

  • Ambos nodos tienen la direccion IP flotante configurada en una interfaz dummy
  • Ambos nodos publican rutas por BGP para llegar a IP flotante
  • El router de la red debe aprender esas rutas de forma a que cuando un cliente quiera conectarse a la IP flotante, este direccionara el trafico
    hacia nuestros nodos
  • En caso de que uno de los nodos caiga, el router dejara de aprender rutas de el por lo tanto el trafico se dirige al nodo activo

Esta implementacion nos ofrece alta disponibilidad entre nodos ademas de balanceo de carga
En este documento mostraremos paso a paso lo necesario para implementar la IP Flotante utilizando BGP entre 2 nodos CentOS 7 y
un tercer nodo CentOS 7 que actuara de router
Para esto utilizaremos BIRD tanto en los nodos que tendran la IP flotante como en el router para que aprenda las rutas por BGP
El entorno en el que esta basado este documento es el siguiente:

  • nodo1:

    • hostname: ha-bgp1
    • SO: CentOS 7
    • IP: 10.100.1.151
    • BGP AS: 64513
  • nodo2:

    • hostname: ha-bgp2
    • SO: CentOS 7
    • IP: 10.100.2.151
    • BGP AS: 64514
  • router:

    • hostname: rt1
    • SO: CentOS 7
    • IP eth0: 10.100.1.150
    • IP eth0: 10.100.2.150
    • BGP AS: 64515
  • IP Flotante: 10.155.155.1/32

Instalacion de BIRD

Utilizaremos BIRD tanto para publicar las rutas como para aprenderlas, por lo tanto debemos instalarlo tanto en los nodos, como en el router
El paquete BIRD se encuentra dentro del repositorio epel, el cual podemos configurar/habilitar con el siguiente comando:

yum install -y epel-release

Una vez que tenemos configurado el repositorio epel procedemos a instalar BIRD

yum install -y bird

Configurar IP flotante en una interfaz dummy

Como comentamos en la introduccion, la IP flotante en realidad seria una IP configurada en el equipo sobre una interfaz dummy

Que es una interfaz dummy?

Una interfaz dummy es un interfaz logica que no tiene conexion alguna con el mundo fisico, es decir, una interfaz que solo puede ser vista por el
mismo equipo donde esta configurado
Configuramos la interfaz en el nodo1, en nuestro caso ha-bgp1, esto lo haremos creando un servicio de systemd que se encargue de levantar la IP
ha-bgp1

[root@ha-bgp1 ~]# cat <<EOF > /etc/systemd/system/dummy0.service
[Unit]
Description=Dummy network interface
After=network.target
[Service]
Type=simple
ExecStartPre=-/sbin/ip link add dev dummy0 type dummy
ExecStartPre=/sbin/ip link set dummy0 up
ExecStartPre=/sbin/ip addr add dev dummy0 10.155.155.1/32
ExecStart=/usr/bin/sleep infinity
ExecStop=/sbin/ip link del dummy0
[Install]
WantedBy=multi-user.target
EOF

Luego habilitamos y levantamos dicho servicio

[root@ha-bgp1 ]# systemctl daemon-reload
[root@ha-bgp1 ]# systemctl start dummy0
[root@ha-bgp1 ]# systemctl enable dummy0

ha-bgp2
Luego debemos hacer lo mismo en ha-bgp2

[root@ha-bgp2 ~]# cat <<EOF > /etc/systemd/system/dummy0.service
[Unit]
Description=Dummy network interface
After=network.target
[Service]
Type=simple
ExecStartPre=-/sbin/ip link add dev dummy0 type dummy
ExecStartPre=/sbin/ip link set dummy0 up
ExecStartPre=/sbin/ip addr add dev dummy0 10.155.155.1/32
ExecStart=/usr/bin/sleep infinity
ExecStop=/sbin/ip link del dummy0
[Install]
WantedBy=multi-user.target
EOF

Habilitamos y levantamos el servicio

[root@ha-bgp2 ]# systemctl daemon-reload
[root@ha-bgp2 ]# systemctl start dummy0
[root@ha-bgp2 ]# systemctl enable dummy0

Instalar y configurar BIRD

Una vez que tenemos nuestra IP flotante configurada en la interfaz dummy de cada nodo, debemos configurar BIRD para que publique rutas para llegar a dicha IP utilizando el protocolo BGP
Movemos la configuracion de ejemplo para crear un archivo de configuracion vacio
ha-bgp1

[root@ha-bgp1 ~]# mv /etc/bird.conf /etc/bird.conf_original

ha-bgp2

[root@ha-bgp2 ~]# mv /etc/bird.conf /etc/bird.conf_original

Configuramos BIRD en el nodo1

Editamos el archivo de configuracion del servicio

[root@ha-bgp1 ~]# vi /etc/bird.conf

Y agregamos lo siguiente

log "/var/log/bird" all;
router id 10.100.1.151;
protocol device {
    scan time 60;
}
protocol direct {
    interface "dummy0";
}
protocol bgp router1 {
    local as 64513;
    source address 10.100.1.151;
    import none;
    export all;
    graceful restart on;
    next hop self;
    multihop 2;
    neighbor 10.100.1.150 as 64515;
}

Configuramos BIRD en el nodo2

Editamos el archivo de configuracion del servicio

[root@ha-bgp2 ~]# vi /etc/bird.conf

Y agregamos lo siguiente

log "/var/log/bird" all;
router id 10.100.2.151;
protocol device
{
    scan time 60;
}
protocol direct {
    interface "dummy0";
}
protocol bgp floatingip {
    local as 64514;
    source address 10.100.2.151;
    import none;
    export all;
    graceful restart on;
    next hop self;
    multihop 2;
    neighbor 10.100.2.150 as 64515;
}

Configuramos el firewall para que permita conexiones BGP

En caso de que tengamos habilitado el firewall del equipo debemos habilitar el puerto 179 tanto TCP como UDP
ha-bgp1

[root@ha-bgp1 ~]# firewall-cmd --permanent --add-port=179/tcp --add-port=179/udp
[root@ha-bgp1 ~]# firewall-cmd --reload

ha-bgp2

[root@ha-bgp2 ~]# firewall-cmd --permanent --add-port=179/tcp --add-port=179/udp
[root@ha-bgp2 ~]# firewall-cmd --reload

Iniciamos y habilitamos el servicio en ambos nodos

ha-bgp1

[root@ha-bgp1 ~]# systemctl start bird
[root@ha-bgp1 ~]# systemctl enable bird

ha-bgp2

[root@ha-bgp2 ~]# systemctl start bird
[root@ha-bgp2 ~]# systemctl enable bird

Configuracion de BIRD en el router

Finalmente debemos configurar el router para que aprenda las rutas publicadas por los nodos

[root@rt1 ~]# vi /etc/bird.conf

Agregamos lo siguiente:

filter floatingip {
  if net = 10.155.155.1/32 then accept;
}
router id 10.100.1.150;
protocol direct {
  interface "*";
}
protocol kernel {
  persist;      # No elimina las rutas cuando se baja el servicio
  scan time 20; # Escanea la tabla de ruteo del kernel cada 20 segundos
  export all;   # Carga todas las rutas a la tabla de rutas del kernel
}
protocol device {
  scan time 10; # Escanea las interfaces cada 10 segundos
}
protocol bgp nodo1 {
  import filter floatingip;
  hold time 10;
  local as 64515;
  neighbor 10.100.1.151 as 64513;
}
protocol bgp nodo2 {
  import filter floatingip;
  hold time 10;
  local as 64515;
  neighbor 10.100.2.151 as 64514;
}

En caso de que tengamos habilitado el firewall debemos crear las reglas correspondientes para permitir BGP

[root@rt1 ~]# firewall-cmd --permanent --add-port=179/tcp --add-port=179/udp
[root@rt1 ~]# firewall-cmd --reload

Finalmente iniciamos y habilitamos el servicio bird

[root@rt1 ~]# systemctl start bird
[root@rt1 ~]# systemctl enable bird

Podemos comprobar las rutas importadas por nuestro router de la siguiente manera

[root@rt1 ~]# birdc show route
BIRD 1.4.5 ready.
10.100.2.0/24      dev eth0 [direct1 11:54:43] * (240)
10.100.1.0/24      dev eth0 [direct1 11:54:43] * (240)
10.155.155.1/32    via 10.100.1.151 on eth0 [nodo1 11:54:47] * (100) [AS64513i]
                   via 10.100.2.151 on eth0 [nodo2 11:54:48] (100) [AS64514i]

Con esto tenemos configurada nuestra IP flotante entre equipos que no estan en la misma subnet. Esta configuracion la podemos utilizar con cualquier router que soporte el protocolo BGP

Forzar replicacion csync2

Forzar replicacion de directorios csync2

En caso de que tengamos implementada la replicacion de archivos via csync2es probable que existan casos en los que necesitemos forzar la replicacion de archivos manualmente ya sea para asegurarnos que esta todo replicado o para identificar problemas

Identificar configuración

Lo primero que tenemos que hacer es identificar para que configuración o para que directorio queremos forzar la replicacion

Las configuraciones las podemos encontrar dentro del directorio /etc/csync2/

El csync2 permite tener varias configuraciones diferentes y dentro de cada configuración podemos definir uno o mas directorios que se replicaran, estas configuraciones se definen en los archivos /etc/csync2/csync2_nombreconf.cfg, donde nombreconf es el nombre de la configuración.

Forzar la replicacion

Una vez identificado el nombre de la configuración podemos forzar la replicacion con el siguiente comando

csync2 -C nombreconf -xXvrB

Ejemplo, si nuestra configuración se llama www

csync2 -C www -xXvrB

Explicación de las opciones utilizadas:

  • C: Para identificar la configuración
  • x: Verifica todos los archivos y sus actualizaciones
  • X: Verifica archivos borrados
  • r: Verifica de forma recursiva directorios y sub-directorios
  • B: No bloquea la base de datos sqlite con grandes transacciones
  • v: habilita el modo verbose, se puede habilitar mas nivel de detalle agregando la opción varias veces. Ejemplo: csync2 -C www -xXvvvvrB

En algunos casos la replicacion falla debido a que faltan directorios padres en los otros equipos, en estos casos existen 2 soluciones:

  • Sincronizar la estructura de directorio a mano, esto seria crear a mano los directorios faltantes en los demás nodos
  • Ejecutar la sincronizacion en modo ejecución inicial, esto se hace agregando la opción -I, esto es un tanto arriesgado porque podemos llegar a perder datos, se debe ejecutar en el nodo que tiene la ultima versión de los datos

Configurar Let’s Encrypt con HaProxy en RHEL/CentOS/SL 7

Configurar Let's Encrypt con HaProxy en RHEL/CentOS/SL 7

Let's ecnypt nos sirve de CA para tener nuestros certificados firmados sin necesadidad de pagar por ello, este es el motivo por el cual se volvio tan famoso los ultimos tiempos, "Seguridad Gratis!"

En este documento demostraremos como configurar let's encrypt con HaProxy para proteger nuestros sitios HTTP

Primero debemos instalar todos los paquetes necesarios

Instalar paquetes necesarios

yum install git bc

Descargar letsencrypt

git clone https://github.com/letsencrypt/letsencrypt /opt/letsencrypt

Creacion del certificado

Antes de crear el certificado

Es muy importante que nos aseguremos que exista un registro DNS publico para el nombre de dominio para el cual queremos crear el certificado y que apunte a la IP de nuestro HaProxy, tambien debemos bajar el HaProxy debido a que el plugin standalone de la herramienta certbot necesita abrir los puertos 80 y 443, debemos de asegurarnos que desde internet se pueda acceder a los puerto 80 y 443 de nuestro servidor.

service haproxy stop

Ejecutar Let's Encrypt y obtener nuestro certificado

El comando letsencrypt-auto se encarga de instalar y configurar todas las dependencias que necesitamos para crear nuestros certificados.

Las opciones que utilizamos son las siguientes:

-d dominio.com: Aqui ingresamos el dominio para el cual queremos crear el certificado
-m maildeladministrador@dominio.com: Aqui ingresamos el correo del administrador, este correo nos podria ayudar en el futuro para recuperar los archivos .key
--agree-tos: Esta opcion acepta los terminos de servicio de let's encrypt

# /opt/letsencrypt/letsencrypt-auto certonly --standalone -d www.infratic.com -m vic.ad94@gmail.com --agree-tos
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Obtaining a new certificate
Performing the following challenges:
tls-sni-01 challenge for www.infratic.com
Waiting for verification...
Cleaning up challenges
 
IMPORTANT NOTES:
 - Congratulations! Your certificate and chain have been saved at
   /etc/letsencrypt/live/www.infratic.com/fullchain.pem. Your cert
   will expire on 2017-08-08. To obtain a new or tweaked version of
   this certificate in the future, simply run letsencrypt-auto again.
   To non-interactively renew *all* of your certificates, run
   "letsencrypt-auto renew"
 - If you like Certbot, please consider supporting our work by:
 
   Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
   Donating to EFF:                    https://eff.org/donate-le

Esto nos tuvo que crear los siguientes archivos en el directorio /etc/letsencrypt/live/dominio.com/

La descripcion de los diferentes archivos que se encuentran en dicho directorio son las siguientes

cert.pem: Your domain's certificate
chain.pem: The Let's Encrypt chain certificate
fullchain.pem: cert.pem and chain.pem combined
privkey.pem: Your certificate's private key

Configurar HaProxy para usear certificados

Preparando los ceriticados

Ahora debemos crear el .pem para el haproxy dentro de la carpeta /etc/haproxy/certs

# mkdir /etc/haproxy/certs
# cat /etc/letsencrypt/live/www.infratic.com/fullchain.pem /etc/letsencrypt/live/www.infratic.com/privkey.pem >> /etc/haproxy/certs/www.infratic.com.pem

Luego debemos crear el archivo /etc/haproxy/certs/list.txt que nos permitira tener varios dominios con sus respectivos certificados en el mismo balanceador

El archivo debe tener la siguiente sintaxis

/ruta/archivo/dominio.com.pem dominio.com

En este caso

/etc/haproxy/certs/www.infratic.com.pem       www.infratic.com

Para esto podemos usar el comando echo

# echo "/etc/haproxy/certs/www.infratic.com.pem       www.infratic.com" >>  /etc/haproxy/certs/list.txt

Configurando HaProxy

Lo que debemos hacer en nuestro HaProxy es definir la lista de certificados para el frontend o listen
En este caso configuraremos nuestro frontend el cual tendra un solo backend, recuerden que pueden tener mas backend utilizando ACLs en el frontend

Configurar el frontend

Creamos el frontend que escuchara en el puerto 443 y publicara nuestro certificado agregando las siguientes lineas al archivo /etc/haproxy/haproxy.cfg.

En el frontend se crea una ACL llamada letsencrypt-acl del tipo path_beg, esta ACL nos servira mas adelante al momento de renovar los certificados

frontend www-https
   bind 0.0.0.0:443 ssl crt-list /etc/haproxy/certs/list.txt
   reqadd X-Forwarded-Proto:\ https
   acl letsencrypt-acl path_beg /.well-known/acme-challenge/
   use_backend letsencrypt-backend if letsencrypt-acl
   default_backend www-backend

Configurar el backend

Creamos nuestro backend agregando las siguientes lineas al archivo /etc/haproxy/haproxy.cfg

backend b_webfarm
    mode http
    option forwardfor
    balance source
    cookie SRVNAME insert nocache
    server  av-app1 av-app1.infratic.com:80 cookie S1 check port 80 inter 10s weight 1
    server  av-app2 av-app2.infratic.com:80 cookie S2 check port 80 inter 10s weight 10

Luego debemos crear un backend que se utiliza para las validaciones de letsencrypt, que nos permite actualizar luego el certificado sin detener el servicio haproxy, este backend es referenciado en el fronted que creamos previamente, para eso agregamos las siguientes lineas al archivo /etc/haproxy/haproxy.cfg

backend letsencrypt-backend
   server letsencrypt 127.0.0.1:54321

Configurar renovacion automatica

Los certificados de Let's Encrypt tienen una duracion maxima de 90 dias, en este caso configuraremos la auto-renovacion cada 2 meses de forma a que tengamos tiempo para resolver problemas de autualizacion en caso de que se presenten
Para esta tarea crearemos un script que reciba como parametro el nombre del dominio y ejecute todos los comandos necesarios para la renovacion

# vi /opt/letsencrypt/renew.sh

Y agregamos lo siguiente

#!/bin/bash
dominio=$1
/opt/letsencrypt/letsencrypt-auto certonly --agree-tos --renew-by-default --standalone-supported-challenges http-01 --http-01-port 54321 -d $dominio
cat /etc/letsencrypt/live/$dominio/fullchain.pem /etc/letsencrypt/live/$dominio/privkey.pem >> /etc/haproxy/certs/$dominio.pem
service haproxy reload

Luego asignamos permisos de ejecucion al script

chmod 755 /opt/letsencrypt/renew.sh

Ahora debemos agregar una tarea cron que ejecute este script cada 2 meses

crontab -e

Y agregamos

00 02 * */2 * /opt/letsencrypt/renew.sh

Porque NO usar particiones para Luns o discos virtuales exclusivos para datos en RHEL/CentOS/SL

Porque NO usar particiones para Luns o discos virtuales exclusivos para datos en RHEL/CentOS/SL

El problema al momento de querer extender un disco virtual o lun de datos es que por mas que el sistema operativo reconozca el tamaño nuevo del disco, al momento de querer utilizar ese espacio nuevo es necesario extender la particion, o de utlima crear una particion nueva. Esto requiere reinicios en caso de que el disco se este utilizando, el reinicio es para que el S.O. vuelva a leer la tabla de particiones.

La forma de evitar tener que reiniciar cada vez que querramos extender nuestra LUN o disco virtual es implementando LVM sobre el disco directo sin particion

Primero crearemos el Volume Group de forma directa sobre la LUN o disco virtual

En este ejemplo lo haremos con un disco virtual exclusivo para datos de 10GB que es el /dev/sdb

Lo primero que tenemos que hacer es verificar que el S.O. vea el disco

# fdisk -l /dev/sdb
 
Disk /dev/sdb: 10.7 GB, 10737418240 bytes, 20971520 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes

Verificamos que el disco este vacio

# dd if=/dev/sdb bs=512 count=2 |strings
2+0 registros leídos
2+0 registros escritos
1024 bytes (1,0 kB) copiados, 0,00250696 s, 408 kB/s

No nos muestra nada por lo tanto esta vacio

Formateamos el disco completo como Physical Volume

# pvcreate /dev/sdb
  Physical volume "/dev/sdb" successfully created

Creamos un Volume Group

# vgcreate testvg /dev/sdb
  Volume group "testvg" successfully created

Luego creamos un Logical Volume de pruebas con 8G

# lvcreate -n testlv -L8G testvg
  Logical volume "testlv" created.

Creamos un FS XFS sobre el volumen y lo montamos en el /mnt

# mkfs.xfs /dev/testvg/testlv
# mount  /dev/testvg/testlv /mnt
# df -h /mnt/
S.ficheros                Tamaño Usados  Disp Uso% Montado en
/dev/mapper/testvg-testlv   8,0G    33M  8,0G   1% /mnt

Ahora ampliamos el disco desde la consola de administracion de VMWARE o el virtualizador que usemos (esto no lo voy a mostrar aca)

Es necesario re-escanear el disco desde el S.O. para que este vea el nuevo tamaño

Esto lo hacemos con el siguiente comando:

echo 1 > /sys/class/block/#dispositivo/device/rescan

Donde #dispositivo es el nombre de nuestro disco en cuestion, en este caso lo haremos sobre la sda.

echo 1 > /sys/class/block/sdb/device/rescan

Hecho esto el S.O. ya deberia reconocer el nuevo tamaño, por ende podemos ampliar nuestra particion

#  fdisk -l /dev/sdb
 
Disk /dev/sdb: 21.5 GB, 21474836480 bytes, 41943040 sectors
Units = sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes

Luego debemos extender el Physical Volume

# pvresize /dev/sdb
  Physical volume "/dev/sdb" changed
  1 physical volume(s) resized / 0 physical volume(s) not resized
# vgs testvg
  VG     #PV #LV #SN Attr   VSize  VFree
  testvg   1   1   0 wz--n- 20,00g 12,00g

Con el comando vgs verificamos que el Volume Group reconocio el nuevo espacio. Luego podemos extender nuestro Logical Volume para que llegue a los 18G

# lvresize -L18G /dev/testvg/testlv
  Size of logical volume testvg/testlv changed from 8,00 GiB (2048 extents) to 18,00 GiB (4608 extents).
  Logical volume testlv successfully resized.

Con esto ya podemos extender nuestro FileSystem

# xfs_growfs /dev/testvg/testlv
meta-data=/dev/mapper/testvg-testlv isize=256    agcount=4, agsize=524288 blks
         =                       sectsz=512   attr=2, projid32bit=1
         =                       crc=0        finobt=0
data     =                       bsize=4096   blocks=2097152, imaxpct=25
         =                       sunit=0      swidth=0 blks
naming   =version 2              bsize=4096   ascii-ci=0 ftype=0
log      =internal               bsize=4096   blocks=2560, version=2
         =                       sectsz=512   sunit=0 blks, lazy-count=1
realtime =none                   extsz=4096   blocks=0, rtextents=0
data blocks changed from 2097152 to 4718592
 
 
# df -h /dev/testvg/testlv
S.ficheros                Tamaño Usados  Disp Uso% Montado en
/dev/mapper/testvg-testlv    18G    33M   18G   1% /mnt

Con esto evitamos la necesidad de reiniciar nuestros equipos para extender nuestros discos virtuales o LUNs

Uno de los puntos mas importantes es detectar si los discos contienen datos antes de formatearlos.

Detectar si un disco esta siendo utilizado

Para detectar si un disco esta siendo utilizado podemos utilizar el comando dd de la siguiente forma:

dd if=/dev/sdb bs=512 count=2 |strings
Si el disco esta vacio deberia mostrar algo parecido a lo siguiente

# dd if=/dev/sdb bs=512 count=2 |strings
2+0 registros leídos
2+0 registros escritos
1024 bytes (1,0 kB) copiados, 0,00250696 s, 408 kB/s

En caso que tenga LVM deberia de mostrar algo parecido a lo siguiente:

# dd if=/dev/sdb bs=512 count=2 | strings
2+0 records in
2+0 records out
LABELONE
LVM2 0016hxjXHBW8Bm1xLnQZoEBDoKzWi0x8IRG
1024 bytes (1.0 kB) copied, 7.9217e-05 s, 12.9 MB/s

Extender disco virtual creado con la opción Thick Provision Eager Zeroed VMWare 5.5

Extender disco virtual creado con la opción Thick Provision Eager Zeroed

Si tenemos un disco del tipo Thick Provision Eager Zeroed compartido entre 2 nodos y la extendemos a traves de la interfaz grafica la maquina virtual tendrá errores al levantar, errores como:

An error was received from the ESX host while powering on VM nodo1.
Failed to start the virtual machine.
Module DiskEarly power on failed.
Cannot open the disk '/vmfs/volumes/55788bcb-66e4405e-8d19-441ea15c3790/nodo1/nodo1_1.vmdk' or one of the snapshot disks it depends on.
Thin/TBZ/Sparse disks cannot be opened in multiwriter mode.
VMware ESX cannot open the virtual disk "/vmfs/volumes/55788bcb-66e4405e-8d19-441ea15c3790/nodo1/nodo1_1.vmdk" for clustering. Verify that the virtual disk was created using the thick option.

El problema radica en que cuando se extiende a traves de interfaz grafica el disco con el nuevo tamaño queda en formato Thick Provison Lazy Zeroed y dicho formato no soporta estar activo en varias maquinas virtuales

La forma correcta de hacerlo

La forma correcta de hacerlo es la siguiente:

  • Apagamos todos los nodos que accedan al disco compartido
  • Ingresamos por SSH a uno de los nodos esx que tenga acceso al DS
  • Extendemos el disco virtual con el comando vmkfstools

La sintaxis del comando vmkfstools para este caso es la siguiente:

vmkfstools -X <nuevo_tamaño> -d eagerzeroedthick <ruta_del_disco_virtual>

La <ruta_del_disco_virtual> normalmente seria la siguiente:
/vmfs/volumes/<Nombre_del_DS>/<nombrevm>/<archivo>.vmdk

Ej:
Si queremos extender un disco virtual hasta los 100GB, teniendo en cuenta los siguientes datos:

DataStore: VSP
Nombre de la maquina virtual: nodo1
Nombre del disco: nodo1.vmdk

El comando seria

vmkfstools -X 100G -d eagerzeroedthick /vmfs/volumes/VSP/nodo1/nodo1.vmdk

Luego podemos encender de vuelta todas las maquinas virtuales y las mismas ya deberian de ver el disco con el nuevo tamaño

Es muy importante que la extension se haga de la forma comentada, caso contrario las VM no encenderian y se tendrian que realizar varias tareas para recuperar los datos

Configurar replicacion de archivos en RHEL/CentOS/SL 6/7 con lsyncd y csync2

csync2

Configurar replicacion de archivos en RHEL/CentOS/SL 6/7 con lsyncd y csync2

Introduccion

En caso de que necesitemos sincronizar en tiempo cercano al real directorios entre 2 o mas servidores podemos utilizar las herramientas lsync y csync2
csync2 es el encargado de la sincronizacion o copia como tal
lsync se encarga de detectar cualquier cambio en los directorios y de notificar a csync2 para que este los sincronice
En este documento veremos como instalar y configurar estas 2 herramientas para sincronizar entre 3 nodos el directorio /home y /web

Instalacion de los paquetes necesarios

RHEL/CentOS/SL 6

Para instalar lsyncd y csync2 en CentOS/RHEL 6 solo es necesario el repositorio EPEL y se instala directo vía yum como se detalla abajo.
Configurar el repositorio EPEL

rpm -Uhv https://dl.fedoraproject.org/pub/epel/epel-release-latest-6.noarch.rpm

Instalar los paquetes necesarios

yum install csync2 lsyncd xinetd

RHEL/CentOS/SL 7

Para versiones de RHEL 7 es necesario descargar la versión de csync2 que es para Fedora22, la misma se puede descargar desde la siguiente URL http://rpm.pbone.net/index.php3/stat/3/limit/1/srodzaj/1/dl/40/search/csync2/field%5B%5D/1/field%5B%5D/2
Una vez que descargamos instalamos con el comando:

yum install ftp://ftp.pbone.net/mirror/archive.fedoraproject.org/fedora/linux/releases/22/Everything/x86_64/os/Packages/c/csync2-1.34-15.fc22.x86_64.rpm

Configurar el repositorio EPEL

rpm -Uhv https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm

Instalar los paquetes necesarios

yum install csync2 lsyncd xinetd

Los pasos anteriores son los únicos que difieren en cuanto a la versión 7 de la versión 6, una vez instalado los pasos son los mismos en ambas versiones

Configurar csync2

Habilitar el servicio csync2 bajo xinetd:

Para habilitar el servicio csync2 a traves de xinetd

sed -i.bak 's#yes#no#g' /etc/xinetd.d/csync2

Luego levantamos el servicio xinetd

service xinetd start

Generar los .key para la autenticacion entre los servidores

Para la autenticacion entre los servidores es necesario crear un archivo .key, el mismo lo podemos crear con el comando csync2 y la opcion -k:

csync2 -k /etc/csync2/csync2.key

Configurar alias y directorios

Podemos crear varias configuraciones de csync2 que se encarguen de sincronizar 1 o mas directorios, en el siguiente ejemplo crearemos un alias home que se encarga de sincronizar el /home y otro que sincronizara el /web de nombre web
Para configurar csync2 debemos crear el archivo /etc/csync2/csync2_<alias>.cfg, cargando lo siguiente:

vim /etc/csync2/csync2_home.cfg

nossl * *;
group web
{
        host node1;
        host node2;
        host node3;
        key /etc/csync2/csync2.key;
        include /home/;
        exclude *.log;
        auto younger;
}

vim /etc/csync2/csync2_web.cfg

nossl * *;
group web
{
        host node1;
        host node2;
        host node3;
        key /etc/csync2/csync2.key;
        include /web/;
        auto younger;
}

Se debe tener en cuenta que el nombre de host (en este caso node1) debe ser el hostname o fqdn de cada servidor

Copiar los archivos del csync a los otros nodos:

Copiamos a los demas nodos:

scp /etc/csync2/* node2:/etc/csync2

scp /etc/csync2/* node3:/etc/csync3

Iniciar la replicacion de csync2

Para la sincronizacion inicial es necesario correr el siguiente comando por cada alias

csyncs2 -xXrvB -C <alias>

Ej:

csyncs2 -xXrvB -C home

Configurar lsyncd

Crear archivos de configuracion

Se deben agregar las siguientes lineas al archivo /etc/lsyncd.conf

vim /etc/lsyncd.conf

settings {
        logident        = "lsyncd",
        logfacility     = "user",
        logfile         = "/var/log/lsyncd.log",
        statusFile      = "/var/log/lsyncd_status.log",
        statusInterval  = 1
}

initSync = {
        delay = 1,
        maxProcesses = 1,
        action = function(inlet)
                local config = inlet.getConfig()
                local elist = inlet.getEvents(function(event)
                        return event.etype ~= "Init"
                end)
                local directory = string.sub(config.source, 1, -2)
                local paths = elist.getPaths(function(etype, path)
                        return "\t" .. config.syncid .. ":" .. directory .. path
                end)
                log("Normal", "Processing syncing list:\n", table.concat(paths, "\n"))
                spawn(elist, "/usr/sbin/csync2.sh", "-C", config.syncid, "-x")
        end,
        collect = function(agent, exitcode)
                local config = agent.config
                if not agent.isList and agent.etype == "Init" then
                        if exitcode == 0 then
                                log("Normal", "Startup of '", config.syncid, "' instance finished.")
                        elseif config.exitcodes and config.exitcodes[exitcode] == "again" then
                                log("Normal", "Retrying startup of '", config.syncid, "' instance.")
                                return "again"
                        else
                                log("Error", "Failure on startup of '", config.syncid, "' instance.")
                                terminate(-1)
                        end
                        return
                end

                local rc = config.exitcodes and config.exitcodes[exitcode]
                if rc == "die" then
                        return rc
                end
                if agent.isList then
                        if rc == "again" then
                                log("Normal", "Retrying events list on exitcode = ", exitcode)
                        else
                                log("Normal", "Finished events list = ", exitcode)
                        end
                else
                        if rc == "again" then
                                log("Normal", "Retrying ", agent.etype, " on ", agent.sourcePath, " = ", exitcode)
                        else
                                log("Normal", "Finished ", agent.etype, " on ", agent.sourcePath, " = ", exitcode)
                        end
                end
                return rc
        end,

        init = function(event)
                local inlet = event.inlet;
                local config = inlet.getConfig();
                log("Normal", "Recursive startup sync: ", config.syncid, ":", config.source)
                spawn(event, "/usr/sbin/csync2.sh", "-C", config.syncid, "-xXrvvB")
        end,

        prepare = function(config)
                if not config.syncid then
                        error("Missing 'syncid' parameter.", 4)
                end
                local c = "csync2_" .. config.syncid .. ".cfg"
                local f, err = io.open("/etc/csync2/" .. c, "r")
                if not f then
                        error("Invalid 'syncid' parameter: " .. err, 4)
                end
                f:close()
        end
}

local sources = {
        ["/home"] = "home",
        ["/web"] = "web"
}

for key, value in pairs(sources) do
        sync {initSync, source=key, syncid=value}
end

No olvidar cambiar el “home” por el alias que le asignara a cada directorio, este alias debe tener su configuracion correspondiente en la siguiente ruta:

/etc/csync2/csync2_<alias>.cfg

Ej:

/etc/csync2/csync2_home.cfg

Agregar la ruta del archivo de configuracion de lsync

Debemos agregar la ruta del archivo creado en el paso previo en el archivo /etc/sysconfig/lsyncd, eso lo podemos hacer con el comando sed:

sed -i.bak 's#^LSYNCD_OPTIONS=.*#LSYNCD_OPTIONS=" /etc/lsyncd.conf"#g' /etc/sysconfig/lsyncd

Crear el Script que ejecutara el lsync

Para sincronizar las carpetas el lsync intentara ejecutar el script /usr/sbin/csync2.sh, el cual debemos crear:

cat <<EOF > /usr/sbin/csync2.sh
#!/bin/bash

/usr/sbin/csync2 $@

exit 0

## Se agrega el exit 0 para que no se detenga el lsyncd al dar un error
EOF

Dar permisos de ejecución al Script

chmod 755 /usr/sbin/csync2.sh

Habilitar e Iniciar el servicio lsyncd:

Habilitamos el servicio para que incie junto con el Sistema Operativo

chkconfig lsyncd on
chkconfig xinetd on

Luego levantamos el servicio

service lsyncd start

Instalación y configuración de Keepalived para usarlo con HAProxy


KeepAlived

KeepAlived es el encargado de levantar las direcciones IPs flotantes, el mismo se encarga de verificar si el servicio HaProxy se esta ejecutando en los nodos de manera a hacer failover automatico en caso de que caiga el servicio HaProxy

Instalacion de KeepAlived

El servicio keepalived es parte del paque "keepalived" que podemos instalar via yum

yum install -y keepalived

El archivo de configuracion principal del servicio es el /etc/keepalived/keepalived.conf *en dicho archivo se establecen tanto las direcciones IP que manejara el servicio ademas de los metodos para definir en que nodo debe estar preferentemente la IP.

###Abajo un ejemplo de configuracion basico del servicio keepalived con 2 direcciones IP flotantes.

vrrp_script chk_haproxy {          # Script de verificacion 
  script "killall -0 haproxy"      # verifica si existe un pid de haproxy
  interval 2                       # dicha verificacion lo hace cada 2 segundos
  weight 2                         # agrega 2 puntos al nodo que tenga un pid de haproxy
}
 
 
vrrp_instance VI_1 {               # Nombre de la instancia
    state MASTER                   # El rol que cumple dicho nodo en cuanto a la instancia, puede ser MASTER o BACKUP
    interface eth0                 # Interfaz de red en la cual se levantara la IP
    virtual_router_id 1            # ID del Virtual Router   
    priority 101                   # Prioridad o puntaje del nodo, 100 para el BACKUP y 101 para el MASTER
    advert_int 1                   # Intervalo en segundo de verificacion entre nodos
    vrrp_unicast_bind 10.150.48.73  # IP interna del nodo
    vrrp_unicast_peer 10.150.48.74  # IP del segundo nodo
    virtual_ipaddress {            # Se establecen las direcciones IP a levantar, pueden ser varias 
        10.150.48.75
        10.150.48.77
    }
    authentication {               # Se establece el metodo de autenticacion entre nodos
        auth_type PASS
        auth_pass 1111
   }
 
   track_script {                  # Se configura el chequeo del proceso haproxy configurado previamente
        chk_haproxy
   }
}
 
vrrp_instance VI_2 {               # Nombre de la instancia
    state BACKUP                   # El rol que cumple dicho nodo en cuanto a la instancia, puede ser MASTER o BACKUP
    interface eth0                 # Interfaz de red en la cual se levantara la IP
    virtual_router_id 2            # ID del Virtual Router   
    priority 100                   # Prioridad o puntaje del nodo, 100 para el BACKUP y 101 para el MASTER
    advert_int 1                   # Intervalo en segundo de verificacion entre nodos
    vrrp_unicast_bind 10.150.48.73  # IP interna del nodo
    vrrp_unicast_peer 10.150.48.74  # IP del segundo nodo
    virtual_ipaddress {            # Se establecen las direcciones IP a levantar, pueden ser varias 
        10.150.48.76
        10.150.48.78
    }
    authentication {               # Se establece el metodo de autenticacion entre nodos
        auth_type PASS
        auth_pass 1111
   }
 
   track_script {                  # Se configura el chequeo del proceso haproxy configurado previamente
        chk_haproxy
   }
}

Se debe tener en cuenta que dicho archivo debe ser editado conforme al rol y a la IP de cada nodo.

Una vez creado el archivo de configuracion debemos habilitar e iniciar el servicio

service keepalived start
chkconfig keepalived on

Instalacion y configuracion de HAProxy en CentOS/RHEL 6/7

haproxy

HaProxy

Instalacion de HaProxy

El servicio haproxy es parte del paquete “haproxy” que podemos instalar via yum

yum install -y haproxy

El archivo de configuracion principal del servicio es el /etc/haproxy/haproxy.cfg dicho archivo esta separado en 3 partes, global, defaults y luego las configuraciones de los distintos servicios, los mismos pueden ser de tipo frontend, backend o listen

Abajo un ejemplo de configuracion basico de global:

global
    log             127.0.0.1 local2
    chroot          /var/lib/haproxy
    pidfile         /var/run/haproxy.pid
    maxconn         4000
    user            haproxy
    group           haproxy
    stats socket    /var/lib/haproxy/stats
    daemon

log : Direccion IP y facility que utilizara el demonio para guardar registros via Syslog
chroot : Ruta de ejecucion y enjaulamiento del demonio
pidfile : Ruta del archivo PID
maxconn : Numero maximo de conexiones concurrentes que aceptara el demonio
user : Usuario con el cual se ejecuta el demonio
group : Grupo con el cual se ejecuta el demonio
daemon : Modo en el que se levantara el proceso, por defecto daemon ya que se utiliza como servicio del S.O.
stats socket : Ruta del archivo socket para las estadisticas, el mismo puede ser consultado con el comando socat


Ejemplo de configuracion basica de defaults

Las configuraciones defaults son las que se setean por defecto para cada frontend, backend o listen que creemos.

defaults
    mode        http
    maxconn     3000
    option      dontlognull
    option      http-server-close
    option      redispatch
    retries     3
    timeout http-request        1m
    timeout queue               1m
    timeout connect             10s
    timeout client              10m
    timeout server              10m
    timeout http-keep-alive     10s
    timeout check               10s
    tcplog

mode : El modo default en el cual trabajaran los frontend, backend y listen, puede ser tcp o http
maxconn *: Cantidad maxima de conexiones para cada frontend, backend o listen *
option dontlognull : Evita que se registre en los logs repuestas nulas o vacias
option http-server-close : Fuerza el cierre de la conexion una vez que se recibio la respuesta del servidor backend
option redispatch : Activa la redistribución de sesión en caso de fallo de conexión
retries : Numero de intentos sobre un servidor luego de un fallo de conexion
timeout http-request *: Tiempo de espera para recibir la respuesta HTTP completa *
timeout queue : Tiempo de espera para las conexiones que estan en espera
timeout connect : Tiempo de espera para que el servidor acepte la conexion
timeout client : Tiempo maximo de inactividad del lado del cliente
timeout server : Tiempo maximo de inactividad del lado del servidor
timeout http-keep-alive : Tiempo maximo de espera para reutilizar una conexion HTTP
timeout check : Tiempo maximo de espera para que los servidores respondan los chequeos luego de abrir la conexion
tcplog : Modo de logueo, los mas utilizados son tcplog y httplog, tcplog registra las peticiones y que nodo atendio la misma, httplog registra la URL solicitada por el cliente


Definicion de frontend

El frontend es la parte de en la cual se especifica el tipo de aplicacion, los puertos y direcciones IP en las que escuchara el servicio, tambien se establecen las primeras reglas para las conexiones, el frontend seria el encargado de recibir las conexiones de los clientes.
Ejemplo de frontend para balancear conexiones HTTP:

frontend f_webfarm
    bind 192.168.1.1:80
    bind 192.168.1.2:80
    mode http
    acl acl_nagios hdr_end(host) -i nagios.infratic.com
    acl acl_git url_beg /git
    use_backend b_nagios if acl_nagios
    use_backend b_git if acl_git
    default_backend b_webfarm

bind : IP:Puerto que debe abrir el HaProxy para dicho frontend, pueden ser distintas direcciones IP y distintos puertos, tambien se puede poner 0.0.0.0:80 para que abra el puerto 80 de todas las IPs que tiene configurado el S.O.
mode : Modo en el que trabajara , si no se especifica hereda de la configuracion defaults.
acl : acl se utilizan para crear reglas o politicas, por ejemplo en el primer caso crea una acl para cuando se consulta a git.infratic.com, luego se crea una acl para cuando se consulta al /git

Las acl’s mas comunes son:
url_beg: String con el cual inicia la URL.
url_sub: String del subfolder.
url_end: String con el cual termina la URL.
hdr_end(host): Nombre al cual intenta conectarse el cliente.

use_backend : Se utiliza para determinar cual sera el backend a utilizar en caso de que se cumplan una o varias acl’s.
default_backend : Hace referencia al nombre del backend que se utilizara en caso de que no se cumpla ninguna regla de use_backend.


Definicion de backend

La configuracion backend es donde se determinan cuales son los nodos, cuales son los metodos de chequeo de vida y cuales son las opcion o metodos de balanceo
Ejemplo basico de backend

backend backend_web
    mode http
    balance source
    option http-server-close
    option httpclose
    option forceclose
    cookie SERVERID insert nocache indirect
    option httpchk GET /test HTTP/1.1\r\nHost:\ lb.infratic.com\r\n
    server jb1 jb1.infratic.com:8280 check cookie JB1 inter 10s
    server jb2 jb2.infratic.com:8280 check cookie JB2 inter 10s

mode : Modo en el que trabajara , si no se especifica hereda de la configuracion defaults.
balance : Se establece el tipo de balanceo a utilizar.

Los metodos mas utilizados son:
roundrobin: Distribuye la carga entre los nodos de manerae equitativa, es decir si tenemos 2 nodos para cada 10 conexiones caeran 5 a cada nodo.
source: Realiza el balanceo basandose en la direccion IP del cliente, es decir cada vez que un cliente se conecte ira al mismo nodo al cual fue en la primera conexion (siempre y cuando el nodo este activo)

option http-server-close : Fuerza a cerrar las conexiones http entre el balanceador y el nodo
option httpclose : Cierra la conexion http entre el cliente y el balanceador (Agregar el header Connection:Close)
option forceclose : Fuerza el cierre de las conexiones http entre el cliente y el balanceador
cookie SERVERID : Establece el ID del cookie para cada nodo backend
option httpchk : Se establece el metodo PING, seria el metodo por el cual el balanceador se dara cuenta cuando un nodo caiga, en el caso de HTTP el balanceador espera una respuesta diferente al codigo HTTP 500, si un nodo retorna HTTP 500 dicho nodo sera marcado como caido
server : Se configura cada nodo backend, la sintaxis es “server nombre fqdn_o_ip opciones”.

Las opciones mas usadas son:
check: Establece que se debe chequear el nodo
check port: Establece el puerto en el cual se realizara el chequeo
cookie: Establece el ID para los cookies de dicho nodo
inter: Intervalo entre chequeos
maxconn: Cantidad maxima de conexiones para dicho nodo
weight: Peso del nodo, mientras mas peso tengo, mas conexiones le caeran
backup: Se establece el nodo como backup, es decir que solo le caeran conexiones al momento que todos los nodos activos caigan


Definicion de listen

La configuracion listen es una mezcla entre frontend y backend, dentro del listen se establecen las direcciones IPs y puertos donde se escuchanb y a la vez se determinan cuales son los nodos, a diferencias del frontend tiene cierta limitaciones a la hora de tener varios backends basandose en la url para discriminar a que backend debe ir
Ejemplo basico de listen

listen MariaDB_farm 0.0.0.0:3306
    mode tcp
    option mysql-check user haproxy_check
    option tcplog
    balance source
    server  av-db1 av-db1.infratic.com:3306 check inter 5s
    server  av-db2 av-db2.infratic.com:3306 check inter 5s

Las opciones utilizadas en listen tienen el mismo significado tanto en backend como en frontend*