Diseño de sistemas · Escalabilidad

De un nodo
a muchos

Por qué un solo servidor no alcanza y cómo un load balancer lo resuelve.

→ avanzar · S notas del orador · F pantalla completa · O vista general

Deslizá hacia abajo para recorrerla. Las simulaciones se activan al llegar a cada una.

github.com/gastonprieto/presentacion-load-balancer
El punto de partida

Un cliente, un servidor

requestoklentaerror

Así arrancan casi todos los sistemas: toda la app corre en una sola máquina.

  • ✅ Simple de desplegar y de razonar
  • ✅ Barato: una sola VM
  • ✅ Sin coordinación entre nodos
  • ⚠️ …pero todo depende de esa máquina
Cómo leer el servidor

2/4 = requests en proceso / capacidad (workers).
La barra es la carga. Los cuadraditos naranjas a la izquierda son la cola.

Problema 1

Single Point of Failure

Si ese servidor cae (falla de hardware, kernel panic, OOM, un rm -rf…), cae todo el sistema.

  • Disponibilidad del sistema = disponibilidad de esa máquina
  • 99,9% ≈ 8,7 h caído por año
  • No hay a quién derivarle el tráfico
Disponibilidad · últimos 40 s
Problema 2

Saturación ante picos de tráfico

4 workers × ~0,8 s por request → capacidad máxima ≈ 5 req/s.

Tráfico entrante
2 req/s
Utilización: 40%
  • Por encima de la capacidad, la cola crece → la latencia se dispara
  • Cola llena → 503 Service Unavailable
Problema 3

Escalar verticalmente tiene techo

Scale up: «comprar una máquina más grande».

Tráfico entrante
4 req/s
80%
  • 💸 El costo crece más rápido que la capacidad
  • ⏻ Cada salto requiere reiniciar → downtime
  • 🧱 Hay un techo físico
  • ☝️ Sigue siendo un solo nodo (SPOF)
Problema 4

Cada deploy es una caída

Para actualizar hay que detener el proceso. Mientras arranca la versión nueva, nadie atiende.

  • Los requests en curso se cortan
  • Se termina deployando de noche o "en ventana de mantenimiento"
  • Menos deploys → cambios más grandes → más riesgo
Disponibilidad · últimos 40 s
La solución

Escalado horizontal + Load Balancer

En vez de una máquina más grande, muchas máquinas iguales. El LB es el único punto de entrada y reparte el tráfico.

Tráfico entrante
3 req/s
60%
  • El cliente solo conoce la IP del LB
  • Capacidad ≈ N × capacidad de un nodo
Algoritmos · 1

Round Robin

Por turnos: 1→S1, 2→S2, 3→S3, 4→S1…

  • ✅ Trivial de implementar, casi sin estado
  • ✅ Reparto parejo si los requests y los servidores son parecidos
  • ⚠️ No mira cuán ocupado está cada servidor
  • ⚠️ Requests muy dispares → servidores desbalanceados
Pseudocódigo

i = (i + 1) % N
return servers[i]

Algoritmos · 2

Least Connections

● grande = request pesado (8× más lento)

El LB elige el servidor con menos conexiones activas.

  • Compará la latencia p95 con cada algoritmo
  • Ideal cuando la duración varía mucho: uploads, reportes, websockets
  • Costo: el LB tiene que llevar la cuenta de conexiones
Medir bien

La mediana engaña: p50, p95, p99

● grande = request pesado (8× más lento)

El promedio y la mediana esconden a los usuarios con mala suerte: unos pocos requests lentos estiran la cola.

  • Una página que hace 20 requests tiene 1 − 0,95²⁰ ≈ 64% de chance de pegarle a la p95
  • Por eso los SLOs se definen en p95/p99, no en el promedio
Algoritmos · 3

IP Hash y Weighted

servidor = hash(IP) mod N: el mismo cliente siempre cae en el mismo servidor (mirá los colores).

  • ✅ Afinidad: aprovecha cachés locales
  • ⚠️ Si cambia N, cambia el mapeo de casi todos los clientes
  • 💡 Por eso existe el consistent hashing: al agregar un nodo solo se mueve ~1/N de las claves
  • ⚠️ Muchos usuarios detrás de una misma IP (NAT) → desbalance

Servidores de distinto tamaño: S1 tiene peso 3, así que recibe 3 de cada 5 requests.

  • ✅ Mezclar máquinas viejas y nuevas
  • ✅ Canary: mandar 5% del tráfico a la versión nueva
  • Mirá los contadores "atendidas": ~60% / 20% / 20%
Smooth weighted RR (Nginx)

pesos 3,1,1 → S1 S1 S2 S1 S3 …

Resiliencia

Health checks

👆 clic en un servidor para tirarlo / levantarlo

El LB consulta periódicamente GET /health a cada servidor. Si falla N veces seguidas, lo saca de la rotación.

  • Sin health checks el LB sigue mandando tráfico a un nodo muerto: no logra conectar y le devuelve 502 al cliente
  • Con health checks: detecta, saca, y cuando vuelve lo reincorpora
  • Trade-off: chequear seguido detecta antes, pero carga más
Disponibilidad · últimos 40 s
Estado

¿Dónde vive la sesión?

🔑 = sesiones guardadas · clic en un servidor para tirarlo

Cada servidor guarda la sesión en su memoria. El LB reparte por turnos…

  • …y el siguiente request cae en otro servidor que no conoce al usuario → 401
  • El usuario "se desloguea" a cada rato

Sticky sessions: el LB recuerda a qué servidor fue cada cliente (cookie) y lo manda siempre ahí.

  • ✅ Funciona sin cambiar la app
  • ⚠️ Si ese servidor cae, se pierden sus sesiones (probá tirarlo)
  • ⚠️ Carga despareja y escalar es más difícil

Servidores stateless: la sesión vive en un store compartido (Redis, DB, o un JWT en el cliente).

  • ✅ Cualquier servidor atiende a cualquier usuario
  • ✅ Tirar un servidor no desloguea a nadie
  • ⚠️ Redis pasa a ser una dependencia crítica (se replica)
Tipos de load balancer

Capa 4 vs. Capa 7

L4 transporte · TCP/UDP

lo que ve el LB
src 203.0.113.7:51522 → dst 10.0.0.5:443
GET /api/orders HTTP/1.1 Cookie: session=… 🔒
  • Decide solo por IP y puerto: no abre el contenido
  • Muy rápido y barato, millones de conexiones
  • Sirve para cualquier protocolo (DB, MQTT, gRPC…)
  • No puede rutear por URL ni cookies
Ej.: AWS NLB · HAProxy (mode tcp) · LVS · Maglev

L7 aplicación · HTTP

lo que ve el LB (termina TLS)
GET /api/orders HTTP/1.1
Host: shop.com
Cookie: session=abc123
/api/* → pool API
/static/* → pool estáticos / CDN
Host: admin.* → pool admin
  • Rutea por path, headers, cookies
  • Termina TLS, reintenta, comprime, aplica rate limiting
  • Más CPU por request
Ej.: AWS ALB · Nginx · Envoy · Traefik · HAProxy (mode http)
¿Y si se cae el LB?

El load balancer como nuevo SPOF

  • Dos LBs comparten una IP virtual (VIP)
  • Se mandan heartbeats; si el pasivo deja de recibirlos, toma la VIP (VRRP / keepalived)
  • En la nube: el LB gestionado (AWS ELB, GCP LB) ya es redundante por diseño
  • A escala global: DNS con varias IPs / anycast
Disponibilidad · últimos 40 s
Problema 4, resuelto

Rolling deploy: cero downtime

  1. Sacar un servidor de la rotación (drain)
  2. Esperar que termine sus requests en curso
  3. Desplegar la versión nueva
  4. Esperar health check OK → vuelve a rotación
  5. Repetir con el siguiente

Variantes: blue/green (dos entornos completos y se cambia el LB) y canary (un % del tráfico primero).

Disponibilidad · últimos 40 s
⏸ Un parate

¿Resolvimos todo? A veces, movimos el problema

Escalamos la app… pero todos los servidores le pegan a la misma base de datos. El cuello de botella se mudó.

Todas las consultas van a una DB: 4 conexiones × ~0,5 s ≈ 8 req/s. Subí el tráfico y agregá servidores: la cola se forma en la DB.

Un caché (Redis) responde el 80% de las lecturas en ~60 ms; solo los misses llegan a la DB. ⚠️ Invalidación y datos viejos.

Tráfico entrante
4 req/s
Tu turno

Playground

👆 clic en un servidor para tirarlo / levantarlo
Tráfico
6 req/s
Servidores
Algoritmo
Resumen

Lo que nos llevamos

🖥️

Un solo nodo

SPOF, tope de capacidad, techo vertical y downtime en cada deploy.

↔️

Escalar horizontal

N servidores idénticos detrás de un load balancer. El cliente solo ve el LB.

🎯

Algoritmos

RR simple · Least Conn cargas variables · Hash afinidad · Weighted nodos distintos.

♥

Health checks

El LB detecta nodos rotos, los saca de rotación y los reincorpora.

🔑

Stateless

La sesión en un store compartido, no en la memoria del servidor.

🧭

L4 vs L7

IP/puerto, rápido y genérico, vs. HTTP: routing por path, headers y cookies.

🛡️

Redundar el LB

Activo-pasivo con VIP y heartbeats, o un LB gestionado.

🔁

Rolling deploys

Drenar, actualizar, verificar y seguir: deploys sin cortar el servicio.

🗄️

El cuello se mueve

Escalar la app no escala la DB: un caché adelante la alivia.

Seguir explorando

Llevátela y probala

QR a la presentación publicada
Escaneá para abrirla en el celular
gastonprieto.github.io/
presentacion-load-balancer

🧪 Animate a probar tu propia arquitectura

En el Architecture Playground armás un sistema con las piezas que vimos (clientes, servicios, load balancers, proxies, cachés, colas, brokers) y lo ponés bajo carga.

Arrancá por el preset Load Balancer y probá otro algoritmo, un servidor lento o uno caído.

Abrir el playground →
github.com/gastonprieto/presentacion-load-balancer

🐳 En el repo también hay un laboratorio con Docker para probar lo mismo con servicios de verdad.