Lab práctico · Semana 9: Fiabilidad y operaciones — Well-Architected, alta disponibilidad, DR, SRE y observabilidad

Observabilidad y SLO de un servicio en Cloud Run

⏱ 90-120 minDificultad: mediaApartados: 6.26.16.6

Qué vas a construir

Un servicio pequeño en Cloud Run que responde bien o con error según la ruta. Sobre él montarás la observabilidad de un servicio real: un uptime check desde varias regiones, un SLO de disponibilidad con su alerta por burn rate, logs estructurados y una métrica basada en logs. Al final provocarás errores y verás cómo se consume el presupuesto de errores.

flowchart LR
  U["Uptime check (3+ regiones)"] --> CR["Cloud Run: slo-demo"]
  T["Tu tráfico (bucle curl)"] --> CR
  CR -->|"logs JSON"| LOG["Cloud Logging"]
  LOG -->|"log-based metric"| MON["Cloud Monitoring"]
  CR -->|"métricas de peticiones"| MON
  MON --> SLO["SLO 99 % + alerta burn rate"]
  SLO --> MAIL["Notificación por correo"]

Antes de empezar

  • Lab 1 hecho (proyecto con facturación y alerta de presupuesto). Usa un proyecto dedicado, por ejemplo pca-lab-18.
  • Abre Cloud Shell y define las variables:
export PROJECT_ID=$(gcloud config get-value project)
export REGION=europe-southwest1
export SERVICE=slo-demo
gcloud config set run/region $REGION
  • Habilita las APIs necesarias (Cloud Run, Cloud Build y Artifact Registry para construir desde código, Monitoring y Logging):
gcloud services enable run.googleapis.com cloudbuild.googleapis.com \
  artifactregistry.googleapis.com monitoring.googleapis.com logging.googleapis.com

Coste: Cloud Run escala a cero y el tráfico del lab es mínimo; el uptime check hace unas pocas miles de ejecuciones (el primer millón por proyecto y mes es gratis, septiembre de 2026). El build consume unos minutos de Cloud Build (2.500 gratis al mes por cuenta de facturación).

Paso 1: crea la aplicación

Una aplicación mínima en Python, sin dependencias externas. Escribe logs estructurados: si imprimes una línea JSON por la salida estándar, Cloud Run la convierte en una entrada de Cloud Logging con su severity y sus campos en jsonPayload. La ruta /error devuelve un 500 para simular fallos.

mkdir -p ~/slo-demo && cd ~/slo-demo

cat > main.py <<'EOF'
import json, os, random
from http.server import BaseHTTPRequestHandler, HTTPServer

def log(severity, message, **campos):
    print(json.dumps({"severity": severity, "message": message, **campos}), flush=True)

class Handler(BaseHTTPRequestHandler):
    def do_GET(self):
        if self.path.startswith("/error"):
            log("ERROR", "Fallo simulado en el pago", ruta=self.path, pedido=random.randint(1000, 9999))
            self.send_response(500)
            body = b"error simulado\n"
        else:
            log("INFO", "Peticion atendida", ruta=self.path)
            self.send_response(200)
            body = b"ok\n"
        self.send_header("Content-Type", "text/plain")
        self.end_headers()
        self.wfile.write(body)

    def log_message(self, *args):
        pass  # evitamos el log por defecto (no estructurado)

HTTPServer(("", int(os.environ.get("PORT", 8080))), Handler).serve_forever()
EOF

cat > Dockerfile <<'EOF'
FROM python:3.12-slim
WORKDIR /app
COPY main.py .
CMD ["python", "main.py"]
EOF

Paso 2: despliega en Cloud Run

--source . sube el código, lo construye con Cloud Build usando el Dockerfile y guarda la imagen en Artifact Registry (te pedirá confirmar la creación del repositorio cloud-run-source-deploy: responde Y).

gcloud run deploy $SERVICE --source . --region $REGION \
  --allow-unauthenticated --max-instances 2

--allow-unauthenticated hace el servicio público para que el uptime check pueda llamarlo sin credenciales. Si tu proyecto pertenece a una organización con la política de domain restricted sharing, este paso fallará; en ese caso despliega sin ese parámetro y en el paso 3 marca la opción de autenticación del uptime check (las comprobaciones de disponibilidad pueden autenticarse contra Cloud Run con un agente de servicio).

Guarda la URL y pruébala:

export URL=$(gcloud run services describe $SERVICE --region $REGION --format='value(status.url)')
export HOST=${URL#https://}
curl -s $URL/
curl -s -o /dev/null -w "%{http_code}\n" $URL/error

Debes ver ok y después 500.

Paso 3: uptime check

El uptime check comprueba desde fuera (al menos 3 regiones del mundo) que el servicio responde. Es tu vista «como la ve el usuario».

gcloud monitoring uptime create "slo-demo-uptime" \
  --resource-type=uptime-url \
  --resource-labels=host=$HOST,project_id=$PROJECT_ID \
  --protocol=https --path=/ --period=5

Por consola: Monitoring > Uptime checks > Create uptime check, protocolo HTTPS, tipo de recurso URL, host del servicio, frecuencia 5 minutos.

En unos minutos, en Monitoring > Uptime checks verás las comprobaciones en verde desde varias regiones.

Paso 4: genera tráfico

El SLO necesita peticiones. Lanza en Cloud Shell un bucle con tráfico mayoritariamente bueno (déjalo corriendo en una pestaña aparte con el botón + de Cloud Shell; ahí tendrás que volver a exportar URL):

for i in $(seq 1 600); do curl -s -o /dev/null $URL/; sleep 1; done

Paso 5: crea el servicio y el SLO

Cloud Monitoring reconoce los servicios de Cloud Run como candidatos.

  1. Ve a Monitoring > SLOs y pulsa Define service.
  2. Elige el servicio de Cloud Run slo-demo y guárdalo.
  3. Dentro del servicio, pulsa Create SLO.
  4. SLI: elige Availability y evaluación Request-based (proporción de peticiones correctas sobre el total). Si la consola no ofrece Availability para tu servicio, elige Other y define un SLI de tipo request-based con la métrica run.googleapis.com/request_count, contando como buenas las que tienen response_code_class = "2xx".
  5. SLO: periodo de cumplimiento Rolling, 7 días; objetivo 99 %. Es un objetivo bajo a propósito para que el efecto del laboratorio sea visible.
  6. Guárdalo con el nombre Disponibilidad 99 %.

Piensa en el presupuesto: con un 99 % a 7 días, puedes fallar el 1 % de las peticiones del periodo.

Paso 6: alerta por burn rate

  1. En el SLO, pulsa Create SLO alert.
  2. Lookback duration: 60 minutos. Burn rate threshold: 10. Es la alerta de fast burn que recomienda la documentación (umbral 10 veces la tasa base, ventana de 1-2 horas).
  3. Notification channels: pulsa Manage notification channels, añade un canal de Email con tu correo y selecciónalo.
  4. En la documentación de la alerta escribe qué debe hacer quien la reciba (un mini runbook): «Revisar la última revisión desplegada en Cloud Run y los logs con severity ERROR; si coincide con un despliegue, revertir el tráfico a la revisión anterior».
  5. Guarda la política.

Paso 7: métrica basada en logs

Queremos contar los «fallos de pago» a partir de los logs, sin tocar el código. Una log-based metric de tipo contador convierte cada entrada que cumpla el filtro en un punto de una métrica de Monitoring.

gcloud logging metrics create fallos_pago \
  --description="Errores de pago registrados por slo-demo" \
  --log-filter='resource.type="cloud_run_revision" AND resource.labels.service_name="slo-demo" AND severity>=ERROR AND jsonPayload.message="Fallo simulado en el pago"'

La métrica solo cuenta desde su creación (no es retroactiva). Aparecerá en Metrics Explorer como logging.googleapis.com/user/fallos_pago.

Paso 8: provoca errores y observa

En otra pestaña de Cloud Shell, genera una ráfaga de errores (uno de cada dos peticiones falla, muy por encima del 1 % permitido):

for i in $(seq 1 300); do curl -s -o /dev/null $URL/error; curl -s -o /dev/null $URL/; done

Ahora observa, con paciencia (las métricas tardan unos minutos en llegar):

  • Logs Explorer (Logging > Logs Explorer), con la consulta:
resource.type="cloud_run_revision"
resource.labels.service_name="slo-demo"
severity>=ERROR

Abre una entrada y fíjate en jsonPayload.pedido y jsonPayload.ruta: son los campos del log estructurado.

  • Error Reporting: no mostrará nada, porque no hay trazas de excepción, solo mensajes. Es un buen recordatorio de que Error Reporting agrupa excepciones.
  • Metrics Explorer: logging.googleapis.com/user/fallos_pago subiendo.
  • SLOs: el cumplimiento baja y el error budget restante cae; en la gráfica de burn rate verás que supera 10.
  • Tu correo: en unos minutos debería llegar la alerta.

Comprueba que funciona

  • El uptime check aparece en verde desde al menos tres regiones.
  • El SLO muestra un presupuesto de errores consumido tras la ráfaga.
  • Has recibido un correo de la alerta de burn rate (y después, al cesar los errores, el aviso de incidente resuelto; puede tardar hasta la duración de la ventana).
  • La métrica fallos_pago refleja la ráfaga de errores.
  • Si tienes tiempo: en Observability Analytics (Logging > Observability Analytics o Log Analytics, según cómo aparezca en tu consola), actualiza el bucket _Default para usar SQL y cuenta los errores por minuto. Es opcional: la actualización del bucket no se puede deshacer.

Limpieza

Borra todo lo creado (o elimina el proyecto entero con gcloud projects delete $PROJECT_ID, la opción más segura):

# Alerta y canal (sustituye los IDs que te devuelva el list)
gcloud monitoring policies list --format="value(name,displayName)"
gcloud monitoring policies delete POLICY_NAME
gcloud beta monitoring channels list --format="value(name,displayName)"
gcloud beta monitoring channels delete CHANNEL_NAME

# Uptime check
gcloud monitoring uptime list-configs --format="value(name,displayName)"
gcloud monitoring uptime delete UPTIME_CHECK_ID

# Métrica basada en logs
gcloud logging metrics delete fallos_pago

# Servicio de Cloud Run e imágenes
gcloud run services delete $SERVICE --region $REGION
gcloud artifacts repositories delete cloud-run-source-deploy --location $REGION

El SLO y el servicio de Monitoring se borran desde Monitoring > SLOs (menú del servicio > Delete). Borra también el bucket PROJECT_ID_cloudbuild si se creó (gcloud storage rm -r gs://${PROJECT_ID}_cloudbuild).

Preguntas para pensar como arquitecto

¿Por qué alertar por burn rate del SLO en lugar de «más de 5 errores 500 por minuto»?

Un umbral fijo no tiene en cuenta el volumen: 5 errores por minuto son una catástrofe con 10 peticiones por minuto y ruido con 100.000. El burn rate mide la proporción de errores respecto al objetivo pactado y cuánto tardarías en agotar el presupuesto, así que alerta solo cuando el impacto en usuarios es real. Además, combinar fast burn (página) y slow burn (ticket) reduce la fatiga de alertas.

El uptime check estaba en verde mientras la mitad de las peticiones a /error fallaban. ¿Es un problema?

Muestra sus límites: el uptime check solo prueba una ruta y cada pocos minutos. Es útil para detectar caídas completas y problemas de red o DNS desde fuera, pero no mide la experiencia real de todos los usuarios. Por eso el SLO se basa en las peticiones reales (request-based) y el uptime check es un complemento. Para recorridos críticos (login, pago), usa synthetic monitors o comprueba rutas representativas.

¿Cuándo usarías una métrica basada en logs frente a instrumentar una métrica propia con OpenTelemetry?

La métrica basada en logs es ideal cuando la información ya está en los logs y no quieres (o no puedes) tocar el código, por ejemplo en una aplicación de terceros. Tiene un coste: dependes del volumen de logs (que pagas) y de que el formato no cambie. Si controlas el código y la métrica es importante o de alto volumen, instrumentarla con OpenTelemetry es más eficiente y robusto.

El equipo pide un SLO del 100 % para el servicio de pagos. ¿Qué respondes?

Que el 100 % es inalcanzable y contraproducente: dejaría un presupuesto de errores de cero, impediría cualquier cambio y costaría muchísimo en redundancia. Además los usuarios no lo perciben, porque su red y sus dispositivos fallan más. Se elige un SLO según lo que el usuario necesita y lo que el negocio está dispuesto a pagar (por ejemplo 99,95 %), por encima del SLA que se promete a los clientes.


Volver al módulo