Be Software Architecture Wise
Be Wise Apps

Read in English · Lire en français · Auf Deutsch lesen

Sección 9.1 de 9.8 · Intermedio · 28 min de lectura

Estrategias de escalado: vertical, horizontal y el cubo de escalado

Videolección

5 min · narrado en inglés, con subtítulos
Solo escuchar (audio)
Leer la transcripción

Te damos la bienvenida a la sección nueve punto uno. Cuando más personas usan un sistema, tienes que añadir capacidad. Esta lección trata de las decisiones prácticas: qué recursos añadir, dónde, cuándo y con cuánta automatización.

Piensa en una cafetería con mucho movimiento. Puedes atender a más clientes comprando una máquina de café más grande y más rápida. Eso es escalar verticalmente. O puedes abrir más mostradores con más personal. Eso es escalar horizontalmente. Solo funciona si cualquier mostrador puede atender a cualquier cliente. También puedes dividir el trabajo: un mostrador para las bebidas y otro para la comida, o una cafetería por barrio. Y como el personal adicional tarda en llegar, un gerente sensato lo programa antes de la hora punta del almuerzo.

El escalado vertical, o scaling up, da más potencia a una sola máquina, y no cambia nada en el software. Pero existe una máquina más grande que todas, los precios suben de forma pronunciada cerca de lo más alto, y una máquina es un dominio de fallo. El escalado horizontal, o scaling out, ejecuta más instancias detrás de un balanceador de carga. Con ocho instancias, perder una cuesta un octavo de la capacidad, no toda. El precio es que cualquier instancia debe gestionar cualquier solicitud, y el trabajo debe ser divisible. Sobre todo, escala el cuello de botella actual. Duplicar la capa web cuando la base de datos está ocupada al noventa y cinco por ciento no aporta nada más que una factura mayor.

Martin Abbott y Michael Fisher describen tres formas distintas de escalar horizontalmente con el cubo de escalado. Su esquina es una única instancia que lo hace todo. El eje X es la clonación: copias idénticas detrás de un balanceador de carga, cada una haciendo todo el trabajo. Es el eje más barato, pero todos los clones siguen hablando con los mismos datos. El eje Y divide por función: servicios separados para las cartas, los pedidos, el despacho y los pagos, cada uno de los cuales escala y falla por separado. Este es el eje que hay detrás de los microservicios. El eje Z divide por datos: el mismo código, pero cada copia se encarga de una parte de los clientes, como una célula o un shard por ciudad. Un defecto en una célula afecta solo a sus clientes. Los sistemas reales combinan los tres. Usa el eje más barato que resuelva el problema real.

La clonación solo funciona si cualquier clon puede atender cualquier solicitud, así que el servicio debe ser sin estado. El estado se esconde en las sesiones guardadas en memoria, los archivos en el disco local, las tareas programadas, los bloqueos dentro del proceso y los contadores. Cada uno pasa a un almacén compartido. La ausencia de estado es barata de incorporar en el diseño y cara de añadir después.

El autoescalado cierra un bucle de retroalimentación. Los usuarios envían tráfico a las instancias a través de un balanceador de carga. Las instancias informan de su carga a un almacén de métricas, con un retardo. El autoescalador compara la métrica con su objetivo y elige un número de instancias. Después, el orquestador o la API de la nube inicia o detiene instancias. Y cada instancia nueva debe arrancar, calentarse y superar su comprobación de preparación antes de recibir tráfico. Desde una subida de la carga hasta disponer de capacidad útil suelen pasar de dos a diez minutos. Así que el escalado reactivo gestiona los cambios graduales. Los saltos repentinos necesitan margen de capacidad, colas o capacidad programada de antemano.

¿Qué señal debería impulsar el bucle? Una API de pedidos espera sobre todo a la base de datos y a otros servicios, así que su CPU puede estar al veinticinco por ciento mientras todos los hilos están ocupados. Una señal mejor está ligada al recurso que se agota: las solicitudes en curso por instancia o, en los trabajadores de colas, el trabajo acumulado por trabajador. La latencia sube por razones que más instancias no pueden arreglar, como una base de datos lenta. Úsala para las alertas, no para escalar. Y para evitar el flapping, escala hacia fuera deprisa y hacia dentro despacio, con un número mínimo y uno máximo de instancias.

Vamos a dimensionar la API de pedidos de la plataforma de reparto de comida para el pico del almuerzo. El minuto de más actividad llegó a dos mil cincuenta solicitudes por segundo, y se espera un crecimiento del quince por ciento. Así que planifica para dos mil cuatrocientas. Cada instancia tiene treinta y dos hilos de trabajo y tarda ciento veinte milisegundos por solicitud. Según la ley de Little, eso son doscientas ochenta y ocho solicitudes en curso en el pico. Para mantener un treinta por ciento de margen de capacidad, ejecuta cada instancia al setenta por ciento de sus hilos: veintidós punto cuatro solicitudes. Eso exige doce punto nueve, así que trece instancias. Pero el servicio debe sobrevivir a la pérdida de una de sus tres zonas. Con un reparto de cinco, cuatro y cuatro, perder cinco deja ocho instancias con treinta y seis solicitudes cada una, más que sus treinta y dos hilos. Si permites el ochenta y cinco por ciento durante la pérdida de una zona, necesitas once instancias en dos zonas: seis por zona, dieciocho en total. Es sobrevivir a la pérdida de una zona, y no la carga media, lo que fija el tamaño de la flota. Por último, comprueba la capa de debajo. Un máximo de treinta instancias con diez conexiones cada una necesita trescientas, pero la base de datos admite doscientas. Así que el equipo añade primero un pooler de conexiones.

La sección completa trata el escalado de la capa de datos y ofrece una guía de decisión. A continuación, en la sección nueve punto dos, veremos el balanceo de carga y las sesiones. Nos vemos allí.

Lo que necesitas saber

Qué significa escalar en la práctica

La Sección 2.4 definió la escalabilidad como la capacidad de gestionar más carga añadiendo recursos, y mostró con la ley de Amdahl y la ley de escalabilidad universal por qué añadir recursos no siempre añade capacidad. Esta sección trata de las decisiones prácticas: qué recursos añadir, dónde, cuándo y con cuánta automatización. El punto de partida es siempre la misma pregunta: ¿cuál es el parámetro de carga y qué recurso se agota primero? La API de pedidos de una plataforma de reparto de comida a la hora del almuerzo está limitada por las solicitudes por segundo; su servicio de despacho, por el número de repartidores activos cuyas posiciones hay que emparejar cada pocos segundos; su base de datos, por las transacciones de escritura por segundo y por las conexiones. Cada uno necesita una respuesta distinta.

Escalar un sistema significa en realidad escalar su cuello de botella actual y después encontrar el siguiente. Duplicar la capa web de un sistema cuya base de datos está ocupada al 95% no aporta nada salvo una factura mayor y una base de datos a la que ahora se presiona más. Por eso toda decisión de escalado empieza por la medición: la utilización y la saturación de cada recurso (el método USE de Brendan Gregg, que se trata en Observabilidad) y una prueba de carga que encuentre dónde empieza a subir la latencia (Planificación de capacidad y pruebas de rendimiento).

Escalado vertical: una máquina más grande

El escalado vertical (scaling up) consiste en dar a la máquina existente más núcleos de CPU, más memoria, discos más rápidos o una red más rápida. Su gran virtud es que no cambia nada en el software. Una única base de datos relacional en un servidor más grande conserva sus transacciones, sus joins y su consistencia sencilla; una aplicación conserva sus cachés en memoria y sus archivos locales. En la nube, redimensionar es una llamada a una API seguida de un reinicio. Las mayores máquinas virtuales en la nube ofrecen hoy cientos de CPU virtuales y terabytes de memoria, mucho más de lo que la mayoría de los sistemas de negocio necesitarán jamás, y algunos sitios web con muchísimo tráfico han explicado públicamente que atienden un tráfico enorme con un pequeño número de potentes servidores de bases de datos.

Los límites, sin embargo, son reales. Existe una máquina más grande que todas, y el precio por unidad de capacidad sube de forma pronunciada cerca de lo más alto de la gama. Una máquina es un dominio de fallo: cuando falla, o se reinicia para redimensionarla o aplicarle parches, todo lo que hay en ella se detiene, así que escalar verticalmente por sí solo no hace nada por la disponibilidad. A menudo el software no puede aprovechar del todo una máquina más grande: un proceso de un solo hilo, un bloqueo global o una fila caliente de la base de datos limitan la aceleración exactamente como predice la ley de Amdahl. Y la capacidad llega en escalones grandes y lentos, así que pagas el pico a todas horas. Aun así, el escalado vertical es el primer paso correcto con mucha más frecuencia de lo que sugiere la moda, sobre todo en las bases de datos, donde escalar horizontalmente es caro en esfuerzo de diseño.

Escalado horizontal: más máquinas

El escalado horizontal (scaling out) consiste en ejecutar más instancias y repartir el trabajo entre ellas, normalmente con un balanceador de carga delante. Escala mucho más lejos, en escalones pequeños y baratos, y la misma redundancia que añade capacidad también tolera los fallos: con ocho instancias, perder una cuesta un octavo de la capacidad y no toda. Las plataformas en la nube convierten añadir una instancia en cuestión de segundos o minutos, y esa es la base del autoescalado.

El precio es que el software debe estar diseñado para ello. Cualquier instancia debe poder gestionar cualquier solicitud, lo que significa que las instancias no deben guardar nada de lo que dependa una solicitud posterior (la siguiente sección). El trabajo debe ser divisible: una tarea que necesita un bloqueo global no se ejecuta más deprisa en diez máquinas. Los datos deben ser accesibles desde todas las instancias, lo que traslada la presión a la capa de datos compartida. Y el sistema pasa a ser distribuido, con los fallos parciales, los timeouts y los reintentos que describe la Sección 6.1. La mayoría de los sistemas reales combinan ambos: las capas de aplicación sin estado escalan horizontalmente, mientras que las bases de datos escalan verticalmente mientras pueden hacerlo de forma razonable y después escalan horizontalmente con réplicas y particiones.

Escalado vertical y horizontal comparados

InquietudVertical (scale up)Horizontal (scale out)
Cambios en el softwareNingunoLas instancias deben ser sin estado y el trabajo, divisible
Límite superiorLa mayor máquina disponibleNormalmente, la capa de datos compartida o los costos de coordinación
Curva de costosSube de forma pronunciada en los tamaños más grandesAproximadamente lineal, en escalones pequeños
Fallo (failure)Un solo dominio de fallo; redimensionar exige un reinicioPerder una instancia supone perder una fracción de la capacidad
Velocidad del cambioMinutos más un reinicioDe segundos a minutos; puede ser automático
Consistencia y transaccionesSin cambios y sencillasSin cambios en las capas sin estado; difícil para los datos
Uso típicoBases de datos, software heredado y con estado, primera respuesta al crecimientoCapas web y de API, trabajadores, todo lo que no tenga estado

El cubo de escalado: tres formas distintas de escalar horizontalmente

Martin Abbott y Michael Fisher, que dirigieron la tecnología en eBay y PayPal, describen el escalado horizontal con el cubo de escalado en The Art of Scalability (primera edición de 2009, segunda edición de 2015). Separa tres formas independientes de dividir un sistema, cada una dibujada como un eje de un cubo cuyo origen es una única instancia que lo hace todo.

El eje X es la duplicación horizontal, o clonación: ejecutar N copias idénticas de toda la aplicación o servicio y repartir las solicitudes entre ellas. Es el eje más sencillo y barato, y funciona bien para la gestión de solicitudes sin estado y para el tráfico de lectura. No hace nada por el tamaño de los datos ni por el volumen de escritura, porque todos los clones siguen hablando con los mismos datos, y tampoco hace nada por el tamaño del código base ni por el número de equipos que trabajan en él.

El eje Y divide el sistema por función: servicios distintos para las cartas, los pedidos, el despacho y los pagos, cada uno de los cuales ejecuta solo el código y los datos de su capacidad. Así, cada servicio puede escalar, fallar y desplegarse por separado, y cada uno es más pequeño y más fácil de entender. Este es el eje que hay detrás de los microservicios (Sección 6.3), con todos sus costos. El eje Z divide por datos: todas las copias ejecutan el mismo código, pero cada una es responsable solo de una parte de los clientes, inquilinos (tenants), regiones o registros, elegida mediante una clave como la ciudad o el identificador del cliente. El sharding de bases de datos es el eje Z aplicado al almacenamiento (Sección 7.4); una arquitectura basada en células lo aplica a pilas completas, de modo que un defecto o un despliegue erróneo en una célula afecta solo a los clientes de esa célula.

El cubo de escalado

El cubo de escalado: el eje X clona instancias idénticas, el eje Y divide el sistema por función y el eje Z lo divide por datos para que cada copia atienda una parteorigen: una solainstancia de la applos tres ejes combinadosX: clonarY: dividirfuncionesZ: dividirdatosEje X: clonarN copias idénticas tras unbalanceador, cada una hace todoel trabajo: 14 API de pedidosEje Y: dividir por funciónServicios separados porcapacidad: cartas, pedidos,despacho, pagosEje Z: dividir por datosMismo código; cada copiatiene parte de clientes o datos:una célula o shard por ciudad
El cubo de escalado de Abbott y Fisher: cada eje es una forma distinta de dividir un sistema, y los sistemas reales suelen combinar los tres.

Los tres ejes en la plataforma de reparto de comida

EjeQué se divideEjemploResuelveCostos y límites
X: clonarLas solicitudes, entre copias idénticas14 instancias de la API de pedidos detrás de un balanceador de cargaRendimiento de solicitudes, fallos de instanciasExige ausencia de estado; todos los clones comparten una base de datos
Y: funciónCódigo y datos, por capacidadServicios separados de carta, pedidos, despacho y pagosNecesidades distintas de escalado y de fallo; tamaño de los equiposLlamadas de red, datos distribuidos, sobrecarga operativa
Z: datosClientes o registros, por claveUna célula por grupo de ciudades, o pedidos fragmentados (sharding) por ciudadTamaño de los datos, volumen de escritura, radio de impactoCapa de enrutamiento, consultas entre partes, reequilibrado, claves calientes

Los ejes se combinan. El servicio de pedidos de la plataforma (una división en el eje Y) se ejecuta como muchos clones (X) en cada célula regional (Z). El consejo práctico de Abbott y Fisher es usar el eje más barato que resuelva el problema real: X para el volumen de solicitudes, Y cuando partes del sistema tienen necesidades de escalado, de fallo o de equipo realmente distintas, y Z cuando un conjunto de datos o un radio de impacto se ha vuelto demasiado grande. Elegir Z porque suena impresionante trae el enrutamiento, el reequilibrado y las consultas entre particiones mucho antes de que hagan falta.

La ausencia de estado: la condición previa para clonar

El escalado en el eje X solo funciona si cualquier clon puede atender cualquier solicitud. Un servicio sin estado no guarda nada entre solicitudes en su propia memoria ni en su propio disco; todo lo que necesita una solicitud posterior vive en un almacén compartido o viaja con la solicitud. El estado que suele esconderse en una instancia es fácil de enumerar en cuanto lo buscas: sesiones de usuario guardadas en memoria, archivos subidos escritos en el disco local, cachés en memoria que se tratan como fuente de verdad, tareas programadas que deben ejecutarse exactamente en una máquina, bloqueos dentro del proceso que protegen un recurso compartido y contadores para los límites de tasa.

Cada uno tiene un remedio estándar: las sesiones pasan a un almacén compartido o a un token firmado (Sección 9.2); los archivos pasan al almacenamiento de objetos; las cachés se convierten en una optimización de rendimiento que puede perderse en cualquier momento, o pasan a una caché compartida (Sección 7.6); las tareas programadas usan un planificador con elección de líder o una cola; los bloqueos pasan a la base de datos o a un servicio de coordinación; los contadores de límite de tasa pasan a un almacén compartido. La regla de la aplicación de doce factores según la cual los procesos son sin estado y no comparten nada (Sección 10.3) es esta misma idea. La ausencia de estado es barata de incorporar en el diseño y cara de añadir después, porque el estado se filtra en el código en muchos sitios pequeños.

Autoescalado: reactivo, programado y predictivo

El autoescalado cierra un bucle de retroalimentación: mide la carga, la compara con un objetivo y cambia el número de instancias. El autoescalado reactivo lo hace de forma continua a partir de una métrica. El seguimiento de objetivo (target tracking) es la forma habitual: mantener la CPU media al 60%, o las solicitudes en curso por instancia en 30. El Horizontal Pod Autoscaler de Kubernetes calcula desired = ceil(current replicas × current metric ÷ target): seis pods con una media de 45 solicitudes en curso frente a un objetivo de 30 pasan a ser ceil(6 × 45 ÷ 30) = 9. De forma predeterminada, vuelve a evaluar cada 15 segundos e ignora las desviaciones de hasta el 10% respecto al objetivo.

El autoescalado programado cambia la capacidad en momentos conocidos: subir el mínimo a 18 instancias a las 11:15 todos los días laborables antes del pico del almuerzo y bajarlo a las 14:00. Es rudimentario y muy eficaz para patrones predecibles, y es la única manera de tener la capacidad lista antes de un cambio brusco, como una puesta a la venta de entradas a las 10:00 en punto. El autoescalado predictivo, que ofrecen los principales proveedores de nube, prevé la carga a partir del historial (ciclos diarios y semanales) y escala por delante de ella, con el escalado reactivo como red de seguridad para las sorpresas.

La propiedad crucial de cualquier esquema reactivo es el retraso. Las métricas se recogen y se promedian durante decenas de segundos; el autoescalador actúa en su siguiente evaluación; una máquina virtual nueva tarda un minuto o más en arrancar, hay que descargar una imagen de contenedor, y un servicio JVM o Python debe iniciarse, calentar sus cachés y sus pools de conexiones y superar su comprobación de preparación antes de que el balanceador de carga le envíe tráfico. En un clúster, puede que primero haya que añadir los propios nodos mediante un segundo autoescalador, más lento. Desde el momento en que sube la carga hasta que la nueva capacidad es útil suelen pasar de dos a diez minutos. Por tanto, el autoescalado reactivo gestiona los cambios graduales; los cambios repentinos deben absorberse con margen de capacidad (headroom), con colas (Sección 9.3) o con capacidad programada de antemano.

El bucle de retroalimentación del autoescalado y de dónde viene el retraso

Bucle de autoescalado: las instancias informan de la carga, el autoescalador la compara con un objetivo, el orquestador inicia instancias y las nuevas solo se unen al balanceador cuando están listasPúblicoBalanceadorInstanciassirven tráficoMétricasalmacénAutoscalerOrquestadoro API de nube1 informa cargatras un retardo4 inicia, calientay está lista2 compara métrica conobjetivo, fija n.º réplicas3 inicia o parainstanciasDe subida de carga a capacidad útil: a menudo 2 a 10 min
Cada paso del bucle añade retraso, así que el autoescalado reactivo sigue a la carga con minutos de retraso; el margen de capacidad y la capacidad programada cubren el hueco.

Qué métrica usar para escalar, y por qué la CPU suele ser un error

La utilización de la CPU es la señal predeterminada en la mayoría de las plataformas porque siempre está disponible. Solo es la señal correcta cuando el servicio está limitado por la CPU: redimensionado de imágenes, cifrado, serialización intensiva. La mayoría de los servicios de negocio no lo están. Una API de pedidos pasa la mayor parte de cada solicitud esperando a la base de datos, al proveedor de pagos y a otros servicios; su CPU puede estar al 25% mientras todos los hilos de trabajo están ocupados esperando y las solicitudes nuevas se encolan. Escalar según la CPU no hace entonces nada hasta que es demasiado tarde. También ocurre el fallo contrario: una tormenta de recolección de basura o el calentamiento del JIT elevan la CPU sin que aumente la carga, y el autoescalador añade instancias que no hacen falta.

Una señal mejor es la que está directamente ligada al recurso que se agota. En los servicios de solicitud-respuesta, suele ser la concurrencia: el número de solicitudes en curso por instancia, comparado con el número de hilos de trabajo o de conexiones de que dispone. La ley de Little hace exacta la relación (en curso = tasa de llegada × tiempo de respuesta), y reacciona tanto a más tráfico como a respuestas más lentas. En los trabajadores de colas, es el trabajo acumulado por trabajador o la antigüedad del mensaje más antiguo, que expresa cuánto tardará la cola en vaciarse. La latencia es tentadora, pero peligrosa como señal de escalado: sube por razones que más instancias no pueden arreglar, como una base de datos lenta, y escalar horizontalmente un servicio cuya dependencia está en apuros envía a esa dependencia todavía más carga. Usa la latencia para las alertas y escala según una medida del trabajo.

Señales de escalado y cuándo engañan

SeñalAdecuada paraEngaña cuando
Utilización de la CPUTrabajo limitado por la CPU: procesamiento de imágenes, cifrado, serialización intensivaEl trabajo espera a la E/S, a hilos o a conexiones; la recolección de basura o el calentamiento inflan la CPU
Solicitudes por segundo por instanciaServicios cuyas solicitudes cuestan más o menos lo mismoCambia la mezcla de solicitudes, por ejemplo más búsquedas y menos lecturas de la carta
Solicitudes en curso por instanciaLa mayoría de los servicios de solicitud-respuesta; se corresponde con los límites de hilos y de conexionesUna dependencia aguas abajo se ralentiza; escalar horizontalmente le añade entonces carga, así que limita el máximo
Trabajo acumulado en cola por trabajador, o antigüedad del mensaje más antiguoTrabajadores asíncronosLos trabajadores están limitados por un límite de tasa aguas abajo y no por su número
LatenciaAlertas y SLO (Sección 9.5)La causa es una dependencia compartida que más instancias no pueden arreglar
Métrica de negocio (pedidos por minuto, sesiones activas)Carga con una relación estable y medida con la capacidadLa relación se desvía a medida que cambia el producto; recalibra con pruebas de carga

Cooldowns, estabilización y flapping

Un autoescalador que reacciona a cada vaivén oscila (flapping): añade instancias cuando llega una ráfaga, las quita un minuto después cuando la ráfaga pasa y luego las vuelve a añadir, pagando cada vez el costo de arranque y alterando las cachés y los pools de conexiones. También puede perseguir su propia cola: las instancias nuevas están frías y son lentas, así que la latencia o la CPU por solicitud suben brevemente después de escalar horizontalmente, lo que parece más carga.

Las defensas habituales son asimétricas. Escala hacia fuera deprisa, porque la falta de capacidad perjudica a los usuarios; escala hacia dentro despacio, porque el exceso de capacidad solo cuesta dinero. Un cooldown o ventana de estabilización hace que el autoescalador espere después de una acción, o que tome la recomendación más alta de una ventana reciente antes de reducir: el autoescalador de Kubernetes usa de forma predeterminada una ventana de cinco minutos (300 segundos) para reducir y ninguna para ampliar. Una banda de tolerancia ignora las desviaciones pequeñas. Los recuentos mínimo y máximo de instancias acotan el bucle: el mínimo mantiene capacidad suficiente para sobrevivir a la pérdida de una zona y a los primeros minutos de una avalancha, y el máximo protege la base de datos, los servicios aguas abajo y el presupuesto frente a un autoescalador que confunde un problema con carga. La reducción también debe ser elegante: una instancia elegida para retirarse debe dejar de recibir solicitudes nuevas y terminar las que tiene en curso (drenaje de conexiones, Sección 9.2).

Ejemplo práctico

Ejemplo práctico: dimensionar la API de pedidos para el pico del almuerzo

La API de pedidos de la plataforma de reparto de comida debe soportar el almuerzo de los días laborables. En las últimas ocho semanas, el minuto de más actividad llegó a 2050 solicitudes por segundo (rps) hacia las 12:30; marketing espera un crecimiento del 15% este trimestre. Una prueba de carga de una instancia (4 vCPU, 32 hilos de trabajo) encontró un tiempo de respuesta medio de 120 ms y una latencia que empezaba a subir hacia los 260 rps. El servicio se ejecuta en tres zonas de disponibilidad y debe sobrevivir a la pérdida de una zona en el pico.

Pico previsto λ2050 × 1.15 ≈ 2360, redondeado al alza a 2400 rps
Ley de Little para la flotaL = λ × W = 2400 × 0.12 s = 288 solicitudes en curso en el pico
Capacidad de una instancia32 hilos ÷ 0.12 s ≈ 267 rps, lo que concuerda con el codo que encontró la prueba de carga (unos 260 rps)
Utilización objetivo70% de 32 hilos = 22.4 solicitudes en curso por instancia (unos 187 rps), lo que deja un 30% de margen de capacidad para las ráfagas
Instancias para el pico288 ÷ 22.4 = 12.9, así que 13 instancias (2400 ÷ 13 ≈ 185 rps cada una)
Sobrevivir a la pérdida de una zona13 repartidas 5/4/4: perder 5 deja 8 instancias con 288 ÷ 8 = 36 en curso cada una, más que los 32 hilos: sobrecarga. Permitir hasta el 85% (27.2 en curso) durante la pérdida de una zona exige 288 ÷ 27.2 = 10.6, así que 11 en dos zonas: 6 por zona, 18 en total
Rampa y retraso del escalado horizontalLa demanda sube de 600 rps a las 11:30 a 2400 a las 12:15: 40 rps por minuto. El escalado horizontal reactivo tarda unos 5 minutos (1 minuto en detectar, 4 en arrancar y calentarse), durante los cuales la demanda sube 200 rps; la capacidad sobrante al 70% es de unos 80 rps por instancia, así que el escalado reactivo por sí solo podría seguir el ritmo de esta rampa gradual; lo que no puede dar es protección frente a la pérdida de una zona
DecisiónEl escalado programado sube el mínimo a 18 (6 por zona) a las 11:15 y lo baja a 6 (2 por zona) a las 14:00; el escalado reactivo según las solicitudes en curso por instancia (objetivo 22) se mantiene como red de seguridad, con un máximo de 30
Comprobación de la base de datos18 instancias × un pool de 10 conexiones = 180; con el máximo de 30 instancias, 300. La base de datos admite 200, así que el equipo añade un pooler de conexiones antes de subir el máximo

La ley de Little convierte una tasa prevista y un tiempo de respuesta medido en una cifra de concurrencia que se puede comparar directamente con los límites de hilos y de conexiones.

Es el requisito de pérdida de zona, y no la carga media, el que fija el tamaño de la flota en el pico: 18 instancias en lugar de 13. El autoescalado reactivo no puede dar esta protección porque sustituir una zona perdida lleva minutos.

Escalar horizontalmente la capa de aplicación multiplicó la carga sobre las conexiones de la base de datos compartida, que es el siguiente cuello de botella. Todo ejercicio de dimensionamiento debería terminar comprobando la capa de debajo.

Escalar la capa de datos

Las capas de aplicación sin estado son fáciles de escalar; para eso se hacen sin estado. La presión recae entonces sobre las bases de datos, las cachés y los intermediarios que comparten, y por eso la capa de datos suele ser el verdadero límite de un sistema. Las herramientas se trataron en el Capítulo 7 y aquí se resumen en el orden en que los profesionales suelen recurrir a ellas. Primero, reduce el trabajo: índices, mejores consultas, menos idas y vueltas por solicitud. Segundo, absorbe las lecturas con la caché (Sección 7.6) y las réplicas de lectura, aceptando el retardo de replicación cuando el caso de uso lo permita. Tercero, protege las conexiones: cada instancia de la aplicación tiene un pool, así que el total crece con la flota, y un pooler de conexiones como PgBouncer permite que miles de conexiones de clientes compartan unos pocos cientos de conexiones a la base de datos (el max_connections predeterminado de PostgreSQL es de solo 100). Cuarto, escala verticalmente la base de datos principal, lo que a menudo es más barato que cualquier rediseño. Solo entonces particiona las escrituras en el eje Z según una clave como la ciudad o el cliente (Sección 7.4), o pasa a una base de datos SQL distribuida.

Hay dos advertencias. Primera, autoescalar la capa de aplicación puede perjudicar a la capa de datos: cada instancia nueva abre conexiones y calienta su caché desde la base de datos, así que un gran escalado horizontal puede sobrecargar por sí mismo la base de datos. Segunda, algunos límites son lógicos, no físicos: una sola fila caliente (el recuento de existencias de las últimas 500 entradas de un concierto, o un número de secuencia global) serializa las escrituras por grande que sea el servidor. Esos límites se eliminan con el diseño, por ejemplo dividiendo las existencias en varios contadores o reservando asientos por lotes, no con hardware.

Guía de decisión

Guía de decisión: cómo añadir capacidad a un componente

Recorre las preguntas en orden para el componente que limita hoy el sistema.

  1. ¿Se ha medido el cuello de botella (utilización, saturación y encolamiento de CPU, memoria, conexiones, bloqueos y llamadas aguas abajo) en lugar de suponerlo?
    SíContinúa con el componente que se satura primero.
    NoHaz primero una prueba de carga y mide. Escalar la capa equivocada añade costo y ninguna capacidad, y puede empujar más carga hacia el verdadero cuello de botella.
  2. ¿Se puede abaratar el propio trabajo (un índice, una caché, menos idas y vueltas, una carga útil más pequeña, menos logging)?
    SíHaz eso primero. Reduce el costo a cualquier escala y a menudo elimina por completo la necesidad de escalar.
    NoContinúa: hay que añadir capacidad.
  3. ¿El componente es sin estado, o se puede trasladar su estado (sesiones, archivos, bloqueos en memoria, programaciones) a almacenes compartidos?
    SíEscala horizontalmente en el eje X detrás de un balanceador de carga, con autoescalado según una señal de carga y margen de capacidad suficiente para el retraso del escalado horizontal.
    NoEscala verticalmente por ahora y planifica el trabajo para externalizar el estado; una única instancia con estado es también un punto único de fallo.
  4. ¿Está el límite en la capa de datos: la tasa de escritura, el tamaño de almacenamiento o el número de conexiones de una base de datos?
    SíUsa la caché y las réplicas de lectura para las lecturas, un pooler de conexiones para las conexiones y el particionamiento en el eje Z (shards o células) para las escrituras y el almacenamiento.
    NoContinúa.
  5. ¿Tienen partes del componente necesidades de carga, de escalado o de fallo muy distintas?
    SíConsidera una división en el eje Y para que cada parte escale y falle por separado, aceptando los costos de la distribución descritos en el Capítulo 6.
    NoQuédate con la clonación en el eje X: es el eje más barato de operar.

Comprueba lo que has entendido

La CPU de una API de pedidos está al 30%, pero la latencia p99 se ha triplicado y todos los hilos de trabajo están ocupados. ¿Por qué no ha reaccionado el autoescalador basado en la CPU y qué señal lo habría hecho?

El servicio está esperando a la E/S, así que los hilos se agotan mientras la CPU está inactiva. Las solicitudes en curso por instancia, comparadas con el número de hilos, habrían subido con la carga.

¿Qué eje del cubo de escalado aborda una base de datos que ha superado la capacidad de escritura de un solo servidor?

El eje Z: particionar los datos según una clave para que cada shard o célula gestione una parte de las escrituras.

¿Por qué los autoescaladores suelen escalar hacia fuera deprisa, pero hacia dentro despacio?

La falta de capacidad perjudica a los usuarios de inmediato, mientras que el exceso solo cuesta dinero, y reducir con demasiado entusiasmo provoca flapping cuando vuelve la carga.

¿Por qué el autoescalado reactivo no puede proteger una puesta a la venta de entradas que empieza exactamente a las 10:00?

La demanda se dispara en segundos, pero detectar la subida, arrancar instancias y calentarlas lleva minutos, así que la capacidad debe programarse antes de que empiece la venta.

Siguiente · 9.2Balanceo de cargaBalanceadores de carga de capa 4 y de capa 7, los principales algoritmos, incluido el poder de dos elecciones aleatorias, las comprobaciones de actividad (liveness) y de preparación (readiness), el drenaje de conexiones, los problemas de las sesiones persistentes (sticky sessions), el estado de sesión externalizado y el balanceo de carga global.Continuar →

Fuentes y lecturas recomendadas

¿Tienes una pregunta sobre esta sección? ArchitectBot, el asistente de estudio con IA del curso, responde primero a partir del curso.

Pregunta: “Nuestra API de pedidos se autoescala según la CPU, pero aun así se cae a la hora del almuerzo. ¿Según qué deberíamos escalar y cuántas instancias necesitamos realmente?”