Un balanceador solo aporta alta disponibilidad si deja de enviar tráfico a los servidores que fallan. HAProxy lo hace con comprobaciones de salud activas: consulta periódicamente cada backend y lo saca de la rotación cuando no responde como se espera, sin esperar a que falle una petición real. En este tutorial configurarás en Ubuntu 24.04 comprobaciones HTTP con umbrales de caída y recuperación, un servidor de respaldo, una página de mantenimiento cuando no queda ningún backend y el drenado manual de servidores para hacer mantenimiento sin cortar conexiones. Todo se prueba con un laboratorio de tres backends en la misma máquina.
Requisitos previos
- Un servidor con Ubuntu 24.04 LTS, por ejemplo un VPS de CubePath.
- Un usuario no root con privilegios
sudo. - El puerto 80 libre.
En producción los backends estarán en otras máquinas, normalmente en una red privada; bastará con cambiar las direcciones de las líneas server. En el laboratorio son tres sitios de Nginx escuchando en 127.0.0.1.
Paso 1: Instalar HAProxy, Nginx y socat
Ubuntu 24.04 incluye HAProxy 2.8 LTS. Nginx servirá los backends de prueba y socat permite hablar con el socket de administración de HAProxy:
sudo apt update
sudo apt install haproxy nginx socat
Desactiva el sitio por defecto de Nginx, que ocupa el puerto 80 que usará HAProxy:
sudo rm /etc/nginx/sites-enabled/default
sudo systemctl reload nginx
Si usas UFW, permite HTTP:
sudo ufw allow 80/tcp
Paso 2: Crear backends con un endpoint de salud
Una buena comprobación de salud no se limita a ver si el puerto responde: consulta una ruta que la propia aplicación controla. Así la aplicación puede declararse no disponible (por ejemplo, si pierde la conexión con la base de datos) y también puedes sacar un servidor de servicio a propósito.
En el laboratorio, /health devuelve 200 mientras exista el archivo /var/www/backendN/health y 503 si se borra. Crea los archivos:
for i in 1 2 3; do
sudo mkdir -p /var/www/backend$i
echo OK | sudo tee /var/www/backend$i/health > /dev/null
done
Crea la configuración de los tres backends:
sudo nano /etc/nginx/conf.d/test-backends.conf
server {
listen 127.0.0.1:8081;
root /var/www/backend1;
default_type text/plain;
location = /health { try_files /health =503; }
location / { return 200 "backend-1\n"; }
}
server {
listen 127.0.0.1:8082;
root /var/www/backend2;
default_type text/plain;
location = /health { try_files /health =503; }
location / { return 200 "backend-2\n"; }
}
server {
listen 127.0.0.1:8083;
root /var/www/backend3;
default_type text/plain;
location = /health { try_files /health =503; }
location / { return 200 "backend-3\n"; }
}
Valida, recarga y prueba el endpoint:
sudo nginx -t && sudo systemctl reload nginx
curl -i http://127.0.0.1:8081/health
HTTP/1.1 200 OK
Content-Type: text/plain
OK
Paso 3: Configurar HAProxy con comprobaciones HTTP
Primero crea la página que HAProxy devolverá cuando no quede ningún servidor disponible. Un errorfile es una respuesta HTTP completa, cabeceras incluidas:
sudo nano /etc/haproxy/errors/maintenance.http
HTTP/1.1 503 Service Unavailable
Content-Type: text/html; charset=utf-8
Cache-Control: no-store
Connection: close
<!DOCTYPE html>
<html lang="es">
<head><meta charset="utf-8"><title>Mantenimiento</title></head>
<body>
<h1>Estamos en mantenimiento</h1>
<p>Vuelve a intentarlo en unos minutos.</p>
</body>
</html>
Guarda una copia de la configuración original de HAProxy y ábrela:
sudo cp /etc/haproxy/haproxy.cfg /etc/haproxy/haproxy.cfg.orig
sudo nano /etc/haproxy/haproxy.cfg
Sustituye su contenido por:
global
log /dev/log local0
log /dev/log local1 notice
chroot /var/lib/haproxy
stats socket /run/haproxy/admin.sock mode 660 level admin
stats timeout 30s
user haproxy
group haproxy
daemon
defaults
log global
mode http
option httplog
option dontlognull
option redispatch
retries 2
timeout connect 3s
timeout client 30s
timeout server 30s
frontend web_in
bind :80
default_backend web_servers
backend web_servers
balance roundrobin
option httpchk
http-check send meth GET uri /health ver HTTP/1.1 hdr Host localhost
http-check expect status 200
default-server inter 2s fall 3 rise 2 on-marked-down shutdown-sessions
errorfile 503 /etc/haproxy/errors/maintenance.http
server web1 127.0.0.1:8081 check
server web2 127.0.0.1:8082 check
server web3 127.0.0.1:8083 check backup
frontend stats
bind 127.0.0.1:8404
stats enable
stats uri /stats
stats refresh 5s
Qué controla cada parte:
| Directiva | Efecto |
|---|---|
option httpchk + http-check send | Envía GET /health con cabecera Host a cada servidor. Sin httpchk, check solo comprueba que el puerto TCP acepta conexiones. |
http-check expect status 200 | Solo un 200 cuenta como éxito. Sin esta línea, cualquier 2xx o 3xx vale. |
inter 2s | Intervalo entre comprobaciones. |
fall 3 | Tres fallos seguidos marcan el servidor como DOWN (unos 6 segundos con inter 2s). |
rise 2 | Dos éxitos seguidos lo devuelven a UP, para no meter en rotación un servidor que aún está arrancando. |
on-marked-down shutdown-sessions | Cierra las conexiones abiertas con un servidor cuando se marca como caído, en lugar de dejarlas colgadas. |
backup | web3 solo recibe tráfico cuando no queda ningún servidor principal disponible. |
option redispatch + retries 2 | Si falla la conexión con un servidor, la petición se reintenta en otro. |
errorfile 503 | Página que se devuelve cuando el backend no tiene ningún servidor disponible. |
default-server aplica esos parámetros a todas las líneas server del backend; puedes sobrescribirlos en una línea concreta.
Valida y recarga:
sudo haproxy -c -f /etc/haproxy/haproxy.cfg
sudo systemctl reload haproxy
Configuration file is valid
Comprueba que el tráfico se reparte entre los dos servidores principales:
for i in $(seq 1 4); do curl -s http://localhost/; done
backend-1
backend-2
backend-1
backend-2
backend-3 no aparece porque es de respaldo.
Paso 4: Ver el estado de los servidores
El socket de administración devuelve el estado de cada servidor. La salida de show stat es CSV; los campos 1, 2 y 18 son el backend, el servidor y su estado:
echo "show stat" | sudo socat stdio /run/haproxy/admin.sock | cut -d, -f1,2,18 | grep web_servers
web_servers,web1,UP
web_servers,web2,UP
web_servers,web3,UP
web_servers,BACKEND,UP
Para ver la página de estadísticas en el navegador, abre un túnel SSH desde tu equipo con ssh -L 8404:127.0.0.1:8404 your_user@your_server_ip y visita http://localhost:8404/stats.
Deja abierta una segunda terminal siguiendo el log de HAProxy para ver los cambios de estado en los pasos siguientes:
sudo tail -f /var/log/haproxy.log
Paso 5: Probar la conmutación por error
Simula que web1 deja de estar sano borrando su archivo de salud:
sudo rm /var/www/backend1/health
En unos 6 segundos (tres comprobaciones fallidas) aparece en el log:
Server web_servers/web1 is DOWN, reason: Layer7 wrong status, code: 503, info: "Service Temporarily Unavailable", check duration: 0ms. 1 active and 1 backup servers left. 0 sessions active, 0 requeued, 0 remaining in queue.
Ahora todo el tráfico va a web2:
for i in $(seq 1 3); do curl -s http://localhost/; done
backend-2
backend-2
backend-2
Haz caer también web2. Al no quedar servidores principales, entra el de respaldo:
sudo rm /var/www/backend2/health
sleep 8
for i in $(seq 1 2); do curl -s http://localhost/; done
backend-3
backend-3
El log lo indica con 0 active and 1 backup servers left. Running on backup. Por último, haz caer web3:
sudo rm /var/www/backend3/health
sleep 8
curl -i http://localhost/
HTTP/1.1 503 Service Unavailable
content-type: text/html; charset=utf-8
cache-control: no-store
...
<h1>Estamos en mantenimiento</h1>
El log registra backend web_servers has no server available!. Los clientes ven tu página de mantenimiento en lugar de un error genérico.
Restaura los tres servidores:
for i in 1 2 3; do echo OK | sudo tee /var/www/backend$i/health > /dev/null; done
Tras dos comprobaciones correctas (unos 4 segundos) cada servidor vuelve a UP:
Server web_servers/web1 is UP, reason: Layer7 check passed, code: 200, check duration: 0ms. 1 active and 0 backup servers online. 0 sessions requeued, 0 total in queue.
Paso 6: Drenar un servidor para hacer mantenimiento
Antes de actualizar o reiniciar un backend, conviene sacarlo de rotación de forma ordenada. El estado drain deja de enviarle conexiones nuevas pero respeta las que ya tiene abiertas:
echo "set server web_servers/web1 state drain" | sudo socat stdio /run/haproxy/admin.sock
echo "show stat" | sudo socat stdio /run/haproxy/admin.sock | cut -d, -f1,2,18 | grep web_servers
web_servers,web1,DRAIN
web_servers,web2,UP
web_servers,web3,UP
web_servers,BACKEND,UP
Cuando termines el mantenimiento, devuélvelo a la rotación:
echo "set server web_servers/web1 state ready" | sudo socat stdio /run/haproxy/admin.sock
Otros estados útiles son maint, que lo saca por completo y detiene también sus comprobaciones, y set server web_servers/web1 weight 50% para reducir su carga temporalmente. Los cambios hechos por el socket solo viven en memoria: se pierden si reinicias o recargas HAProxy.
Consejoel mismo efecto se consigue desde la aplicación. Si tu endpoint de salud devuelve
503durante el apagado ordenado del servicio, HAProxy lo sacará solo de la rotación antes de que deje de atender.
Paso 7: Ajustar la velocidad de detección
Con inter 2s fall 3 un servidor caído tarda unos 6 segundos en salir de la rotación. Para reaccionar antes sin multiplicar las comprobaciones normales, HAProxy permite intervalos distintos según el estado:
default-server inter 3s fastinter 1s downinter 5s fall 3 rise 2 on-marked-down shutdown-sessions
fastinter 1s: intervalo usado mientras un servidor está cambiando de estado (tras el primer fallo o el primer éxito). La caída se detecta en unos 3 segundos.downinter 5s: intervalo para servidores ya caídos, para no insistir innecesariamente.
Si además quieres validar el contenido de la respuesta y no solo el código, sustituye la línea http-check expect por http-check expect string OK, que exige que el cuerpo contenga ese texto.
Paso 8: Comprobaciones para servicios que no son HTTP
Para backends TCP (bases de datos, Redis, colas) usa mode tcp en el backend. HAProxy incluye comprobaciones que hablan el protocolo del servicio, más fiables que comprobar solo el puerto. Estos bloques son de referencia: adáptalos a tus direcciones y añádelos junto a un frontend en mode tcp.
Redis, enviando PING y esperando +PONG:
backend redis_servers
mode tcp
option tcp-check
tcp-check send PING\r\n
tcp-check expect string +PONG
server redis1 10.0.0.31:6379 check inter 2s fall 3 rise 2
PostgreSQL y MySQL o MariaDB, con un usuario dedicado sin privilegios que debes crear en la base de datos:
backend pg_servers
mode tcp
option pgsql-check user haproxy_check
server pg1 10.0.0.41:5432 check
backend mysql_servers
mode tcp
option mysql-check user haproxy_check post-41
server db1 10.0.0.51:3306 check
Estas comprobaciones confirman que el servidor responde a nivel de protocolo, pero no si es el primario o una réplica. Para distinguirlo necesitas un endpoint HTTP auxiliar (por ejemplo, Patroni expone uno para PostgreSQL) y combinar check port <puerto> con option httpchk.
Solución de problemas
- Todos los servidores en
DOWNconLayer4 connection problem: HAProxy no llega al puerto. Comprueba la dirección y el puerto concurl -i http://<ip>:<puerto>/healthdesde el balanceador y revisa el cortafuegos del backend. Layer7 wrong status, code: 404: la ruta de salud no existe o depende del dominio. Revisauriy la cabeceraHostdehttp-check send.Layer7 timeout: el endpoint tarda más que el tiempo límite de la comprobación (por defecto el valor deinter, otimeout checksi lo defines endefaults). Haz que/healthsea rápido y no ejecute consultas pesadas.- HAProxy no arranca tras editar
errorfile: el archivo debe empezar por la línea de estado HTTP y no superar el tamaño de un búfer (16 KB por defecto). Revisasudo haproxy -c -f /etc/haproxy/haproxy.cfg. - No ves cambios de estado en
/var/log/haproxy.log: comprueba que rsyslog está activo consystemctl status rsyslog; como alternativa, usasudo journalctl -u haproxy -f.
Conclusión
HAProxy ya saca automáticamente de la rotación los servidores que fallan su comprobación de salud, usa un servidor de respaldo cuando caen los principales, muestra una página de mantenimiento si no queda ninguno y te permite drenar servidores para hacer mantenimiento sin cortes. Como siguientes pasos, añade terminación TLS al frontend, envía los cambios de estado a tu sistema de alertas y elimina el punto único de fallo del propio balanceador con un segundo nodo HAProxy y keepalived compartiendo una IP flotante.
