Recientemente me vi en la necesidad de utilizar una sola IP pública para varias VMs. La idea era ofrecer un servicio (OpenVPN en este caso) con fail over: tendría dos instancias de un mismo servicio, solamente una de ellas tendría asignada un IP público, pero si esa VM que tiene el IP público presenta algun problema (se apaga, pierde la red, etc) la otra asumiría el IP público y continuaría brindando el servicio.
En el escenario que mostraré tendré 3 VMs. Los detalles se muestran a continuación:
VM1 | VM1 | VM1 | |
---|---|---|---|
Hostname | KA1 | KA2 | KA3 |
OS | Ubuntu 20.04 | Ubuntu 20.04 | Ubuntu 20.04 |
CPU/MEM | 2vCPU/2GB | 2vCPU/2GB | 2vCPU/2GB |
enp0s3 (Clientes) | 192.168.43.101/24 | 192.168.43.102/24 | 192.168.43.103/24 |
enp0s8 (FloatingIP) | 192.168.1.11/24 | 192.168.1.11/24 | 192.168.1.11/24 |
enp0s9 (Managment) | 10.7.5.1/24 | 10.7.5.2/24 | 10.7.5.3/24 |
La interfaz
enp0s8
de cada una de las VMs sera la que recibirá el «IP público». En este caso, al ser un labarotorio utilizaré el IP 192.168.1.11/24 como «IP público»
El servicio que se utilizará para la configuración del FailOver será el KeepAlive utilizando el protocolo VRRP.
Manos a la obra!
Configuraciones iniciales
Luego del archiconocido apt update
y apt upgrade
de las VMs en donde vamos a trabajar lo primero será configurar las interfaces de red. Las interfaces enp0s3
y enp0s9
se utilizan para la conexión con los clientes y como red de managment respectivamente por lo que tendran un IP estático; mientras que la interfaz enp0s8
(como se mencionó anteriormente) será la que recibirá el FloatingIP de la «red pública», por lo que no se le asignará un IP, solamente se asegurará de que la interfaz está up.
A continuación se muestra el netplan de la primera VM (el resto es solo cambiar los IPs):
cat /etc/netplan/00-installer-config.yaml network: version: 2 renderer: networkd ethernets: enp0s3: addresses: - 192.168.43.101/24 nameservers: addresses: [192.168.43.1, 8.8.8.8] enp0s9: addresses: [10.7.5.1/24]
Si mostramos las interfaces con ip a
veremos que la interfaz enp0s8
esta DOWN:
ip a #[...] 3: enp0s8: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN group default qlen 1000 link/ether 08:00:27:45:34:18 brd ff:ff:ff:ff:ff:ff #[...]
La «encederemos» (en cada VM) y comprobamos el estado de las mismas:
ip link set dev enp0s8 up && ip a show enp0s8 #[...] 3: enp0s8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000 link/ether 08:00:27:e7:cd:c2 brd ff:ff:ff:ff:ff:ff inet6 fe80::a00:27ff:fee7:cdc2/64 scope link valid_lft forever preferred_lft forever
Instalando y configurando KeepAlive
En todas las VMs:
apt install keepalived -y
Una vez instalado tendremos que crear el archivo: /etc/keepalived/keepalived.conf
en cada una de las VMs, y luego modificarlos a conveniencia. Intentaré explicar cada una de las opciones que he utilizado:
LB1:
cat > /etc/keepalived/keepalived.conf << EOF vrrp_instance KA1 { state MASTER interface enp0s9 virtual_router_id 182 priority 207 advert_int 1 #notify "/etc/keepalived/keepalived_failover.sh" authentication { auth_type PASS auth_pass YouWillLoveMyPinkGun } virtual_ipaddress { 192.168.43.100/24 dev enp0s3 192.168.1.11/24 dev enp0s8 } virtual_routes { default via 192.168.1.1/24 } } EOF
El servicio de keepalived es muy flexible a la hora de las configuraciones que nos permite hacer, pero voy a limitarme a lo que nos interesa de momento:
- vrrp_instance KA1 {}: Identifica un bloque de definición de instancia VRRP.
- state: Especifica el estado de la instancia en uso estándar. En nuestro caso dejaremos a cada una de las VMs en el estado Master.
- interface: Especifica la interfaz de red que se utilizará para monitorear. Si esta interfaz se cae por cualquier motivo, entonces el keepalive tomará acciones. En este caso utilizaremos la interfaz de managment (`enp0s9`).
- virtual_router_id: Especifica a qué ID de enrutador VRRP pertenece la instancia. Se puede utilizar cualquier numero, pero tener en cuenta que este ID tiene q repetirse en los demás archivos de configuracion de las demas VMs.
- priority: Especifica la prioridad de la instancia en el enrutador VRRP. Este número varía entre las diferentes VMs. Mientras una VM tenga un numero de `priority` mayor será la que asuma el IP.
- advert_int: Especifica el intervalo en el cual se encuestan a las demas VMs del cluster para ver su disponibilidad. En segundos (establecido en 1 por defecto).
- notify: Especifica un script de shell que se ejecutará durante la transición al estado Master. En este momento esta comentado, pero en la ultima sección del artículo mostraré un ejemplo.
- authentication: Identificar un bloque de definición de autenticación VRRP. Es la contraseña que utilizan los diferentes integrantes del «cluster» del keepalive para comunicarse entre sí (esta configuración puede obviarse).
- virtual_ipaddress: Identifica un bloque de definición VIP VRRP. En esta sección se pueden configurar los IPs y las interfaces que tendra la VM que este actuando como Master. Nótese que se le puede asignar un IP a una interfaz que anteriormente ya estaba configurada, en el ejemplo la VM actuando como Master en su interfaz `enp0s3` asumirá el IP 192.168.43.100/24.
- virtual_routes: Permite especificar la ruta por defecto del Master. En este caso la ruta por defecto será el gateway de mis VMs, o sea mi ISP.
Inicializando los servicios
Una vez tengamos configurado el keepalived
a nuestro gusto podemos proceder a inicializar el servicio en la primera VM y checkear su estado:
systemctl start keepalived && systemctl status keepalived #[...] ● keepalived.service - Keepalive Daemon (LVS and VRRP) Loaded: loaded (/lib/systemd/system/keepalived.service; enabled; vendor preset: enabled) Active: active (running) since Wed 2022-07-13 16:54:06 UTC; 16s ago Main PID: 3844 (keepalived) Tasks: 2 (limit: 1066) Memory: 1.5M CGroup: /system.slice/keepalived.service ├─3844 /usr/sbin/keepalived --dont-fork └─3870 /usr/sbin/keepalived --dont-fork Jul 13 16:54:06 KA1 Keepalived_vrrp[3870]: Registering Kernel netlink reflector Jul 13 16:54:06 KA1 Keepalived_vrrp[3870]: Registering Kernel netlink command channel Jul 13 16:54:06 KA1 Keepalived_vrrp[3870]: Opening file '/etc/keepalived/keepalived.conf'. Jul 13 16:54:06 KA1 Keepalived_vrrp[3870]: (Line 11) Truncating auth_pass to 8 characters Jul 13 16:54:06 KA1 Keepalived_vrrp[3870]: Registering gratuitous ARP shared channel Jul 13 16:54:06 KA1 Keepalived_vrrp[3870]: (KA1) Entering BACKUP STATE (init) Jul 13 16:54:10 KA1 Keepalived_vrrp[3870]: (KA1) Entering MASTER STATE
Las interfaces que sean utilizadas por el servicio de
keepalived
tienen q estar en estado UP, sino el servicio dará error.Nótese que la contraseña es muy larga y será truncada a solamente 8 caracteres!
Ahora podremos comprobar los IPs de esta VM (actualmente en el estado Master del servicio):
ip a #[...] 2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000 link/ether 08:00:27:3f:cf:a8 brd ff:ff:ff:ff:ff:ff inet 192.168.43.101/24 brd 192.168.43.255 scope global enp0s3 valid_lft forever preferred_lft forever inet 192.168.43.100/24 scope global secondary enp0s3 valid_lft forever preferred_lft forever inet6 fe80::a00:27ff:fe3f:cfa8/64 scope link valid_lft forever preferred_lft forever 3: enp0s8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000 link/ether 08:00:27:45:34:18 brd ff:ff:ff:ff:ff:ff inet 192.168.1.11/24 scope global enp0s8 valid_lft forever preferred_lft forever inet6 fe80::a00:27ff:fe45:3418/64 scope link valid_lft forever preferred_lft forever 4: enp0s9: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000 link/ether 08:00:27:a9:7a:5d brd ff:ff:ff:ff:ff:ff inet 10.7.5.1/24 brd 10.7.5.255 scope global enp0s9 valid_lft forever preferred_lft forever inet6 fe80::a00:27ff:fea9:7a5d/64 scope link valid_lft forever preferred_lft forever
Como se puede comprobar la interfaz enp0s3
tiene dos IPs asignados, mientras que la interfaz enp0s8
(la que fungiría como IP público) tiene su único IP asignado (FloatingIP).
Bien, procedamos entonces a configurar la VM2 (KA2):
cat > /etc/keepalived/keepalived.conf << EOF vrrp_instance KA1 { state MASTER interface enp0s9 virtual_router_id 182 priority 150 advert_int 1 #notify "/etc/keepalived/keepalived_failover.sh" authentication { auth_type PASS auth_pass YouWillLoveMyPinkGun } virtual_ipaddress { 192.168.43.100/24 dev enp0s3 192.168.1.11/24 dev enp0s8 } virtual_routes { default via 192.168.43.1/24 } } EOF
Nótese que la prioridad en este es más baja que en el caso de la VM1 (KA1)
Solo resta inicializar y comprobar el estado del servicio.
systemctl start keepalived && systemctl status keepalived #[...] ● keepalived.service - Keepalive Daemon (LVS and VRRP) Loaded: loaded (/lib/systemd/system/keepalived.service; enabled; vendor preset: enabled) Active: active (running) since Wed 2022-07-13 16:56:10 UTC; 2min 39s ago Main PID: 3113 (keepalived) Tasks: 2 (limit: 1066) Memory: 1.6M CGroup: /system.slice/keepalived.service ├─3113 /usr/sbin/keepalived --dont-fork └─3139 /usr/sbin/keepalived --dont-fork Jul 13 16:56:10 KA2 Keepalived[3113]: WARNING - keepalived was build for newer Linux 5.4.166, running on Linux 5.4.0-96-generic #109-Ubuntu SMP Wed Jan 12 16> Jul 13 16:56:10 KA2 Keepalived[3113]: Command line: '/usr/sbin/keepalived' '--dont-fork' Jul 13 16:56:10 KA2 Keepalived[3113]: Opening file '/etc/keepalived/keepalived.conf'. Jul 13 16:56:10 KA2 Keepalived[3113]: Starting VRRP child process, pid=3139 Jul 13 16:56:10 KA2 Keepalived_vrrp[3139]: Registering Kernel netlink reflector Jul 13 16:56:10 KA2 Keepalived_vrrp[3139]: Registering Kernel netlink command channel Jul 13 16:56:10 KA2 Keepalived_vrrp[3139]: Opening file '/etc/keepalived/keepalived.conf'. Jul 13 16:56:10 KA2 Keepalived_vrrp[3139]: (Line 11) Truncating auth_pass to 8 characters Jul 13 16:56:10 KA2 Keepalived_vrrp[3139]: Registering gratuitous ARP shared channel Jul 13 16:56:10 KA2 Keepalived_vrrp[3139]: (KA1) Entering BACKUP STATE (init)
Comprobemos el estado de los IPs:
ip a #[...] 2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000 link/ether 08:00:27:b5:e3:b7 brd ff:ff:ff:ff:ff:ff inet 192.168.43.102/24 brd 192.168.43.255 scope global enp0s3 valid_lft forever preferred_lft forever inet6 fe80::a00:27ff:feb5:e3b7/64 scope link valid_lft forever preferred_lft forever 3: enp0s8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000 link/ether 08:00:27:e7:cd:c2 brd ff:ff:ff:ff:ff:ff inet6 fe80::a00:27ff:fee7:cdc2/64 scope link valid_lft forever preferred_lft forever 4: enp0s9: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000 link/ether 08:00:27:63:ea:59 brd ff:ff:ff:ff:ff:ff inet 10.7.5.2/24 brd 10.7.5.255 scope global enp0s9 valid_lft forever preferred_lft forever inet6 fe80::a00:27ff:fe63:ea59/64 scope link valid_lft forever preferred_lft forever
Como se puede apreciar la interfaz enp0s3
continúa con su único IP mientras que la interfaz enp0s3
no tiene ningun IP asignado.
Solamente queda configurar la ultima VM (KA3):
cat > /etc/keepalived/keepalived.conf << EOF vrrp_instance KA1 { state MASTER interface enp0s9 virtual_router_id 182 priority 100 advert_int 1 #notify /etc/keepalived/keepalived_failover.sh authentication { auth_type PASS auth_pass YouWillLoveMyPinkGun } virtual_ipaddress { 192.168.43.100/24 dev enp0s3 192.168.1.11/24 dev enp0s8 } virtual_routes { default via 192.168.43.1/24 } } EOF
Ahora, como en los anteriores casos, procedemos a inicializar el servicio y a checkearlo:
systemctl start keepalived && systemctl status keepalived #[...] ● keepalived.service - Keepalive Daemon (LVS and VRRP) Loaded: loaded (/lib/systemd/system/keepalived.service; enabled; vendor preset: enabled) Active: active (running) since Wed 2022-07-13 17:05:00 UTC; 21s ago Main PID: 2214 (keepalived) Tasks: 2 (limit: 1066) Memory: 1.5M CGroup: /system.slice/keepalived.service ├─2214 /usr/sbin/keepalived --dont-fork └─2226 /usr/sbin/keepalived --dont-fork Jul 13 17:05:00 KA3 Keepalived[2214]: Command line: '/usr/sbin/keepalived' '--dont-fork' Jul 13 17:05:00 KA3 Keepalived[2214]: Opening file '/etc/keepalived/keepalived.conf'. Jul 13 17:05:00 KA3 Keepalived[2214]: Starting VRRP child process, pid=2226 Jul 13 17:05:00 KA3 Keepalived_vrrp[2226]: Registering Kernel netlink reflector Jul 13 17:05:00 KA3 Keepalived_vrrp[2226]: Registering Kernel netlink command channel Jul 13 17:05:00 KA3 Keepalived_vrrp[2226]: Opening file '/etc/keepalived/keepalived.conf'. Jul 13 17:05:00 KA3 Keepalived_vrrp[2226]: (Line 11) Truncating auth_pass to 8 characters Jul 13 17:05:00 KA3 Keepalived_vrrp[2226]: Registering gratuitous ARP shared channel Jul 13 17:05:00 KA3 Keepalived_vrrp[2226]: (KA1) Entering BACKUP STATE (init)
Si checkeamos el estado de la interfaces y sus IPs asignados, podremos comprobar que ocurre lo mismo que con el caso de KA2.
Comprobando que el servicio funciona
Para comprobar que el servicio funciona simplemente apagaremos la interfaz de managmente enp0s9
de KA1. Recuerden q es esta la interfaz configurada para checkear.
ip link set dev enp0s9 down
Si ahora checkeamos el estado del servicio en KA1, y luego sus interfaces veremos los FloatingIP dejan de estar configurados:
systemctl status keepalived #[...] Jul 13 17:08:33 KA1 Keepalived_vrrp[3870]: Netlink reports enp0s9 down Jul 13 17:08:33 KA1 Keepalived_vrrp[3870]: (KA1) Entering FAULT STATE Jul 13 17:08:33 KA1 Keepalived_vrrp[3870]: (KA1) sent 0 priority
ip a #[...] 2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000 link/ether 08:00:27:3f:cf:a8 brd ff:ff:ff:ff:ff:ff inet 192.168.43.101/24 brd 192.168.43.255 scope global enp0s3 valid_lft forever preferred_lft forever inet6 fe80::a00:27ff:fe3f:cfa8/64 scope link valid_lft forever preferred_lft forever 3: enp0s8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000 link/ether 08:00:27:45:34:18 brd ff:ff:ff:ff:ff:ff inet6 fe80::a00:27ff:fe45:3418/64 scope link valid_lft forever preferred_lft forever 4: enp0s9: <BROADCAST,MULTICAST> mtu 1500 qdisc fq_codel state DOWN group default qlen 1000 link/ether 08:00:27:a9:7a:5d brd ff:ff:ff:ff:ff:ff
Ahora, si checkeamos el estado de KA2 veremos como la interfaz enp0s3
asume el FloatingIP configurado, mientras que con la interfaz enp0s8
ocurre igual:
ip a #[...] 2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000 link/ether 08:00:27:b5:e3:b7 brd ff:ff:ff:ff:ff:ff inet 192.168.43.102/24 brd 192.168.43.255 scope global enp0s3 valid_lft forever preferred_lft forever inet 192.168.43.100/24 scope global secondary enp0s3 valid_lft forever preferred_lft forever inet6 fe80::a00:27ff:feb5:e3b7/64 scope link valid_lft forever preferred_lft forever 3: enp0s8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000 link/ether 08:00:27:e7:cd:c2 brd ff:ff:ff:ff:ff:ff inet 192.168.1.11/24 scope global enp0s8 valid_lft forever preferred_lft forever inet6 fe80::a00:27ff:fee7:cdc2/64 scope link valid_lft forever preferred_lft forever 4: enp0s9: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000 link/ether 08:00:27:63:ea:59 brd ff:ff:ff:ff:ff:ff inet 10.7.5.2/24 brd 10.7.5.255 scope global enp0s9 valid_lft forever preferred_lft forever inet6 fe80::a00:27ff:fe63:ea59/64 scope link valid_lft forever preferred_lft forever
Si checkeamos el estado del servicio en KA2:
systemctl status keepalived #[...] Jul 13 16:56:10 KA2 Keepalived_vrrp[3139]: (KA1) Entering BACKUP STATE (init) Jul 13 17:08:35 KA2 Keepalived_vrrp[3139]: (KA1) Entering MASTER STATE
Lo mismo ocurrirá en el caso si apagamos la interfaz de managment de KA2, KA3 asumirá el role de Master.
Si encendemos la interfaz de managment de KA1, veremos como enseguida asume el role de Master, pues tiene una prioridad mayor que KA2:
ip link set dev enp0s9 up && systemctl status keepalived #[...] ● keepalived.service - Keepalive Daemon (LVS and VRRP) Loaded: loaded (/lib/systemd/system/keepalived.service; enabled; vendor preset: enabled) Active: active (running) since Wed 2022-07-13 16:54:06 UTC; 19min ago Main PID: 3844 (keepalived) Tasks: 2 (limit: 1066) Memory: 1.5M CGroup: /system.slice/keepalived.service ├─3844 /usr/sbin/keepalived --dont-fork └─3870 /usr/sbin/keepalived --dont-fork Jul 13 16:54:10 KA1 Keepalived_vrrp[3870]: (KA1) Entering MASTER STATE Jul 13 17:08:33 KA1 Keepalived_vrrp[3870]: Netlink reports enp0s9 down Jul 13 17:08:33 KA1 Keepalived_vrrp[3870]: (KA1) Entering FAULT STATE Jul 13 17:08:33 KA1 Keepalived_vrrp[3870]: (KA1) sent 0 priority Jul 13 17:13:29 KA1 Keepalived_vrrp[3870]: Netlink reports enp0s9 up Jul 13 17:13:29 KA1 Keepalived_vrrp[3870]: (KA1) Entering BACKUP STATE Jul 13 17:13:30 KA1 Keepalived_vrrp[3870]: (KA1) received lower priority (150) advert from 10.7.5.2 - discarding Jul 13 17:13:31 KA1 Keepalived_vrrp[3870]: (KA1) received lower priority (150) advert from 10.7.5.2 - discarding Jul 13 17:13:32 KA1 Keepalived_vrrp[3870]: (KA1) received lower priority (150) advert from 10.7.5.2 - discarding Jul 13 17:13:33 KA1 Keepalived_vrrp[3870]: (KA1) Entering MASTER STATE
La opcion «notify»
En el archivo de configuración hay una opción llamada notify. Esta configuración nos permite correr un script cada vez que keepalive detecte un cambio en sus estados (por ejemplo: de Backup a Master).
cat /etc/keepalived/keepalived.conf #[...] advert_int 1 notify "/etc/keepalived/keepalived_failover.sh" authentication { auth_type PASS #[...]
Como mencioné al principio en mis VMs corre un servicio de OpenVPN al cual se conectaran clientes desde alguna red pública, pero no es necesario que el servicio esté habilitado en todo momento en todas las VMs, por lo tanto, crearemos un script que, en dependencia del estado del servicio de keepalived
iniciará o detendrá el servicio de OpenVPN:
cat > /etc/keepalived/keepalived.conf << EOF #!/bin/bash TYPE=$1 NAME=$2 STATE=$3 case $STATE in "MASTER") /bin/systemctl start openvpn@server /bin/ip r del default via 172.17.52.1 ;; "BACKUP") /bin/systemctl stop openvpn@server /bin/ip r add default via 172.17.52.1 ;; "STOP") /bin/systemctl stop openvpn@server /bin/ip r add default via 172.17.52.1 ;; "FAULT") /bin/systemctl stop openvpn@server /bin/ip r add default via 172.17.52.1 exit 0 ;; *) /usr/bin/logger "keepalived unknown state" exit 1 ;; esac echo "$1 $2 has transitioned to the $3 state with a priority of $4" >> /opt/k_logs/keepalived_status EOF
El notify
pasa 4 parámetros al script:
- $1: Grupo o instancia: Indicación de si la notificación la activa un grupo VRRP o una instancia VRRP en particular.
- $2: Nombre del grupo o instancia
- $3: Indica qué tipo de transición se encuentra el grupo o instancia
- $4: La prioridad
Si hace cualquier cambio en los archivos de configuración (descomentar la opcion del notify en este caso) del servicio
keepalived
recuerde reinicializar el servicio.
En el script, como parte del logueo, indiqué que en /opt/k_logs/keepalived_status
me muestre los cambios, si configuramos el script en KA1, apagamos e iniciamos la interfaz de managment y luego leemos el archivo de log veremos algo como:
cat /var/run/keepalived_status #[...] INSTANCE KA1 has transitioned to the FAULT state with a priority of 207 INSTANCE KA1 has transitioned to the BACKUP state with a priority of 207 INSTANCE KA1 has transitioned to the MASTER state with a priority of 207
Y de esta forma sencilla podemos controlar el servicio de OpenVPN en depedencia del estado del servicio de keepalived
+++++++++++++++++++++++++++++++++++++++++++++++++++
Autores:
Frank Morales
Franco Díaz