Blog de Amazon Web Services (AWS)

Selección de instancias EC2 basada en precio-rendimiento, no en utilización de CPU

Por José Lorenzo Cuéncar Garza, Arquitecto de Soluciones Senior en AWS y Diego Nieto Marin, Arquitecto de Soluciones Senior Especialista en Cómputo Eficiente en AWS.

Cuando un equipo de plataforma llega con la pregunta «¿el tipo de instancia que usamos sigue siendo la elección correcta?», empieza lo interesante. Es una conversación frecuente en el trabajo de arquitectura de soluciones. El patrón se repite: dashboards de Amazon CloudWatch que muestran la CPU al 65-70%, todo parece «normal», y el tipo de instancia está fijo en las plantillas de Infraestructura como Código desde el día uno, sin ningún registro de por qué se eligió ni qué alternativas se evaluaron. Es una decisión heredada, no una decisión técnica.

Una CPU al 70% no indica si el dinero está bien gastado. Indica que el procesador está ocupado. Dos instancias con CPU al 70% pueden tener costos por transacción radicalmente distintos, especialmente cuando una corre en arquitectura x86 y otra en ARM. Con un catálogo de Amazon Elastic Compute Cloud (Amazon EC2) que suma cientos de tipos de instancia y crece cada año, repartido en dos arquitecturas (x86_64 y ARM64) y tres familias de procesadores (AWS Graviton, Intel y AMD), elegir por familiaridad se convierte en una decisión cara.

El problema con la CPU como señal de sizing

El escenario típico es que un equipo lleva meses o años con el mismo tipo de instancia en producción. Cada vez que alguien sugiere revisarlo, la respuesta es «AWS Compute Optimizer dice que estamos bien». Y técnicamente tienen razón: la utilización está dentro del rango aceptable.

El problema de fondo es que optimizar para utilización no es lo mismo que optimizar para eficiencia de costos. AWS Compute Optimizer puede sugerir candidatas basadas en Graviton a partir de métricas de infraestructura, pero no ejecuta la aplicación: no mide cuántas transacciones por dólar entrega cada familia de instancias con el workload real. Esa validación solo la da un test. Un m7g.xlarge con procesador AWS Graviton puede entregar más trabajo a igual o menor costo, y la única forma de comprobarlo es midiéndolo.

Cuando los equipos ven la diferencia entre optimizar utilización y optimizar costo por unidad de trabajo, algunos perciben llevar la CPU al 80% como una mejora; otros cuestionan si reducir el costo por transacción en un 20-40% cambiando de instancia es demasiado simple. Esa variedad de perspectivas es normal, y precisamente por eso la metodología necesita producir números concretos, no argumentos teóricos.

La métrica correcta para la selección de instancias es precio-rendimiento: costo por unidad de trabajo entregada. La unidad de trabajo depende del contexto: una transacción de punto de venta completada, un video transcodificado, un query ejecutado o una inferencia de modelo completada. La unidad varía; el principio no.

Este post documenta una metodología de tres pasos para la selección de instancias basada en datos, inspirada en el AWS Workshop Optimizing EC2: Hands-On Strategies for Cost-Effective Performance. Al cambiar la métrica de utilización a valor entregado por dólar, la conversación cambia por completo.

Tres pasos para la selección de instancias basada en datos

La metodología tiene una secuencia deliberada. No es una lista de opciones: el orden importa.

Paso 1: Medir el punto de quiebre del workload respetando el SLO

Antes de comparar instancias, es necesario saber cuánto trabajo puede hacer la instancia actual antes de que la experiencia del usuario se degrade. Ese umbral lo define el Objetivo de Nivel de Servicio (SLO, por sus siglas en inglés: Service Level Objective).

Por ejemplo, para transacciones de un sistema de punto de venta, un SLO razonable podría ser P99 (percentil 99) < 200 ms: cada transacción que supere ese umbral significa un cliente esperando. Para una API interna de procesamiento batch, quizá el SLO sea un throughput mínimo de X registros por minuto. El negocio fija ese número; el trabajo de ingeniería es encontrar cuántas unidades de trabajo por hora puede manejar la instancia antes de cruzarlo.

El proceso:

  1. Ejecutar wrk contra la instancia actual en niveles de carga crecientes, aumentando las conexiones concurrentes en cada nivel.
  2. Registrar en cada nivel la latencia P99 que reporta la opción --latency, junto con el throughput (Requests/sec).
  3. Identificar el primer nivel donde el SLO se viola: ese es el punto de quiebre.
  4. Registrar el throughput máximo sostenible: el del último nivel que cumplió el SLO.

Wrk es un generador de carga HTTP escrito en C que prueba endpoints HTTP y HTTPS sin instrumentar la aplicación. Con la opción --latency reporta la distribución de percentiles de latencia, incluido el P99: justo la métrica que el SLO necesita.

#!/bin/bash
# Test de punto de quiebre con wrk: niveles de carga crecientes
# Instalación: git clone https://github.com/wg/wrk && cd wrk && make
set -e
command -v wrk >/dev/null 2>&1 || { echo "wrk no está instalado"; exit 1; }

URL="https://app.example.com/api/transaction"  # Reemplazar con la URL real de la aplicación

for CONEXIONES in 50 100 200 300 400; do
  echo "== Nivel de carga: ${CONEXIONES} conexiones concurrentes =="
  wrk -t 8 -c "${CONEXIONES}" -d 3m --latency --timeout 2s "${URL}"
done
# -t: ajustar al número de vCPUs del generador de carga

La opción --latency imprime la distribución de latencia de cada nivel. La línea 99% es la métrica que se compara contra el SLO; Requests/sec es el throughput del nivel. El punto de quiebre es el primer nivel cuya P99 supera los 200 ms; el throughput máximo sostenible es el del nivel anterior. Para la fórmula, requests_por_hora = Requests/sec × 3,600.

Nota práctica: el generador de carga debe tener puertos efímeros suficientes, y el backlog del servidor debe superar las conexiones concurrentes del test.

Con el throughput máximo y el costo por hora de la instancia se calcula la línea de base de precio-rendimiento:

costo_por_request = precio_hora_instancia / requests_por_hora_en_punto_de_quiebre

Ese número es la línea de base. Toda medición posterior se compara contra él.

Paso 2: Comparar 2 a 3 familias o arquitecturas con el test idéntico

Aquí está el corazón de la metodología: lanzar 2 a 3 instancias de precio comparable, ejecutar exactamente el mismo test de carga en cada una y comparar el costo por request, no el costo por hora.

Las tres candidatas de comparación más comunes:

Instancia Arquitectura Procesador
m5.xlarge x86_64 Intel Xeon Platinum
m5a.xlarge x86_64 AMD EPYC
m7g.xlarge arm64 AWS Graviton3

El escenario modela una flota que hoy corre en m5; el catálogo actual incluye generaciones más recientes (m7i, m7a y las familias con Graviton4 y Graviton5). La metodología aplica igual a cualquier generación.

El proceso: lanzar las tres con la misma Amazon Machine Image (AMI) y configuración, desplegar la misma aplicación y ejecutar el mismo comando de wrk sin modificar nada más. Misma configuración, mismo sistema operativo, misma carga. Al final, se calcula el costo por request para cada una.

#!/bin/bash
# Lanzar instancias de comparación
# Obtener AMI actual: consultar el Parameter Store de AWS Systems Manager para AMI dinámicas
# En lugar de hardcodear IDs de AMI, usar parámetros gestionados por AWS:
# AMI x86_64 más reciente de Amazon Linux 2023: /aws/service/ami-amazon-linux-latest/al2023-ami-*
# Alternativa manual: aws ec2 describe-images --owners amazon --filters 'Name=name,Values=al2023-ami-*' --query 'sort_by(Images, &CreationDate)[-1].[ImageId]' --output text

AMI_X86=$(aws ssm get-parameter --name /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64 --query 'Parameter.Value' --output text)
AMI_ARM=$(aws ssm get-parameter --name /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-arm64 --query 'Parameter.Value' --output text)

# Intel x86
aws ec2 run-instances \
  --image-id $AMI_X86 \
  --instance-type m5.xlarge \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=benchmark,Value=intel-baseline}]'

# AMD x86
aws ec2 run-instances \
  --image-id $AMI_X86 \
  --instance-type m5a.xlarge \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=benchmark,Value=amd-comparison}]'

# Graviton ARM
aws ec2 run-instances \
  --image-id $AMI_ARM \
  --instance-type m7g.xlarge \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=benchmark,Value=graviton-comparison}]'

El parámetro /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64 entrega siempre la AMI más reciente de Amazon Linux 2023 para la arquitectura x86_64, sin mantener IDs de AMI en el código. De manera similar, /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-arm64 retorna la versión arm64. AWS Systems Manager mantiene estos parámetros actualizados cada vez que AWS publica una AMI nueva. Este enfoque elimina la necesidad de actualizar manualmente los IDs de AMI en tus scripts.

Una advertencia práctica: si la aplicación usa dependencias con binarios nativos, conviene validar la compatibilidad con ARM antes del test. La gran mayoría del ecosistema moderno soporta arm64; Porting Advisor for Graviton automatiza la verificación.

Resultados típicos de este tipo de ejercicio:

Instancia Precio-rendimiento
m5.xlarge (Intel) Línea base de precio-rendimiento
m5a.xlarge (AMD) ~13% menor costo por request vs. Intel
m7g.xlarge (Graviton) Hasta 40% mejor precio-rendimiento vs. instancias x86 comparables, según datos publicados por AWS. El resultado exacto depende del workload — mídelo con tu propia carga.

En la práctica, las instancias Graviton combinan mayor throughput con menor precio por hora, lo que produce mejoras significativas en costo por unidad de trabajo para la mayoría de workloads web y de procesamiento general. El resultado exacto varía — por eso la metodología insiste en medir con tu propia carga y tu propio SLO.

Y la P99 se mantiene por debajo del SLO en todo momento.

Nota: los precios On-Demand actualizados por región, necesarios para calcular el precio-rendimiento de cada caso, están en la página de precios de Amazon EC2.

Sin los números, el debate se queda en opiniones sobre qué instancia «debería ser mejor». Con los números, se convierte en una conversación sobre gestión de riesgo de migración, que es manejable y accionable: por ejemplo, con un despliegue canary detrás del balanceador de carga.

El experimento completo (tres instancias, tests de carga, análisis) toma una tarde. En un entorno on-premises, una comparación equivalente requiere semanas de proceso de adquisición. El costo total de lanzar las tres instancias por cuatro horas es de menos de 3 USD, según los precios On-Demand de us-east-1 a la fecha de publicación.

Paso 3: Optimizar el código solo después de elegir el hardware correcto

Este paso se ejecuta después, no antes. El error más común en equipos que intentan reducir costos es invertir semanas optimizando código en la instancia incorrecta y obtener mejoras marginales.

La secuencia importa porque el hardware define el techo de lo que el código puede lograr. En los workloads de procesamiento de medios del workshop, un algoritmo optimizado con instrucciones SIMD (procesamiento de datos en paralelo) obtuvo mejoras de 15x a 20x. La condición: que la instrucción vectorial corresponda a la arquitectura del procesador. En Graviton, eso significa compilar con soporte nativo para sus instrucciones vectoriales:

# Optimización para Graviton (instrucciones NEON/SVE)
# Compilar sobre la instancia Graviton para que 'native' detecte la microarquitectura
gcc -O3 -mcpu=native -o my-processor my-processor.c

Nota: native optimiza para la microarquitectura donde se compila. Para flotas con varias generaciones de Graviton, compilar para la generación más antigua desplegada o usar -march=armv8.2-a, que cubre de Graviton2 a Graviton5

Para muchos workloads (especialmente los basados en lenguajes interpretados o runtimes con JIT, como Java), la ganancia del hardware es suficiente para cumplir el objetivo de negocio sin modificar código. Para workloads de procesamiento de medios, combinar la instancia correcta con optimizaciones SIMD puede generar mejoras de un orden de magnitud mayor. Para queries a bases de datos, en los escenarios de consultas repetitivas del workshop, el caching después del right-sizing multiplicó el throughput efectivo entre 100x y 1,000x. Aplica únicamente cuando el hardware ya no es el cuello de botella. El extremo superior corresponde a consultas que pasan de resolverse en la base de datos a servirse desde memoria; el rango varía según el patrón de acceso.

Patrón de resultados cuando se sigue el orden correcto (impactos observados en los workloads del workshop):

Acción Impacto típico
Solo right-sizing de instancia 20-40% de reducción de costos, sin cambios de código
Right-sizing + vectorización (procesamiento de medios) Mejoras de 15-20x en precio-rendimiento; varían según el algoritmo y el tipo de datos
Right-sizing + caching (queries de base de datos) 100-1,000x en throughput efectivo para consultas repetitivas; varía según el patrón de acceso
Right-sizing + optimización combinada Los efectos se multiplican; el hardware define el techo y el código lo aprovecha

El principio: corregir primero la ineficiencia estructural (hardware inadecuado) y luego invertir en optimizaciones de mayor costo (refactoring de código). Invertir el orden significa optimizar algo que se podría simplemente cambiar.

Cuándo aplicar esta metodología

Esta metodología aplica en dos escenarios que comparten el mismo problema de base.

Los equipos que migran desde on-premises tienden a replicar el hardware que conocen: el equivalente más cercano al servidor físico que tenían. Ese mapeo 1:1 ignora que la nube ofrece arquitecturas sin costo de adquisición y con facturación por hora. El experimento de comparación debería ser el primer paso antes de comprometer Reserved Instances o Savings Plans.

Los equipos que optimizan workloads existentes en AWS acumulan deuda de configuración. Un tipo de instancia fijo en las plantillas de Infraestructura como Código por dos años es deuda técnica silenciosa: Graviton3 no existía cuando muchos equipos eligieron su instancia actual, y hoy el catálogo ya va por Graviton5. La metodología da una respuesta defendible cuando alguien pregunta «¿por qué estamos usando esta instancia?».

Para ambos casos, el prerrequisito más importante no es técnico: es tener el SLO definido antes de correr el primer test. Esa es una conversación con el equipo de producto. Sin ese umbral no se puede determinar el punto de quiebre y toda comparación de instancias se vuelve subjetiva.

Por dónde empezar

Esta semana: Elegir un servicio con carga medible. Definir su SLO principal con el equipo de producto. Ejecutar el test de punto de quiebre sobre la instancia actual. Calcular el costo por unidad de trabajo.

Este mes: Ejecutar el mismo test sobre una instancia equivalente de Graviton. Comparar los números. Si entrega menor costo por unidad de trabajo con el mismo SLO, hay un caso de negocio con datos reales.

Este trimestre: Extender la comparación a los 3 a 4 servicios de mayor gasto en Amazon EC2, priorizando los de mayor costo mensual: ahí el 20-40% de ahorro tiene mayor impacto absoluto en la factura.

Recursos

El AWS Workshop Optimizing EC2: Hands-On Strategies for Cost-Effective Performance incluye el entorno guiado completo: scripts de automatización, notebooks con análisis de resultados, cuatro tipos de workloads con características distintas (CPU-bound, memory-bound, I/O-bound y mixed) y herramientas de comparación automatizadas.

La guía técnica de AWS Graviton documenta flags de compilación y optimizaciones por lenguaje y runtime.

Para consultar precios actualizados de instancias por región, la página de precios de Amazon EC2 permite automatizar la selección del conjunto de instancias candidatas.

Conclusión

La utilización de CPU indica si un servidor está ocupado. El costo por unidad de trabajo indica si el dinero está bien gastado. Son métricas distintas y optimizan cosas distintas.

La respuesta a «¿por qué usamos esta instancia?» no debería ser «porque siempre la hemos usado». Debería ser «porque entrega el menor costo por unidad de trabajo dentro de nuestro SLO, medido con un test reproducible bajo condiciones controladas». Esa respuesta es defendible, repetible y no depende de opiniones.

El experimento cuesta menos de 3 USD y una tarde de trabajo. Lo que vale la pena no es solo el ahorro en la factura: es el cambio en cómo un equipo habla de sus decisiones de infraestructura. Pasar de defender elecciones históricas a cuestionar cualquier configuración que no tenga un test que la respalde. Esa es la transformación: no cambiar una instancia, sino cambiar el hábito de elegir instancias.

 

Autores

 

José Lorenzo Cuéncar Garza es Arquitecto de Soluciones Senior en Amazon Web Services para el Sector Público en México, donde se especializa en Cómputo, Cómputo de Alto Rendimiento (HPC) e Inteligencia Artificial Generativa. Ha colaborado con equipos de investigación y profesionales a cargo de aplicaciones de alta concurrencia y misión crítica, apoyando en la adopción de arquitecturas en la nube. Previamente trabajó en empresas de consultoría, construcción, logística y startups tecnológicas. En su rol actual, impulsa la transformación digital de instituciones educativas, organizaciones sin fines de lucro, y gobiernos federales, estatales y municipales en México.
Diego Nieto Diego Nieto Marin es Arquitecto de Soluciones Senior Especialista en Cómputo Eficiente en Amazon Web Services, con cobertura NOLA ( Norte de Latinoamérica ) y México. Se especializa en Amazon EC2, AWS Graviton, EC2 Spot y Karpenter, ayudando a clientes de diversos sectores e industrias a mejorar su relación precio-rendimiento a través de estrategias de selección de procesadores y arquitecturas multi-arch, el uso estratégico de instancias Spot y aprovisionamiento inteligente con Karpenter. En su rol actual, acompaña a organizaciones en el diseño de arquitecturas de cómputo eficiente que equilibran rendimiento, costo y escalabilidad.