Cerramos el módulo anterior con MercadoFresco preparado: cuenta segura, región elegida, consola dominada y CLI configurada con el perfil mercadofresco-dev. Todo eso era el andamio. Ahora empieza la obra. En esta lección levantamos el primer servidor real de MercadoFresco en la nube con Amazon EC2 (Elastic Compute Cloud), el servicio que proporciona máquinas virtuales bajo demanda y la pieza sobre la que se apoya buena parte de lo que viene después.

EC2 es el servicio con el que casi todo el mundo entra en AWS, y también donde más dinero se tira por descuido. Por eso no vamos a hacer un paseo por la pantalla de creación: vamos a entender qué es realmente una instancia, cómo se elige el tamaño sin adivinar, qué son los créditos de CPU (que serán decisivos para el pico de los viernes), cómo se automatiza el arranque del servidor con user data, y cómo un grupo de Auto Scaling convierte el problema 1 de MercadoFresco —las caídas de los viernes— en un problema resuelto por configuración.

Contenido

  1. Qué es una instancia EC2 y qué problema resuelve
  2. AMI: la plantilla de la que nace la instancia
  3. Familias y tipos de instancia: cómo leer t3.micro
  4. Cómo elegir el tamaño sin adivinar
  5. Instancias burstable y créditos de CPU
  6. Modelos de compra: bajo demanda, spot, reservadas y Savings Plans
  7. Ciclo de vida de una instancia: parar no es terminar
  8. Acceso a la instancia: pares de claves, SSH, Instance Connect y Session Manager
  9. user data: que la instancia se instale sola
  10. Metadatos de instancia e IMDSv2
  11. Crear la instancia por consola, paso a paso
  12. Crear la instancia por CLI con el esquema de etiquetado
  13. Plantillas de lanzamiento y Auto Scaling: la solución al pico de los viernes
  14. Apagar y borrar todo para no gastar

Qué es una instancia EC2 y qué problema resuelve

Una instancia EC2 es una máquina virtual que se ejecuta sobre la infraestructura física de AWS. Tiene CPU, memoria, red, un disco de arranque y un sistema operativo completo: para tu software es indistinguible de un servidor de verdad. Puedes instalar lo que quieras, abrir una sesión SSH, mirar los logs y reiniciarla.

La diferencia con el servidor de la oficina de MercadoFresco no está en lo que la máquina es, sino en cómo se obtiene y se devuelve:

Servidor físico de la oficina Instancia EC2
Tiempo de aprovisionamiento Semanas (compra, envío, instalación) Menos de un minuto
Coste inicial 6.000 € pagados de golpe 0 €
Coste continuo Luz, mantenimiento, recambios Por segundo de uso
Cambiar de tamaño Comprar RAM y abrir la caja Parar, cambiar el tipo, arrancar
Tener 10 iguales Comprar 10 servidores Una llamada a la API
Devolverlo Venderlo de segunda mano terminate-instances

Esa última fila es la clave conceptual de todo el módulo: en AWS, crear y destruir son operaciones simétricas y baratas. Cuando Marta necesite cuatro servidores de tienda el viernes a las 17:00 y uno solo el sábado por la mañana, eso deja de ser una fantasía para convertirse en una línea de configuración.

Nota sobre lo que EC2 no cubre en esta lección. Toda instancia vive dentro de una red virtual (VPC) y está protegida por un grupo de seguridad, que es su cortafuegos. Aquí usaremos la VPC por defecto y crearemos un grupo de seguridad mínimo, sin entrar en detalle: las redes son el módulo 3 completo (03-01 VPC, 03-02 grupos de seguridad y NACL). El disco de la instancia se gestiona con EBS, que es la lección 02-02.

AMI: la plantilla de la que nace la instancia

Una AMI (Amazon Machine Image) es una imagen de disco congelada: sistema operativo, paquetes instalados, configuración y ficheros. Cuando lanzas una instancia, AWS copia la AMI a un volumen nuevo y arranca la máquina desde ahí. La AMI es la plantilla; la instancia es la copia viva.

Hay cuatro orígenes posibles:

Origen Qué es Cuándo usarlo
AMI de AWS Amazon Linux 2023, Ubuntu, Windows Server, Debian… mantenidas y parcheadas Punto de partida habitual
AWS Marketplace Imágenes de terceros (a veces con coste por hora añadido) Software comercial preinstalado
AMI de la comunidad Publicadas por cualquier usuario Con precaución: no auditadas
AMI propia Creada por ti a partir de una instancia configurada Arranques rápidos y reproducibles

Para MercadoFresco usaremos Amazon Linux 2023: es gratuita, está optimizada para EC2, incluye la AWS CLI v2 preinstalada y el agente de Systems Manager ya activo (importante para Session Manager, más abajo).

Un detalle que confunde al principio: el identificador de una AMI es distinto en cada región. La misma Amazon Linux 2023 tiene un ami-0abc… en eu-west-1 y otro diferente en eu-central-1. Por eso nunca se escribe a mano en un script; se consulta. La forma robusta es preguntar al almacén de parámetros público de AWS:

# Obtiene el ID de la AMI más reciente de Amazon Linux 2023 (x86_64) en eu-west-1.
# El parámetro es público: AWS lo actualiza cada vez que publica una imagen nueva.
aws ssm get-parameter \
  --name /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64 \
  --query 'Parameter.Value' \
  --output text \
  --profile mercadofresco-dev \
  --region eu-west-1

Salida (ejemplo; el tuyo será distinto):

ami-0c1bc246476a5572b

Desglose del comando:

  • aws ssm get-parameter: Systems Manager Parameter Store guarda pares clave/valor. AWS publica ahí los IDs de sus AMIs para que no tengas que buscarlos.
  • --query 'Parameter.Value': JMESPath, tal como vimos en 01-05, para quedarnos solo con el valor.
  • --output text: sin comillas, listo para meterlo en una variable de shell.

Guárdalo en una variable, porque lo usaremos varias veces:

AMI_ID=$(aws ssm get-parameter \
  --name /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64 \
  --query 'Parameter.Value' --output text \
  --profile mercadofresco-dev --region eu-west-1)

echo "AMI seleccionada: $AMI_ID"

Familias y tipos de instancia: cómo leer t3.micro

El tipo de instancia define cuánta CPU, memoria, red y disco tiene la máquina. AWS ofrece cientos, pero el nombre es un código legible que se descifra por partes:

t3.micro
│││  └──── tamaño dentro de la familia
││└─────── (opcional) atributos extra: a = AMD, g = Graviton/ARM, d = disco local, n = red mejorada
│└──────── generación (3 = tercera; a mayor número, más moderna y normalmente mejor precio/rendimiento)
└───────── familia: para qué está optimizada

Otro ejemplo: m6g.large = familia m (equilibrada), generación 6, g de Graviton (procesador ARM de AWS), tamaño large.

Las familias que necesitas conocer:

Familia Optimizada para Relación vCPU:RAM aprox. Ejemplo de uso en MercadoFresco
T (t3, t4g) Uso general burstable, barata 1:2 / 1:4 Entorno de desarrollo de Luis, servidor de la tienda con tráfico irregular
M (m6i, m7g) Uso general equilibrado, rendimiento sostenido 1:4 Servidor de la tienda en producción
C (c6i, c7g) Cómputo intensivo (mucha CPU) 1:2 Procesado de imágenes del catálogo, cálculo de rutas de reparto
R (r6i, r7g) Memoria intensiva 1:8 Cachés grandes, informes analíticos de Sara en memoria
G / P GPU (gráficos, aprendizaje automático) Variable No aplica hoy en MercadoFresco
I / D Almacenamiento local rápido Variable Bases de datos con NVMe local

Y los tamaños, que en general duplican recursos y precio en cada escalón:

Tamaño vCPU RAM (familia t3) Precio relativo
nano 2 (burst) 0,5 GiB
micro 2 (burst) 1 GiB
small 2 (burst) 2 GiB
medium 2 (burst) 4 GiB
large 2 8 GiB 16×
xlarge 4 16 GiB 32×
2xlarge 8 32 GiB 64×

Esta linealidad tiene una consecuencia importante y contraintuitiva: dos instancias large cuestan lo mismo que una xlarge, pero dos instancias sobreviven a la caída de una zona de disponibilidad y una xlarge no. Es el primer argumento del diseño multi-AZ que vimos en 01-03, y una de las razones por las que preferiremos escalar horizontalmente (más máquinas) antes que verticalmente (máquinas mayores).

Cómo elegir el tamaño sin adivinar

El método profesional no es intuir, es medir. Para MercadoFresco:

  1. Parte del dato conocido. Una instancia de la tienda soporta 600 pedidos/hora. El pico de los viernes es de 900 pedidos/hora. Con una sola máquina, el 33 % de los pedidos del pico se pierde: eso son las caídas del problema 1.
  2. Empieza pequeño. En la nube, cambiar de tamaño cuesta un reinicio. Empezar grande "por si acaso" cuesta dinero cada hora, para siempre.
  3. Mide con CloudWatch (lección 05-01): CPU, memoria (requiere el agente), red y latencia durante al menos una semana completa, incluyendo un viernes.
  4. Aplica la regla del 40-60 %. Si la CPU media está por debajo del 40 %, sobra máquina. Si supera el 70 % de forma sostenida, falta.
  5. Consulta Compute Optimizer, el servicio gratuito de AWS que analiza tus métricas y recomienda tipo y tamaño.

Para nuestro punto de partida elegimos t3.micro por dos razones: entra en la capa gratuita (750 horas al mes durante 12 meses) y su comportamiento burstable nos permite explicar los créditos de CPU, que son justamente lo que hace fracasar a mucha gente en su primer pico de tráfico.

Instancias burstable y créditos de CPU

Aquí está el concepto que más disgustos da a quien empieza, y el que más importa para el viernes de MercadoFresco.

Las instancias de la familia T no te dan la CPU entera todo el tiempo. Te dan una línea base —un porcentaje del núcleo— y acumulan créditos de CPU cuando consumes menos de esa línea. Cuando necesitas más, gastas créditos para llegar al 100 %. Si se te acaban, la instancia se estrangula (throttling) y baja bruscamente a la línea base, aunque la CPU física esté libre.

Tipo vCPU Línea base por vCPU Créditos ganados/hora Créditos máx. acumulables
t3.nano 2 5 % 6 144
t3.micro 2 10 % 12 288
t3.small 2 20 % 24 576
t3.medium 2 20 % 24 576
t3.large 2 30 % 36 864

Un crédito = un minuto de un vCPU al 100 %.

Hagamos el cálculo real de MercadoFresco. Un t3.micro acumula 12 créditos/hora, con un máximo de 288 (equivalente a 24 horas de acumulación). El pico de los viernes dura de 17:00 a 21:00, cuatro horas, y en ese rato la tienda estaría al 100 % de CPU con sus 2 vCPU:

Consumo durante el pico = 2 vCPU × 100 % × 60 min × 4 h = 480 créditos
Créditos disponibles     = 288 acumulados + (12/h × 4 h) = 336 créditos
Déficit                  = 480 − 336 = 144 créditos

Es decir: el t3.micro aguanta menos de tres horas del pico y luego se estrangula al 10 % de CPU. La tienda no se cae por falta de servidor, se cae por falta de créditos, y en el panel de CloudWatch la CPU aparece plana y baja, lo que despista muchísimo si no conoces el mecanismo.

Hay dos salidas, y conviene entender que solo una es buena:

  • Modo unlimited (activado por defecto en T3): cuando se acaban los créditos, AWS deja que sigas al 100 % y te cobra un extra por cada vCPU-hora sobrante. Evita la caída, pero la factura se dispara si el pico es habitual. Se puede forzar el modo standard para que nunca cobre de más, a cambio de aceptar el estrangulamiento.
  • Escalar horizontalmente: añadir instancias durante el pico. Es la solución correcta, y la veremos al final de esta lección con Auto Scaling.

Consultar el saldo de créditos (métrica CPUCreditBalance, en CloudWatch):

aws cloudwatch get-metric-statistics \
  --namespace AWS/EC2 \
  --metric-name CPUCreditBalance \
  --dimensions Name=InstanceId,Value=i-0123456789abcdef0 \
  --start-time 2026-08-01T00:00:00Z \
  --end-time 2026-08-02T00:00:00Z \
  --period 3600 \
  --statistics Average \
  --profile mercadofresco-dev --region eu-west-1

Regla práctica. Las instancias T son excelentes para cargas irregulares con valles largos (desarrollo, entornos de pruebas, servicios internos). Para una carga sostenida y predecible, una M sale más barata y más previsible que una T en modo unlimited.

Modelos de compra: bajo demanda, spot, reservadas y Savings Plans

La misma instancia puede costar cuatro precios distintos según el compromiso que adquieras. Aquí solo los comparamos para que sepas que existen y cuándo se usa cada uno; el análisis económico completo, con cálculos de amortización, es la lección 11-05.

Modelo Descuento típico Compromiso Puede interrumpirse Uso en MercadoFresco
Bajo demanda 0 % (precio base) Ninguno No Todo lo que hagamos en el curso; picos imprevisibles
Spot Hasta 90 % Ninguno Sí, con 2 min de aviso Procesado nocturno de imágenes del catálogo
Reservadas (RI) Hasta 72 % 1 o 3 años, tipo concreto No Base fija de la tienda una vez estabilizada
Savings Plans Hasta 72 % 1 o 3 años, gasto/hora, no tipo No Igual que RI pero con flexibilidad de familia
Host dedicado — (más caro) Variable No Licencias que exigen hardware físico propio

Tres ideas para quedarte:

  • Spot no es "barato y peor": es exactamente la misma máquina, con la condición de que AWS puede recuperarla. Solo sirve para cargas que toleran ser interrumpidas y reintentadas.
  • Reservadas y Savings Plans no son máquinas, son descuentos de facturación que se aplican automáticamente al consumo que ya tienes.
  • No compres compromisos hasta tener tres meses de datos reales. Marta no reservará nada hasta el módulo 11.

Ciclo de vida de una instancia: parar no es terminar

Una instancia pasa por estados bien definidos, y confundir dos de ellos —stopped y terminated— es el error más caro y más irreversible de EC2.

stateDiagram-v2
    [*] --> pending: run-instances
    pending --> running: arranque completado
    running --> stopping: stop-instances
    stopping --> stopped: apagado
    stopped --> pending: start-instances
    running --> shutting_down: terminate-instances
    stopped --> shutting_down: terminate-instances
    shutting_down --> terminated: recursos liberados
    terminated --> [*]
    running --> rebooting: reboot-instances
    rebooting --> running: mismo host, mismo disco

Qué ocurre exactamente en cada transición:

Acción ¿Se cobra la instancia? ¿Se conserva el disco raíz? ¿Cambia la IP pública? ¿Reversible?
Reiniciar (reboot) Sí (nunca se detuvo) No
Parar (stop) No (sí el disco EBS) , se pierde la IP pública automática
Hibernar No Sí, incluida la RAM volcada a disco
Terminar (terminate) No No, se borra por defecto No, jamás

Los cuatro matices que hay que memorizar:

  1. Parar no borra nada. El volumen EBS raíz sigue existiendo y sigue costando dinero (unos 0,08 USD por GB y mes con gp3). Una instancia parada es casi gratis, pero no del todo.
  2. Terminar es definitivo. No hay papelera. El volumen raíz se elimina salvo que hayas desactivado DeleteOnTermination. Si guardabas datos ahí, se han ido.
  3. La IP pública automática se pierde al parar. Al arrancar de nuevo recibes otra distinta. Si necesitas una IP estable, se usa una Elastic IP (módulo 3) o, mejor, un nombre DNS (03-05).
  4. La IP privada sí se conserva mientras la instancia exista.

Para producción, Marta activará siempre la protección contra terminación:

# Impide que un terminate-instances accidental destruya la instancia.
aws ec2 modify-instance-attribute \
  --instance-id i-0123456789abcdef0 \
  --disable-api-termination \
  --profile mercadofresco-dev --region eu-west-1

Para volver a permitir el borrado hay que ejecutar el comando con --no-disable-api-termination. Es una fricción deliberada: dos pasos conscientes en lugar de un clic irreversible.

Acceso a la instancia: pares de claves, SSH, Instance Connect y Session Manager

Un par de claves es una pareja de claves criptográficas. AWS guarda la pública y la coloca dentro de la instancia al arrancar (en ~/.ssh/authorized_keys); tú te quedas la privada en un fichero .pem. AWS no guarda copia de la clave privada: si la pierdes, no hay recuperación posible por soporte.

# Crea el par de claves y guarda la privada con permisos correctos.
aws ec2 create-key-pair \
  --key-name mercadofresco-tienda \
  --key-type ed25519 \
  --query 'KeyMaterial' --output text \
  --profile mercadofresco-dev --region eu-west-1 \
  > ~/.ssh/mercadofresco-tienda.pem

# Sin este chmod, el cliente SSH se niega a usar la clave por ser legible por otros usuarios.
chmod 400 ~/.ssh/mercadofresco-tienda.pem

Notas del comando:

  • --key-type ed25519 es más moderno y corto que RSA; ambos sirven, pero ed25519 es la elección por defecto hoy (Windows con instancias antiguas puede requerir rsa).
  • La salida de KeyMaterial solo se muestra una vez. Si no rediriges a fichero, la has perdido.

Conexión, una vez la instancia esté en running:

ssh -i ~/.ssh/mercadofresco-tienda.pem ec2-user@<IP_PUBLICA>

El usuario por defecto depende de la AMI: ec2-user en Amazon Linux, ubuntu en Ubuntu, admin en Debian. Conectarse como root está deshabilitado a propósito.

Existen dos alternativas que evitan gestionar ficheros .pem, y conviene conocerlas:

Método Requiere clave .pem Requiere puerto 22 abierto Registro de auditoría Comentario
SSH clásico Sí, desde tu IP No, salvo que lo montes Universal, funciona siempre
EC2 Instance Connect No Sí (desde rangos de AWS) Sí, en CloudTrail Botón "Conectar" de la consola; inyecta una clave temporal de 60 s
Session Manager No No, ningún puerto abierto Sí, completo, con grabación de sesión Requiere el agente SSM y un rol IAM en la instancia

Session Manager (parte de AWS Systems Manager) es la opción que usará MercadoFresco en producción: la instancia no necesita IP pública ni puerto SSH abierto, porque es ella la que inicia la conexión saliente hacia AWS. Menos superficie de ataque y auditoría completa de quién entró y qué tecleó.

# Con el plugin de Session Manager instalado en tu equipo:
aws ssm start-session \
  --target i-0123456789abcdef0 \
  --profile mercadofresco-dev --region eu-west-1

Los permisos IAM que esto requiere se detallan en la lección 04-01; los grupos de seguridad que controlan el puerto 22, en la 03-02.

user data: que la instancia se instale sola

El campo user data es un script que la instancia ejecuta como root, una sola vez, en su primer arranque. Es la diferencia entre "he creado un servidor" y "he creado un servidor que ya está sirviendo la tienda".

Este es el script de MercadoFresco. Guárdalo como user-data-tienda.sh:

#!/bin/bash
set -euxo pipefail
# set -e  : aborta a la primera orden que falle
# set -u  : error si se usa una variable no definida
# set -x  : traza cada orden en el log (imprescindible para depurar después)
# pipefail: un fallo en mitad de una tubería no queda enmascarado

# 1. Actualizar el sistema y registrar la marca temporal de arranque
dnf update -y
echo "Arranque de instancia MercadoFresco: $(date -Is)" >> /var/log/mercadofresco-arranque.log

# 2. Instalar el servidor web y PHP (el monolito de MercadoFresco es PHP)
dnf install -y nginx php-fpm php-pgsql

# 3. Recuperar metadatos de la propia instancia usando IMDSv2 (ver apartado siguiente)
TOKEN=$(curl -sX PUT "http://169.254.169.254/latest/api/token" \
  -H "X-aws-ec2-metadata-token-ttl-seconds: 300")
INSTANCE_ID=$(curl -s -H "X-aws-ec2-metadata-token: $TOKEN" \
  http://169.254.169.254/latest/meta-data/instance-id)
AZ=$(curl -s -H "X-aws-ec2-metadata-token: $TOKEN" \
  http://169.254.169.254/latest/meta-data/placement/availability-zone)

# 4. Página de inicio provisional que identifica qué instancia responde.
#    Esto es fundamental cuando tengamos varias detrás de un balanceador (lección 03-03):
#    recargando la página se ve a cuál te ha tocado.
cat > /usr/share/nginx/html/index.html <<HTML
<!doctype html>
<html lang="es">
<head><meta charset="utf-8"><title>MercadoFresco</title></head>
<body style="font-family:system-ui;max-width:40rem;margin:4rem auto">
  <h1>MercadoFresco</h1>
  <p>Producto fresco en 24 horas.</p>
  <hr>
  <p><strong>Instancia:</strong> ${INSTANCE_ID}</p>
  <p><strong>Zona de disponibilidad:</strong> ${AZ}</p>
</body>
</html>
HTML

# 5. Punto de comprobación de salud para el balanceador y para Auto Scaling.
#    Debe responder 200 y ser barato: nada de consultar la base de datos aquí.
echo "OK" > /usr/share/nginx/html/salud

# 6. Arrancar los servicios y dejarlos habilitados para futuros reinicios
systemctl enable --now nginx php-fpm

Cómo se comporta y cómo se depura:

  • Se ejecuta solo en el primer arranque. Si paras y arrancas la instancia, no se repite.
  • La salida completa queda en /var/log/cloud-init-output.log dentro de la instancia. Cuando algo "no funciona y no sé por qué", ese fichero es la primera parada.
  • Debe ser idempotente y sin interacción: nada de apt install sin -y, nada de esperar una pulsación de tecla.
  • Nunca metas credenciales en user data: cualquiera con acceso a la instancia puede leerlo con curl a los metadatos. Para secretos se usa Secrets Manager (lección 04-03).

Metadatos de instancia e IMDSv2

Cada instancia puede preguntarse a sí misma quién es, consultando una dirección especial que solo responde desde dentro: 169.254.169.254. Ahí viven los metadatos de instancia: su ID, su tipo, su AZ, sus IPs, sus etiquetas (si lo habilitas) y —muy importante— las credenciales temporales del rol IAM asociado.

Esa última parte explica por qué la seguridad de este endpoint importa tanto. La versión 1 (IMDSv1) respondía a cualquier GET, lo que permitía que una vulnerabilidad de tipo SSRF en la aplicación web hiciera que el propio servidor filtrara sus credenciales. IMDSv2 exige obtener antes un token por PUT, algo que un SSRF simple no puede hacer.

# Paso 1: pedir el token (obligatorio en IMDSv2). TTL en segundos.
TOKEN=$(curl -sX PUT "http://169.254.169.254/latest/api/token" \
  -H "X-aws-ec2-metadata-token-ttl-seconds: 21600")

# Paso 2: usar el token en cada consulta
curl -s -H "X-aws-ec2-metadata-token: $TOKEN" \
  http://169.254.169.254/latest/meta-data/instance-id

curl -s -H "X-aws-ec2-metadata-token: $TOKEN" \
  http://169.254.169.254/latest/meta-data/instance-type

# Listar todas las rutas disponibles
curl -s -H "X-aws-ec2-metadata-token: $TOKEN" \
  http://169.254.169.254/latest/meta-data/

Rutas útiles en el día a día:

Ruta Devuelve
instance-id i-0123456789abcdef0
instance-type t3.micro
placement/availability-zone eu-west-1a
placement/region eu-west-1
local-ipv4 / public-ipv4 IP privada / pública
iam/security-credentials/<rol> Credenciales temporales del rol
spot/instance-action Aviso de recuperación de una instancia spot

Política de MercadoFresco: exigir IMDSv2 siempre. Se fuerza al lanzar la instancia con --metadata-options "HttpTokens=required", como haremos en el comando de creación.

Crear la instancia por consola, paso a paso

Haremos primero el camino visual, porque enseña el vocabulario, y después el mismo resultado por CLI, que es el que se automatiza.

  1. Consola → busca EC2 → comprueba arriba a la derecha que la región es Irlanda (eu-west-1). Esta comprobación no es opcional: es la trampa que vimos en 01-04.
  2. Instancias → Lanzar instancias.
  3. Nombre y etiquetas: mercadofresco-tienda-01. Pulsa Agregar etiquetas adicionales y completa el esquema obligatorio del proyecto:
    • Proyecto = mercadofresco
    • Entorno = desarrollo
    • Componente = tienda
    • Propietario = luis
    • CentroCoste = operaciones
  4. Imagen (AMI): Amazon Linux 2023, arquitectura x86_64. Fíjate en la etiqueta Apto para la capa gratuita.
  5. Tipo de instancia: t3.micro.
  6. Par de claves: selecciona mercadofresco-tienda (el que creaste antes) o créalo aquí y descarga el .pem.
  7. Configuración de red: deja la VPC por defecto y crea un grupo de seguridad llamado sg-mercadofresco-tienda con dos reglas de entrada: HTTP (80) desde 0.0.0.0/0 y SSH (22) desde Mi IP. Nunca SSH abierto al mundo. El detalle de esto es la lección 03-02.
  8. Almacenamiento: 8 GiB gp3, el valor por defecto. Los discos son la lección 02-02.
  9. Detalles avanzados → despliega hasta el final y pega el contenido de user-data-tienda.sh en el campo Datos de usuario. En el mismo bloque, verifica que Versión de IMDS esté en V2 obligatorio.
  10. Revisa el panel de resumen a la derecha y pulsa Lanzar instancia.

En 30-60 segundos el estado pasará a running y las comprobaciones de estado (2/2) se pondrán en verde. Copia la IP pública y ábrela en el navegador: verás la página de MercadoFresco con el ID de la instancia y su zona de disponibilidad.

Aviso de coste. Un t3.micro está dentro de la capa gratuita durante los 12 primeros meses (750 h/mes). Si ya la has agotado, cuesta del orden de 0,01 USD/hora en eu-west-1. El apartado final explica cómo borrarlo todo.

Crear la instancia por CLI con el esquema de etiquetado

El mismo resultado, en un solo comando reproducible. Primero el grupo de seguridad mínimo (se detalla en 03-02; aquí es solo instrumental):

# Crear el grupo de seguridad en la VPC por defecto
SG_ID=$(aws ec2 create-security-group \
  --group-name sg-mercadofresco-tienda \
  --description "Acceso web y SSH para la tienda de MercadoFresco" \
  --query 'GroupId' --output text \
  --profile mercadofresco-dev --region eu-west-1)

# Abrir HTTP a todo el mundo (es una tienda pública)
aws ec2 authorize-security-group-ingress \
  --group-id "$SG_ID" --protocol tcp --port 80 --cidr 0.0.0.0/0 \
  --profile mercadofresco-dev --region eu-west-1

# Abrir SSH SOLO a tu IP actual
MI_IP=$(curl -s https://checkip.amazonaws.com)
aws ec2 authorize-security-group-ingress \
  --group-id "$SG_ID" --protocol tcp --port 22 --cidr "${MI_IP}/32" \
  --profile mercadofresco-dev --region eu-west-1

Y ahora la instancia:

aws ec2 run-instances \
  --image-id "$AMI_ID" \
  --instance-type t3.micro \
  --key-name mercadofresco-tienda \
  --security-group-ids "$SG_ID" \
  --user-data file://user-data-tienda.sh \
  --metadata-options "HttpTokens=required,HttpPutResponseHopLimit=1" \
  --credit-specification "CpuCredits=standard" \
  --tag-specifications \
    'ResourceType=instance,Tags=[
      {Key=Name,Value=mercadofresco-tienda-01},
      {Key=Proyecto,Value=mercadofresco},
      {Key=Entorno,Value=desarrollo},
      {Key=Componente,Value=tienda},
      {Key=Propietario,Value=luis},
      {Key=CentroCoste,Value=operaciones}]' \
    'ResourceType=volume,Tags=[
      {Key=Proyecto,Value=mercadofresco},
      {Key=Entorno,Value=desarrollo},
      {Key=Componente,Value=tienda},
      {Key=Propietario,Value=luis},
      {Key=CentroCoste,Value=operaciones}]' \
  --profile mercadofresco-dev --region eu-west-1

Parámetro a parámetro, porque cada uno tiene su porqué:

  • --user-data file://…: el prefijo file:// es obligatorio; sin él, la CLI enviaría la cadena literal user-data-tienda.sh como script. La CLI v2 codifica el fichero en base64 por ti.
  • --metadata-options HttpTokens=required: fuerza IMDSv2. HttpPutResponseHopLimit=1 impide que un contenedor dentro de la instancia alcance los metadatos del host.
  • --credit-specification CpuCredits=standard: en pruebas preferimos que la instancia se estrangule antes que generar cargos inesperados por unlimited.
  • --tag-specifications: se pasa dos veces, una para la instancia y otra para el volumen. Este es el error de etiquetado más frecuente: se etiqueta la instancia, se olvida el disco, y en el módulo 11 aparece un gasto de EBS que no se sabe a quién imputar.

Comprobar el resultado con una consulta legible:

aws ec2 describe-instances \
  --filters "Name=tag:Proyecto,Values=mercadofresco" \
            "Name=instance-state-name,Values=running" \
  --query 'Reservations[].Instances[].{
      ID:InstanceId,
      Tipo:InstanceType,
      Estado:State.Name,
      IP:PublicIpAddress,
      AZ:Placement.AvailabilityZone,
      Nombre:Tags[?Key==`Name`]|[0].Value}' \
  --output table \
  --profile mercadofresco-dev --region eu-west-1

Plantillas de lanzamiento y Auto Scaling: la solución al pico de los viernes

Ya tenemos un servidor. Pero un servidor no resuelve el problema 1: los viernes de 17:00 a 21:00 llegan 900 pedidos/hora y una instancia soporta 600. Y comprar una máquina más grande "para el viernes" significa pagarla las 168 horas de la semana para usarla 4.

La respuesta de AWS son dos piezas que trabajan juntas.

Plantilla de lanzamiento (launch template)

Es la receta versionada de cómo debe ser una instancia de la tienda: AMI, tipo, par de claves, grupo de seguridad, user data, etiquetas. Deja de haber "la instancia que montó Luis a mano"; hay una definición que cualquiera puede reproducir idéntica.

# El user data debe ir en base64 dentro del JSON de la plantilla
USER_DATA_B64=$(base64 -w0 user-data-tienda.sh)

aws ec2 create-launch-template \
  --launch-template-name lt-mercadofresco-tienda \
  --version-description "v1 nginx + php-fpm" \
  --launch-template-data "{
    \"ImageId\": \"$AMI_ID\",
    \"InstanceType\": \"t3.micro\",
    \"KeyName\": \"mercadofresco-tienda\",
    \"SecurityGroupIds\": [\"$SG_ID\"],
    \"UserData\": \"$USER_DATA_B64\",
    \"MetadataOptions\": {\"HttpTokens\": \"required\"},
    \"TagSpecifications\": [{
      \"ResourceType\": \"instance\",
      \"Tags\": [
        {\"Key\": \"Name\", \"Value\": \"mercadofresco-tienda-asg\"},
        {\"Key\": \"Proyecto\", \"Value\": \"mercadofresco\"},
        {\"Key\": \"Entorno\", \"Value\": \"desarrollo\"},
        {\"Key\": \"Componente\", \"Value\": \"tienda\"},
        {\"Key\": \"Propietario\", \"Value\": \"luis\"},
        {\"Key\": \"CentroCoste\", \"Value\": \"operaciones\"}
      ]}]
  }" \
  --profile mercadofresco-dev --region eu-west-1

Las plantillas son versionadas: al cambiar el user data creas la versión 2 y puedes volver a la 1 si algo sale mal. Esa capacidad de revertir es el primer paso hacia el problema 4 (despliegues arriesgados), que se resolverá del todo en el módulo 8.

Grupo de Auto Scaling (ASG)

Un ASG mantiene un número de instancias sanas repartidas entre varias zonas de disponibilidad, y lo ajusta según la demanda. Tiene tres números:

Parámetro Significado Valor en MercadoFresco
Mínimo Nunca bajará de aquí 2 (una por AZ: tolerancia a fallo)
Deseado Cuántas quiere tener ahora 2 en reposo
Máximo Nunca subirá de aquí (tope de gasto) 4

El dimensionado sale de las cifras reales:

Pico de los viernes:        900 pedidos/hora
Capacidad por instancia:    600 pedidos/hora
Instancias necesarias:      900 / 600 = 1,5 → 2 como mínimo funcional
Margen para fallo de una AZ: +1
Máximo con margen de crecimiento: 4
aws autoscaling create-auto-scaling-group \
  --auto-scaling-group-name asg-mercadofresco-tienda \
  --launch-template "LaunchTemplateName=lt-mercadofresco-tienda,Version=\$Latest" \
  --min-size 2 --max-size 4 --desired-capacity 2 \
  --vpc-zone-identifier "subnet-aaa11111,subnet-bbb22222" \
  --health-check-type EC2 --health-check-grace-period 120 \
  --tags "Key=Proyecto,Value=mercadofresco,PropagateAtLaunch=true" \
         "Key=Entorno,Value=desarrollo,PropagateAtLaunch=true" \
  --profile mercadofresco-dev --region eu-west-1
  • --vpc-zone-identifier: dos subredes en dos AZ distintas (eu-west-1a y eu-west-1b), tal como decidimos en 01-03. Sustituye los IDs por los de tu VPC por defecto.
  • --health-check-grace-period 120: da 2 minutos al user data antes de juzgar si la instancia está sana. Sin esta espera, el ASG mata instancias que aún se estaban instalando.
  • PropagateAtLaunch=true: las etiquetas se copian a cada instancia nueva. Sin esto, las máquinas creadas por el ASG aparecerían sin etiquetar en la factura.

Y la política que hace el trabajo automáticamente:

# Escalado por seguimiento de objetivo: "mantén la CPU media del grupo al 60 %".
aws autoscaling put-scaling-policy \
  --auto-scaling-group-name asg-mercadofresco-tienda \
  --policy-name cpu-objetivo-60 \
  --policy-type TargetTrackingScaling \
  --target-tracking-configuration '{
    "TargetValue": 60.0,
    "PredefinedMetricSpecification": {"PredefinedMetricType": "ASGAverageCPUUtilization"}
  }' \
  --profile mercadofresco-dev --region eu-west-1

El target tracking es la política recomendada: declaras el objetivo y AWS calcula solo cuándo añadir y cuándo quitar máquinas. Alternativas: escalado por pasos (umbrales manuales) y escalado programado, que para MercadoFresco tiene mucho sentido porque el pico es a hora conocida:

# Subir a 4 instancias todos los viernes a las 16:45 UTC, antes de que llegue el pico.
aws autoscaling put-scheduled-update-group-action \
  --auto-scaling-group-name asg-mercadofresco-tienda \
  --scheduled-action-name pico-viernes-tarde \
  --recurrence "45 16 * * 5" \
  --desired-capacity 4 \
  --profile mercadofresco-dev --region eu-west-1

El flujo completo:

flowchart TD
    A["Viernes 17:00<br/>llegan 900 pedidos/hora"] --> B["CloudWatch mide<br/>CPU media del grupo"]
    B --> C{"¿CPU media<br/>&gt; 60 %?"}
    C -->|Sí| D["ASG lanza instancias<br/>desde lt-mercadofresco-tienda"]
    D --> E["user data instala<br/>nginx + php-fpm"]
    E --> F["Comprobación de salud<br/>/salud responde 200"]
    F --> G["Instancia en servicio<br/>capacidad = 4 x 600 = 2.400 p/h"]
    C -->|No, sábado 03:00| H["ASG reduce hasta el mínimo<br/>2 instancias"]
    H --> I["Se deja de pagar<br/>lo que no se usa"]

Falta una pieza para que esto funcione de verdad: algo que reparta el tráfico entre las instancias. Ese es el Elastic Load Balancing, la lección 03-03. Aquí hemos montado el motor; en el módulo 3 le conectaremos la dirección.

Apagar y borrar todo para no gastar

Regla de oro del curso: lo que creas en una lección, lo borras al terminarla.

# 1. Vaciar y eliminar el grupo de Auto Scaling (--force-delete termina sus instancias)
aws autoscaling delete-auto-scaling-group \
  --auto-scaling-group-name asg-mercadofresco-tienda --force-delete \
  --profile mercadofresco-dev --region eu-west-1

# 2. Eliminar la plantilla de lanzamiento
aws ec2 delete-launch-template \
  --launch-template-name lt-mercadofresco-tienda \
  --profile mercadofresco-dev --region eu-west-1

# 3. Terminar la instancia creada a mano
aws ec2 terminate-instances --instance-ids i-0123456789abcdef0 \
  --profile mercadofresco-dev --region eu-west-1

# 4. Verificar que no queda NADA en ejecución con la etiqueta del proyecto
aws ec2 describe-instances \
  --filters "Name=tag:Proyecto,Values=mercadofresco" \
            "Name=instance-state-name,Values=running,pending,stopped" \
  --query 'Reservations[].Instances[].[InstanceId,State.Name]' --output table \
  --profile mercadofresco-dev --region eu-west-1

Si quieres conservar la instancia para la lección siguiente sin pagar cómputo, párala en lugar de terminarla (seguirás pagando solo el volumen de 8 GiB, unos 0,64 USD al mes):

aws ec2 stop-instances --instance-ids i-0123456789abcdef0 \
  --profile mercadofresco-dev --region eu-west-1

Y recuerda revisar el presupuesto presupuesto-mensual-mercadofresco que configuraste en 01-02: es tu red de seguridad si algo se queda encendido.

Errores Comunes y Consejos

  • Terminar creyendo que se para. El error irreversible por excelencia. Activa siempre --disable-api-termination en cualquier instancia con datos.
  • Perder el fichero .pem. AWS no tiene copia. Si lo pierdes, la única salida es desconectar el volumen y montarlo en otra instancia. Guárdalo en el gestor de contraseñas y ten habilitado Session Manager como plan B.
  • Abrir el puerto 22 a 0.0.0.0/0. Recibirás intentos de acceso automatizados en cuestión de minutos. Siempre <tu-ip>/32, o mejor, ningún puerto SSH y Session Manager.
  • Sorprenderse porque la CPU se queda plana al 10 %. Son los créditos burstable agotados. Mira la métrica CPUCreditBalance antes de culpar a la aplicación.
  • Olvidar file:// en --user-data. La instancia arranca "bien" pero no instala nada. Si el servidor web no responde, entra y lee /var/log/cloud-init-output.log.
  • Etiquetar la instancia y no el volumen. Pasa --tag-specifications para instance y para volume, siempre.
  • Esperar que user data se ejecute en cada arranque. Solo corre la primera vez. Para tareas de cada arranque, usa una unidad de systemd creada por el propio user data.
  • Confundir la IP pública con algo estable. Cambia al parar y arrancar. Usa DNS (03-05).
  • Dejar el ASG con min-size alto "por si acaso". Es la forma más silenciosa de multiplicar la factura: cada instancia del mínimo se paga 24×7.
  • Consejo de oro: empieza pequeño y mide. Es infinitamente más barato subir de tamaño una instancia infradimensionada que descubrir en el módulo 11 que llevas seis meses pagando el doble.

Ejercicios

Ejercicio 1: dimensionar el grupo para un crecimiento a tres ciudades

MercadoFresco quiere abrir en dos ciudades más (problema 3). La previsión es que el pico de los viernes pase de 900 a 2.100 pedidos/hora. Cada instancia sigue soportando 600 pedidos/hora y la política de Marta exige que el servicio siga funcionando aunque caiga una zona de disponibilidad completa.

Calcula el mínimo, el deseado y el máximo del ASG, justificando cada número, y escribe el comando aws autoscaling update-auto-scaling-group correspondiente.

Ejercicio 2: diagnosticar un user data que no funciona

Luis lanza una instancia con el script de la lección, la instancia aparece running con 2/2 comprobaciones en verde, pero al abrir la IP pública el navegador se queda esperando indefinidamente (no da "conexión rechazada", simplemente no responde).

Enumera, en orden de coste creciente de investigación, los pasos que darías y qué comprobarías en cada uno. Indica cuál es la causa más probable dada la pista del "se queda esperando".

Ejercicio 3: elegir tipo y modelo de compra

Para cada una de estas tres cargas de MercadoFresco, elige familia, tamaño aproximado y modelo de compra, y justifica en una frase:

  • A) Servidor de la tienda en producción, tráfico sostenido las 24 horas, 4 GiB de RAM suficientes, CPU al 55 % de media.
  • B) Proceso nocturno que recomprime las 40.000 fotos del catálogo; tarda 3 horas, satura la CPU, y si se interrumpe puede reanudarse desde donde iba.
  • C) Entorno de desarrollo de Luis, encendido de 9:00 a 18:00 de lunes a viernes, casi siempre inactivo con ráfagas al compilar.

Soluciones

Solución 1.

Capacidad necesaria en el pico: 2.100 / 600 = 3,5 → 4 instancias
Tolerancia a la caída de una AZ: el grupo debe seguir dando 4 instancias
  con una AZ menos. Con 2 AZ, la mitad de la capacidad está en cada una,
  así que hay que poder llegar a 8 para sobrevivir a perder la mitad.
  • Mínimo = 2. En valle no hace falta más, pero nunca menos de 2 para tener una instancia en cada AZ y no depender de una sola.
  • Deseado = 2. Es el punto de partida; la política de seguimiento de objetivo lo subirá sola. Se puede combinar con una acción programada que lo lleve a 4 los viernes a las 16:45.
  • Máximo = 8. Cubre las 4 necesarias más el margen para que, si cae una AZ, las 4 restantes puedan lanzarse en la que sigue en pie. El máximo también actúa como tope de gasto: si un ataque o un error dispara la CPU, nunca se pasará de 8 instancias.
aws autoscaling update-auto-scaling-group \
  --auto-scaling-group-name asg-mercadofresco-tienda \
  --min-size 2 --max-size 8 --desired-capacity 2 \
  --profile mercadofresco-dev --region eu-west-1

Solución 2.

El matiz clave: "se queda esperando" ≠ "conexión rechazada". Si nginx estuviera caído pero la red llegase, el sistema operativo devolvería un rechazo inmediato. Un tiempo de espera agotado indica que los paquetes no llegan a la instancia, es decir, un problema de red, no de software.

Pasos por coste creciente:

  1. Grupo de seguridad (30 segundos, sin entrar en la máquina): comprobar que existe una regla de entrada TCP 80 desde 0.0.0.0/0. Esta es la causa más probable.
    aws ec2 describe-security-groups --group-ids "$SG_ID" \\
      --query 'SecurityGroups[].IpPermissions' \\
      --profile mercadofresco-dev --region eu-west-1
    
  2. IP pública: verificar que la instancia tiene una (PublicIpAddress no nulo) y que estás usando esa y no la privada 172.31.x.x.
  3. Subred pública: la subred debe tener ruta a una puerta de enlace de internet (esto se entiende del todo en 03-01).
  4. Entrar por Session Manager (no requiere puerto 22, útil precisamente cuando la red falla) y:
    sudo systemctl status nginx
    sudo tail -50 /var/log/cloud-init-output.log
    curl -s localhost   # si esto responde, el servidor está bien y el problema es de red
    

Solución 3.

Carga Familia y tamaño Modelo de compra Justificación
A) Tienda en producción m6i.large (2 vCPU, 8 GiB) o m7g.large si el software es compatible con ARM Bajo demanda ahora; Savings Plan de 1 año cuando haya 3 meses de datos Carga sostenida 24×7: una T en unlimited acabaría costando más; la M da rendimiento previsible sin créditos
B) Recompresión nocturna c6i.xlarge o mayor (CPU intensiva) Spot Es tolerante a interrupciones y reanudable: cumple exactamente la condición de spot y ahorra hasta el 90 %
C) Desarrollo de Luis t3.medium Bajo demanda + parada programada fuera del horario Perfil ocioso con ráfagas: caso de manual para burstable. Apagarlo por la noche y el fin de semana reduce el coste ~75 %

Conclusión

MercadoFresco ya tiene su primer servidor en AWS, y sobre todo tiene el vocabulario y los mecanismos para razonar sobre él. Sabes que una AMI es la plantilla y la instancia la copia viva, y que el ID de una AMI cambia con la región, por lo que se consulta y no se copia. Sabes descifrar t3.micro y elegir entre las familias T, M, C y R en función de si la carga es irregular, equilibrada, intensiva en CPU o intensiva en memoria. Has visto por dentro el mecanismo de los créditos de CPU y has calculado que un t3.micro no sobrevive a las cuatro horas del pico de los viernes: un dato concreto, no una intuición.

Conoces los cuatro modelos de compra y en qué caso encaja cada uno, y dominas el ciclo de vida con la distinción que más cara sale: parar conserva el disco, terminar lo destruye para siempre. Sabes acceder a la máquina con un par de claves y también sin él, mediante EC2 Instance Connect o Session Manager, que es la opción que MercadoFresco usará en producción por no requerir ningún puerto abierto. Has automatizado la instalación completa de la tienda con user data, sabes dónde leer su log cuando falla, y has protegido los metadatos exigiendo IMDSv2. Has creado la instancia por consola y por CLI aplicando el esquema de etiquetado del proyecto tanto a la instancia como a su volumen.

Y, sobre todo, has puesto la primera pieza real contra el problema 1: una plantilla de lanzamiento versionada y un grupo de Auto Scaling de 2 a 4 instancias con seguimiento de objetivo y escalado programado, dimensionado con las cifras reales de MercadoFresco (900 pedidos/h frente a 600 por instancia). Falta el balanceador que reparta el tráfico entre ellas, y eso es la lección 03-03.

Antes de eso hay una pregunta que el Auto Scaling deja al descubierto: si las instancias nacen y mueren solas, ¿dónde viven los datos? Las fotos de producto que hoy están en /var/www/fotos no pueden estar dentro de un disco que se destruye con la máquina. En la lección 02-02, «Almacenamiento de bloques y de archivos: EBS y EFS», veremos los volúmenes que sobreviven a la instancia, cómo se amplían en caliente, cómo se comparten entre varias máquinas y cómo los snapshots automatizados con Data Lifecycle Manager cierran de una vez el problema 2 de MercadoFresco: las copias de seguridad que nunca fueron fiables.

Curso de AWS

Módulo 1: Introducción a AWS

Módulo 2: Servicios principales de AWS

Módulo 3: Redes y entrega de contenido

Módulo 4: Seguridad e identidad

Módulo 5: Monitorización y gestión

Módulo 6: Bases de datos

Módulo 7: Integración de aplicaciones

Módulo 8: Herramientas para desarrolladores

Módulo 9: Infraestructura como código y gobierno de cuentas

Módulo 10: Contenedores en AWS

Módulo 11: Mejores prácticas y gestión de costos

© Copyright 2026. Todos los derechos reservados