Lab práctico · Semana 2: Cómputo: Compute Engine, MIG, GKE, Cloud Run y cuándo usar cada uno

MIG regional con autoescalado, autocuración y actualización progresiva

⏱ 90-120 minDificultad: mediaApartados: 2.31.2

Qué vas a construir

Un Managed Instance Group regional en Madrid, creado desde un instance template, con autoescalado por CPU, autocuración mediante health check y una actualización progresiva a una segunda versión sin perder capacidad. El balanceador de carga delante lo añadirás en el lab 07.

flowchart TB
  T1["Template web-v1"] --> MIG["MIG regional web-mig, europe-southwest1"]
  T2["Template web-v2"] -.->|"rolling update"| MIG
  MIG --> A["VM zona a"]
  MIG --> B["VM zona b"]
  AS["Autoscaler: CPU 60 %, de 2 a 4 VMs"] --> MIG
  HC["Health check HTTP en el puerto 80"] --> MIG

Antes de empezar

  • Labs 01 y 02 hechos.
  • Crea un proyecto para el lab y habilita Compute Engine:
export PROJECT_ID="pca-lab-03-$RANDOM"
export REGION="europe-southwest1"
export BILLING_ID="$(gcloud billing accounts list --format='value(ACCOUNT_ID)' --limit=1)"
gcloud projects create "$PROJECT_ID" --name="pca-lab-03"
gcloud billing projects link "$PROJECT_ID" --billing-account="$BILLING_ID"
gcloud config set project "$PROJECT_ID"
gcloud config set compute/region "$REGION"
gcloud services enable compute.googleapis.com

Al habilitar Compute Engine en un proyecto nuevo se crea la red default con reglas de firewall básicas (entre ellas, SSH desde cualquier origen). Compruébalo con gcloud compute networks list. Si tu proyecto no la tiene, créala con gcloud compute networks create default --subnet-mode=auto y añade una regla que permita el puerto 22.

Paso 1: Crea el instance template

Qué y por qué: el template define cómo es cada VM. Un script de arranque instala nginx y publica una página con el nombre de la VM y la versión. Usas un template regional (recomendado salvo que lo necesites en varias regiones).

cat > arranque-v1.sh <<'EOF'
#!/bin/bash
apt-get update
apt-get install -y nginx
echo "<h1>v1 - $(hostname)</h1>" > /var/www/html/index.html
systemctl enable --now nginx
EOF

gcloud compute instance-templates create web-v1 \
  --instance-template-region="$REGION" \
  --machine-type=e2-small \
  --image-family=debian-12 --image-project=debian-cloud \
  --tags=web \
  --metadata-from-file=startup-script=arranque-v1.sh

Por consola: Compute Engine → Plantillas de instancia → Crear plantilla, ubicación Regional.

Paso 2: Permite el tráfico web y el de los health checks

Qué y por qué: los health checks de Google Cloud llegan desde los rangos 35.191.0.0/16 y 130.211.0.0/22. Sin esta regla, todas las VMs parecerían «enfermas» y el MIG las recrearía sin parar.

gcloud compute firewall-rules create permitir-web-y-hc \
  --network=default --direction=INGRESS --action=ALLOW \
  --rules=tcp:80 --target-tags=web \
  --source-ranges=35.191.0.0/16,130.211.0.0/22,0.0.0.0/0

(Abrir el 80 a 0.0.0.0/0 es solo para poder probar desde el navegador; en producción el tráfico entraría por el balanceador.)

Paso 3: Crea el MIG regional

Qué y por qué: el grupo reparte las VMs entre zonas de la región, de modo que la caída de una zona no deja el servicio sin VMs.

gcloud compute instance-groups managed create web-mig \
  --region="$REGION" \
  --template="projects/${PROJECT_ID}/regions/${REGION}/instanceTemplates/web-v1" \
  --size=2

gcloud compute instance-groups managed list-instances web-mig --region="$REGION"

La primera vez que uses gcloud compute ssh, Cloud Shell genera una clave SSH (con --quiet, sin pedirte frase de paso).

Verás dos VMs con nombres web-mig-xxxx en zonas distintas. Copia la IP externa de una (gcloud compute instances list) y ábrela en el navegador: http://IP. Debe mostrar «v1 - web-mig-xxxx» (tarda uno o dos minutos en instalar nginx).

Paso 4: Configura la autocuración

Qué y por qué: el MIG ya recrea VMs que se caen, pero no sabe si la aplicación responde. Un health check HTTP lo detecta. El retardo inicial evita recrear VMs que aún están arrancando.

gcloud compute health-checks create http hc-web \
  --port=80 --request-path=/ \
  --check-interval=10s --timeout=5s \
  --healthy-threshold=2 --unhealthy-threshold=3

gcloud compute instance-groups managed update web-mig \
  --region="$REGION" \
  --health-check=hc-web --initial-delay=120

Provoca un fallo: entra por SSH en una VM y detén nginx.

read VM ZONA < <(gcloud compute instances list --filter="name~^web-mig" --format="value(name,zone.basename())" --limit=1)
gcloud compute ssh "$VM" --zone="$ZONA" --quiet --command="sudo systemctl stop nginx"

Observa durante un par de minutos:

watch -n 10 "gcloud compute instance-groups managed list-instances web-mig --region=$REGION"

La VM pasará a UNHEALTHY y después el MIG la recreará (acción RECREATING) hasta que vuelva a estar HEALTHY. Sal de watch con Ctrl+C.

Paso 5: Configura el autoescalado y pruébalo

Qué y por qué: mantener la CPU media en torno al 60 % añadiendo VMs cuando sube la carga y quitándolas cuando baja.

gcloud compute instance-groups managed set-autoscaling web-mig \
  --region="$REGION" \
  --min-num-replicas=2 --max-num-replicas=4 \
  --target-cpu-utilization=0.60 \
  --cool-down-period=90

(--cool-down-period es el periodo de inicialización.)

Genera carga en las VMs con un bucle que consume CPU durante 10 minutos:

for LINEA in $(gcloud compute instances list --filter="name~^web-mig" --format="csv[no-heading](name,zone.basename())"); do
  N=${LINEA%%,*}; Z=${LINEA##*,}
  gcloud compute ssh "$N" --zone="$Z" --quiet --command="nohup timeout 600 bash -c 'yes > /dev/null & yes > /dev/null & wait' > /dev/null 2>&1 &"
done

Observa el grupo:

watch -n 20 "gcloud compute instance-groups managed list-instances web-mig --region=$REGION"

En unos minutos deben aparecer VMs nuevas hasta un máximo de 4. Cuando termine la carga, tras el periodo de estabilización (unos 10 minutos), el grupo volverá a 2. Puedes seguirlo también en la consola: Grupos de instancias → web-mig → Supervisión.

Paso 6: Actualización progresiva a v2

Qué y por qué: publicas una versión nueva sin perder capacidad: maxUnavailable=0 hace que primero se cree la VM nueva y después se borre la vieja. Como el template es inmutable, creas uno nuevo.

sed 's/v1/v2/' arranque-v1.sh > arranque-v2.sh

gcloud compute instance-templates create web-v2 \
  --instance-template-region="$REGION" \
  --machine-type=e2-small \
  --image-family=debian-12 --image-project=debian-cloud \
  --tags=web \
  --metadata-from-file=startup-script=arranque-v2.sh

gcloud compute instance-groups managed rolling-action start-update web-mig \
  --region="$REGION" \
  --version="template=projects/${PROJECT_ID}/regions/${REGION}/instanceTemplates/web-v2" \
  --max-surge=3 --max-unavailable=0

watch -n 15 "gcloud compute instance-groups managed list-instances web-mig --region=$REGION"

Verás VMs nuevas creándose antes de que se borren las antiguas. Cuando termine, la página mostrará «v2». Para un canary, habrías pasado dos versiones, por ejemplo --version=template=...web-v1 --canary-version=template=...web-v2,target-size=50%.

Consulta el estado de la actualización:

gcloud compute instance-groups managed describe web-mig --region="$REGION" \
  --format="yaml(status, updatePolicy, versions)"

Comprueba que funciona

  • Las VMs del grupo están repartidas en al menos dos zonas.
  • Al parar nginx, la VM pasa a UNHEALTHY y se recrea sola.
  • Con carga, el grupo crece hasta 4 VMs y después vuelve a 2.
  • Tras la actualización, todas las VMs sirven «v2» y status.isStable es true.

Limpieza

Lo más seguro es borrar el proyecto completo:

gcloud projects delete "$PROJECT_ID"

Si prefieres borrar recurso a recurso (por ejemplo, para practicar el orden de dependencias):

gcloud compute instance-groups managed delete web-mig --region="$REGION" --quiet
gcloud compute instance-templates delete web-v1 web-v2 --region="$REGION" --quiet
gcloud compute health-checks delete hc-web --quiet
gcloud compute firewall-rules delete permitir-web-y-hc --quiet

Comprueba que no queda ninguna VM: gcloud compute instances list.

Preguntas para pensar como arquitecto

1. ¿Qué habría pasado sin la regla de firewall para los rangos de health check?

Todas las VMs habrían fallado el health check y el MIG las habría recreado una y otra vez (con el retardo inicial entre medias). Es un error clásico: el problema no es la aplicación, sino la red.

2. La aplicación tarda 6 minutos en arrancar y el tráfico sube todos los días a las 9:00. ¿Cómo mejoras el autoescalado?

Ajustar el periodo de inicialización al tiempo real de arranque, añadir autoescalado predictivo o un horario (schedule) que suba la capacidad antes de las 9:00, y reducir el tiempo de arranque con una imagen personalizada que ya traiga el software instalado.

3. ¿Por qué un MIG regional y no uno zonal para esta web?

Porque reparte las VMs entre zonas: si una zona cae, las demás siguen sirviendo y el MIG intenta recuperar la capacidad en las zonas sanas. Un MIG zonal cae entero con su zona.

4. Si esta carga fuera un procesamiento por lotes que puede reintentarse, ¿qué cambiarías para ahorrar?

Usar Spot VMs en el template (--provisioning-model=SPOT); el MIG recrea las VMs reclamadas cuando hay capacidad. Y escalar por una métrica de trabajo pendiente (por ejemplo, mensajes sin procesar en Pub/Sub) en lugar de por CPU.

5. ¿Qué configuración de actualización usarías si el presupuesto no permite VMs extra durante el despliegue?

maxSurge=0 y maxUnavailable mayor que cero: se sustituyen VMs sin crear adicionales, a costa de perder capacidad temporalmente. Ojo: en un MIG regional, un valor fijo de maxUnavailable (y de maxSurge) debe ser 0 o al menos el número de zonas del grupo (normalmente 3); también puedes usar un porcentaje. Es el trade-off coste frente a disponibilidad.


Volver al módulo