Reducir la factura cloud sin perder rendimiento: mide el p99
ArquitecturaBuenas Prácticas

Reducir la factura cloud sin perder rendimiento: mide el p99

Recortar instancias por la media de CPU dispara el p99. Dimensiona con percentiles y margen: qué recomiendan AWS y Azure y cómo traducirlo a euros y riesgo.

Daniel Jordan
12 min read
Compartir:

Escenario ilustrativo, sin cliente detrás: una API con 12 instancias iguales y una CPU media del 22% en las últimas dos semanas. Alguien de finanzas pregunta por qué pagáis 12 máquinas para usar una quinta parte, y la propuesta sale sola: pasar a 6 y ahorrar la mitad del cómputo. La media quedaría en el 44%, una cifra que en un dashboard parece sana.

Ahora mira la misma flota por franjas de 5 minutos. Dos veces al día hay un pico en el que cada instancia llega al 58% de CPU (percentil 95). Con la mitad de las máquinas, esa misma demanda equivale al 116% de la capacidad disponible. Nadie pasa del 100%: lo que ocurre es que las peticiones esperan en cola, y el p99 deja de parecerse al de ayer.

La media de CPU promedia justo las horas que te importan

Una media de 14 días mezcla noches vacías, fines de semana y los minutos al día en los que tus usuarios están pagando. Una flota al 22% no está ociosa: está esperando a las 12:00. Así queda el escenario con las dos cifras juntas (todo ilustrativo):

                          Hoy (12 inst.)    Propuesta (6 inst.)
CPU media                       22%                 44%
Demanda en el pico (p95, 5 min) 58%                116%
(demanda como % de la capacidad disponible)

La segunda fila mide demanda sobre capacidad: por encima del 100% ya no hay CPU que la sirva y lo que sobra espera en cola.

Para sacar tu propia versión de la tabla en Prometheus, estas dos consultas cubren 14 días (cambia el nombre de la métrica si no usas node_exporter):

# CPU media por instancia, 14 días
avg_over_time((1 - avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])))[14d:5m])

# CPU p95 por instancia, 14 días
quantile_over_time(0.95, (1 - avg by (instance) (rate(node_cpu_seconds_total{mode="idle"}[5m])))[14d:5m])

En CloudWatch hay un obstáculo práctico: GetMetricStatistics devuelve como máximo 1.440 puntos por llamada y un valor de percentil por periodo. Catorce días a 5 minutos son 4.032 puntos (14 × 288 = 4.032; cálculo propio), así que son tres llamadas y el p95 de toda la ventana lo calculas tú sobre esos puntos.

La misma trampa existe en latencia. El libro de SRE de Google parte de un servicio con 100 ms de media a 1.000 peticiones por segundo y señala que el 1% de las peticiones puede tardar 5 segundos con facilidad. Son 10 peticiones por segundo esperando cinco segundos, escondidas dentro de una media que sigue pareciendo buena.

Cerca del 100% de CPU lo que crece es la cola

El modelo más simple de teoría de colas, M/M/1 (un servidor, llegadas de Poisson y servicio exponencial), da un número esperado de peticiones en el sistema de ρ/(1-ρ), con ρ la utilización. A ρ=0,5 vale 1. A ρ=0,99 vale 99. Marc Brooker (AWS) lo usa para explicar por qué la latencia se dispara cerca de la saturación. Es una simplificación gruesa, y de ella sale un uso práctico, que es mío y no de Brooker: el p99 no te dice cuánto ahorras, te dice cuánto margen te queda.

La segunda trampa es el fan-out. En "The Tail at Scale", Dean y Barroso (Google) plantean un servidor que responde en 10 ms con un p99 de un segundo. Si una petición necesita respuesta de 100 servidores así en paralelo, el 63% de las peticiones de usuario tardan más de un segundo (1 − 0,99^100 ≈ 0,63; cálculo propio). El libro de SRE lo resume con otra frase: el p99 de un backend puede convertirse con facilidad en la respuesta mediana de tu frontend. Si tu API llama a otros servicios, el recorte de uno de ellos puede acabar notándose en la mediana de los servicios que dependen de él, y no solo en su cola.

El muestreo de 5 minutos se come los picos de 20 segundos

La monitorización básica de EC2 publica métricas cada 5 minutos; la detallada, cada minuto (documentación de CloudWatch). Un pico de 20 segundos al 100% dentro de una ventana de 5 minutos en la que el resto del tiempo estás al 20% se queda en un 25% de media: (20 × 100 + 280 × 20) / 300 = 25,3% (cálculo propio). El pico existió y el gráfico no lo enseña.

Las herramientas de recomendación tienen la misma ceguera por diseño. Azure Advisor muestrea cada 30 segundos y agrega a 30 minutos. Por eso la CPU sirve para dimensionar y el p99 de latencia de la aplicación sirve para decidir si te has pasado.

Antes de recortar, mira lo que la media no cuenta

La CPU media es una señal entre varias y rara vez la primera. Este es el orden en que miraría el resto.

  • Latencia y errores de la aplicación. El libro de SRE de Google las incluye entre sus cuatro señales de oro, junto con el tráfico y la saturación. Si tras quitar capacidad suben los errores, el recorte ha fallado aunque la CPU parezca sana.
  • Saturación, no solo utilización. El método USE de Brendan Gregg define la saturación como trabajo extra que el recurso no puede atender, a menudo en cola. El libro de SRE añade que muchos sistemas degradan antes de llegar al 100%.
  • Memoria, red y disco. Compute Optimizer analiza los cuatro recursos, pero solo ve la memoria de una instancia EC2 si instalas el agente de CloudWatch.
  • Créditos de CPU en instancias de rendimiento ampliable. Vigila CPUCreditBalance y CPUSurplusCreditBalance. En modo estándar, sin créditos la instancia baja poco a poco a su línea base y no puede superarla hasta acumular más.
  • Estrangulamiento de CPU en contenedores. Kubernetes aplica los límites de CPU estrangulando el contenedor. La métrica container_cpu_cfs_throttled_periods_total de cAdvisor cuenta los periodos estrangulados.
  • Arranque del autoescalado. AWS recomienda configurar un calentamiento por defecto de las instancias y, si dudas, empezar por 300 segundos. Una instancia nueva no sirve al instante, y con menos margen esa espera se nota más.

Yo empezaría por la latencia en percentiles altos y por la saturación, y solo después por la CPU media. Antes de quitar una instancia, comprobaría si la flota aguanta el pico con una menos, porque un fallo en ese momento se come el margen que acabas de ahorrar. Y no bajaría otro escalón sin tener a la vista créditos, memoria y estrangulamiento.

Tres patrones para recortar sin romper el p99

Patrón 1: dimensionar por percentil de pico con margen explícito

Elige el tamaño para que el percentil alto de CPU, con el tamaño nuevo, quede por debajo de un techo que decides tú. Los dos grandes proveedores no coinciden en el techo:

  • AWS Compute Optimizer usa por defecto el percentil 99,5 y un margen del 20%, es decir, CPU por debajo del 80% el 99,5% del tiempo. Su preset de máximo ahorro (P90, margen 0%) lo describe la propia AWS como posible causa de mayor latencia o mayor riesgo de degradación.
  • Azure Advisor pide, para cargas de cara al usuario, un p95 de CPU del 40% o menos en el nuevo tamaño. Para cargas que no son de cara al usuario, el 80%.

Dos documentaciones oficiales, dos techos. No voy a elegir uno por ti en silencio: el 80% es razonable para un proceso batch nocturno y arriesgado para un checkout. Un punto de partida prudente para lo que ve el usuario es el 40% de p95 (el techo de Azure Advisor), subiéndolo solo cuando el p99 de latencia lo permita.

Limitación: la CPU es una sola dimensión. El libro de SRE define la saturación como lo "llena" que está tu servicio, poniendo el foco en el recurso más limitado, y ese recurso puede ser memoria, I/O o un pool de conexiones.

Patrón 2: bajar por escalones con un canario de latencia

Quita entre un 10% y un 20% de capacidad cada vez (mi criterio, no una cifra de ninguna fuente), espera a que pase tu peor pico y no pases al siguiente escalón hasta que el p99 de la aplicación se haya mantenido dentro del SLO. Escribe el criterio de vuelta atrás antes de empezar: "si el p99 supera X ms durante Y minutos seguidos, se recupera el tamaño anterior".

El "peor pico" no siempre cae en una semana normal. Compute Optimizer ofrece ventanas de 14 días (por defecto), 32 y 93, y señala que la de 32 días captura los patrones mensuales. Si tu carga tiene cierre de mes, una prueba de una semana no lo ve.

Limitación: tarda semanas. Es el precio de no despertar a nadie a las 3AM.

Patrón 3: usar la media solo donde la factura la usa

En las instancias burstable, la media de CPU sí manda, pero sobre la factura y no sobre la latencia. Según la documentación de AWS, una t3.large tiene una línea base del 30% por vCPU. Si la media de 24 horas la supera, pagas el exceso a una tarifa plana por vCPU-hora (0,05 dólares en la tabla del ejemplo, precios de eu-west-1 (Irlanda) para Linux). Frente a una m5.large (0,1070 dólares por hora, contra 0,0912 de la t3.large), el punto de equilibrio está en el 45,8% de CPU media (cálculo propio: 30% + (0,1070 − 0,0912) / (2 × 0,05) = 45,8%). Si un T3 sostiene el 100%, acabas pagando más que con un M5 equivalente: AWS habla de aproximadamente 1,5 veces, y con los precios de lista de Irlanda sale lo mismo, 1,5 (0,0912 + 0,7 × 2 × 0,05 = 0,1612 dólares por hora frente a 0,1070; el 0,7 es el 70% por encima de la línea base y el 2 son las vCPU de la t3.large; cálculo propio).

Cuándo usarlo: media baja y picos cortos. Limitación: una flota que ya vive por encima de su línea base sale más cara que la instancia fija. Los precios de este patrón son de lista de AWS para Europa (Irlanda, eu-west-1), Linux, bajo demanda, en dólares estadounidenses, la moneda en la que AWS los publica, consultados el 25 de octubre de 2026 en la lista de precios publicada por AWS; pueden haber cambiado, y el cociente entre instancias no depende de la moneda.

Un ahorro de unos 4.700 € al año se evapora en un par de horas de pico degradado

Vuelve al escenario. Con un precio ilustrativo de 0,09 € por instancia-hora (cámbialo por el tuyo), quitar 6 instancias son 6 × 730 horas × 0,09 € = 394 € al mes, unos 4.730 € al año. Ahora pon tu cifra de lo que cuesta una hora de servicio degradado en el pico. Con 2.000 € por hora (otro número ilustrativo), el ahorro de un año completo se gasta en poco más de dos horas.

Es la comparación que hace falta antes de recortar: euros ahorrados por mes frente a horas de pico que puedes permitirte degradar al año. Si no sabes la segunda cifra, tampoco sabes si el recorte compensa.

Cómo aplicarlo en una flota que ya está en producción

Es lo primero que conviene revisar en una auditoría de coste: la CPU media, el p95 de CPU y el p99 de latencia en la misma gráfica. Un orden razonable:

  1. Saca la media y el p95 o p99.5 de CPU en una ventana de 14 días y otra de 32. Si difieren mucho, tienes un ciclo mensual.
  2. Mide el p99 de latencia de la aplicación en el balanceador o en la propia aplicación, no en la infraestructura. Define el SLO antes de tocar nada.
  3. Calcula el tamaño nuevo con un techo de p95 explícito (Patrón 1) y compara el resultado con lo que recomienda Compute Optimizer, que avisa de un mayor riesgo de degradación en el preset P90.
  4. Baja un escalón (Patrón 2), observa un pico completo y decide con el p99 delante.
  5. Si el cuello de botella resulta ser memoria, red o disco, el recorte de CPU no era el ahorro que buscabas.

Lo que este enfoque no resuelve

El modelo M/M/1 es una simplificación y los sistemas con contrapresión o clientes que reintentan sin límite se comportan distinto. Los ahorros que muestran las herramientas se calculan sobre precio de lista: Azure avisa de que no tienen en cuenta Reserved Instances ni Savings Plans, y tu factura real puede salir distinta. Los números de esta pieza son un escenario ilustrativo, no una auditoría. Y la CPU es una línea de la factura entre varias: la transferencia entre zonas de disponibilidad sube el coste sin que la CPU se mueva, como contamos en El precio que nadie te dice de seguir las buenas prácticas de AWS al pie de la letra.

Si mañana apagaras un tercio de tu flota, ¿sabrías decir cuál sería tu p99 en el pico del cierre de mes antes de comprobarlo en producción?

Referencias

Lecturas relacionadas

Tags:#finops#rightsizing#latencia-p99#aws#azure