2026
web-infra
- Docker
- Nginx
- HAProxy
- Traefik
- OpenSSL
- iptables
- Locust
- Bash
Prácticas de Servidores Web de Altas Prestaciones en la Universidad de Granada. Cada una de las cinco se apoya en la anterior, así que juntas forman una sola infraestructura: ocho contenedores Apache en Docker; Nginx, HAProxy y Traefik delante, con round robin, menos conexiones, tolerancia a fallos y un script de escalado; HTTPS con mi propia autoridad certificadora raíz e intermedia; iptables con denegación por defecto en cada contenedor y límites de peticiones contra inundaciones; y pruebas de carga con Apache Bench y Locust distribuido. Unas 42 horas entre las cinco.
Cómo funciona
Ocho servidores, una dirección
La práctica 1 montó la granja: una imagen de Apache + PHP, ocho contenedores a partir de ella, en dos redes Docker con direcciones fijas. La red de servicios es interna, así que nada de fuera puede llegar a ella.
La práctica 2 puso un balanceador delante: la misma granja detrás de Nginx, HAProxy y Traefik por turnos, con round robin y menos conexiones. Si se paran dos nodos, el balanceador simplemente reparte sin ellos; al arrancarlos, vuelven a entrar.
Una cadena de confianza firmada por mí
La práctica 3 pasó todo a HTTPS. Primero un único certificado autofirmado; después, como tarea avanzada, mi propia pequeña autoridad certificadora: una raíz que firma una intermedia, que firma el certificado de los servidores. Apache sirve la cadena entera en un solo archivo.
Los servidores solo hablan TLS 1.2 y 1.3 con cifrados robustos, y el balanceador Nginx reutiliza sesiones TLS y habla HTTP/2. El navegador sigue diciendo «No seguro», y con razón: no conoce mi raíz. El tráfico va cifrado igualmente.
Una autoridad certificadora raíz firma una intermedia, que firma el certificado del servidor; las tres van juntas en cadena-completa.crt para Apache. Se aceptan TLS 1.2 y 1.3; TLS 1.0 y 1.1 se rechazan.
Denegar por defecto
La práctica 4 dio a cada contenedor su propio cortafuegos. Un script de arranque carga las reglas antes de que arranque Apache: descartarlo todo y dejar pasar solo HTTPS desde la dirección del balanceador. Entrar directamente a un servidor web, o a la página de estado del balanceador, ya no funciona.
El balanceador tiene sus propios límites contra inundaciones: como mucho 20 conexiones y 10 peticiones por segundo por dirección (ráfagas de 20), tiempos de espera cortos, sin versión en las cabeceras y sin respuesta al ping.
Reglas: políticas de entrada, salida y reenvío en DROP; aceptar loopback, conexiones establecidas y HTTPS desde el balanceador en 192.168.10.50. Resultados: nginx a web1 por 443 aceptado; el host a web1 por 80 descartado; ping a nginx descartado; una 21.ª conexión desde una dirección descartada; las peticiones por encima de 10 por segundo, tras una ráfaga de 20, respondidas con 503.
El benchmark que chocó con mi propio cortafuegos
La práctica 5 puso a prueba la granja terminada de dos formas. Apache Bench lanzó 10.000 peticiones HTTPS, de 100 en 100, desde un solo contenedor: unas 1.354 peticiones por segundo a 73 ms. Pero 9.906 fueron 503. Desde una sola dirección, un benchmark es idéntico a la inundación que la práctica 4 estaba hecha para frenar, y el límite de peticiones hizo su trabajo.
Locust se comportó más como personas: 100 usuarios, sumando 20 por segundo, repartidos en seis contenedores trabajadores, cada uno con su dirección, con pausas entre páginas. Cero errores. El primer número medía mis propias defensas, no la granja.
Apache Bench: 10.000 peticiones HTTPS, 100 concurrentes, desde una dirección; 9.906 recibieron un 503 del límite de peticiones y 94 salieron bien, a unas 1.354 peticiones por segundo y 73 ms de media. Locust: 100 usuarios en seis trabajadores, 0 % de errores.
Qué salió mal
En Nginx, menos conexiones se comportaba exactamente igual que round robin. Con una sola persona recargando la página nunca hay más de una conexión abierta, así que «el servidor con menos» es simplemente el siguiente de la cola. El algoritmo solo muestra la diferencia con carga concurrente real, y con HAProxy, en la misma prueba, sí se notó.
La mayor sorpresa fue el benchmark (figura 4). Apache Bench daba una tasa buena, pero 9.906 de sus 10.000 peticiones fueron 503 del límite que yo mismo había puesto en la práctica anterior. El rendimiento dice poco hasta que lees la línea de peticiones fallidas: un «no» rápido sigue siendo rápido.