Problema
En entornos serverless es frecuente necesitar un contenedor que mantenga estado durante horas o días, acepte conexiones WebSocket y ofrezca una URL permanente. Los modelos tradicionales de Cloud Run (Service, Job, Worker Pool) imponen límites de tiempo (máximo 60 min) o escalan a cero sin preservar datos en memoria. Cuando una aplicación necesita estar “always‑on”, evitar reinicios frecuentes y conservar datos temporales sin migrar a una VM completa, el patrón encaja peor con los modelos existentes y se vuelve difícil de mantener.
Causa
- Timeout de request/response – Los Services de Cloud Run cierran la petición después de 5 min por defecto (configurable hasta 60 min). Cualquier conexión que supere ese límite, como un WebSocket de larga duración, se corta automáticamente.
- Escalado a cero – Cuando no hay tráfico, la instancia se destruye y con ella el contenido de
/tmpy cualquier volumen efímero. La pérdida de estado local obliga a volver a cargar datos desde Cloud Storage o bases externas en cada arranque. - Ausencia de autoscaling para singleton – Un “instance” de Cloud Run es una única réplica que no escala. Si la carga supera la capacidad asignada, las peticiones se encolan o se agotan, lo que puede derivar en DoS involuntario.
- Almacenamiento no persistente – Aunque se pueden montar buckets vía FUSE, la latencia y la falta de bloqueo POSIX hacen que el acceso a archivos sea poco fiable para operaciones críticas. El disco efímero es volátil y se reinicia cada 1‑2 semanas.
Solución
Utilizar Cloud Run Instances como entorno serverless de un solo nodo, con una URL fija y sin límite de timeout de petición. La arquitectura típica incluye:
- Contenedor con lógica de sesión que mantiene datos en memoria o en un disco efímero mientras la instancia está viva.
- Persistencia externa (Firestore, Cloud SQL, o Cloud Storage) para cualquier estado que deba sobrevivir a reinicios programados.
- Configuración de timeout a 60 min (máximo permitido) para operaciones que necesiten más tiempo, aunque la instancia en sí no está sujeta a ese límite.
- Health checks ligeros para evitar reinicios inesperados y para que el LPU detecte fallos rápidamente.
Pasos de despliegue
- Construir la imagen con el runtime necesario (Python, Node, Go, etc.) y exponer el puerto 8080.
- Crear la instancia usando
gcloud run instances create. Se especifica la memoria y la CPU que la aplicación necesita; el precio es lineal y suele rondar los $5.70/mes para 1 GiB. - Adjuntar volúmenes opcionales:
- Un ephemeral disk (
--disk-size-gb) para archivos temporales de alta velocidad. - Un bucket Cloud Storage montado vía
gcsfusesolo para lecturas/escrituras no críticas.
- Un ephemeral disk (
- Definir variables de entorno que apunten a la base de datos externa y a la configuración de la aplicación.
Cuándo aplicar esta solución
- Sesiones interactivas largas: agentes IA, entornos de depuración remota o aplicaciones que usan WebSocket para streaming continuo.
- Bastiones temporales: acceso seguro a VPC privadas sin provisionar una VM completa.
- Entornos de pruebas aislados: sandboxes de corta duración donde el costo y la velocidad de arranque son críticos.
- Workloads con tráfico predecible y bajo: menos de 2‑3 peticiones concurrentes, donde la ausencia de autoscaling no genera cuellos de botella.
No aplicar cuando se espera alta concurrencia, picos de tráfico impredecibles o cuando la aplicación depende de un disco persistente local. En esos casos, un Service de Cloud Run, GKE o Compute Engine son opciones más seguras.
# 1. Compilar y subir la imagen
docker build -t gcr.io/$PROJECT_ID/long-lived-agent:latest .
docker push gcr.io/$PROJECT_ID/long-lived-agent:latest
# 2. Crear la instancia
gcloud run instances create long-lived-agent \
--image gcr.io/$PROJECT_ID/long-lived-agent:latest \
--region us-central1 \
--memory 1Gi \
--cpu 1 \
--max-request-timeout 60m \
--set-env-vars DB_URI=postgresql://user:pass@host/db,BUCKET_NAME=my-bucket \
--disk-size-gb 10 \
--no-allow-unauthenticated
# 3. Obtener la URL permanente
INSTANCE_URL=$(gcloud run instances describe long-lived-agent --region us-central1 --format='value(status.url)')
echo "Instancia disponible en: $INSTANCE_URL"
Verificación
- Conexión WebSocket: usa
wscato un cliente propio para abrir una sesión contra$INSTANCE_URL. La conexión debe permanecer abierta más de 60 min sin cerrarse. - Persistencia de estado: escribe un registro en
/tmp/state.txt, reinicia la instancia congcloud run instances stopy vuelve a iniciar. Verifica que el archivo desapareció y que la aplicación recuperó su estado desde la base externa. - Escalado: envía simultáneamente 3 peticiones largas (por ejemplo,
curl -N $INSTANCE_URL/stream). Observa que la tercera petición se atiende sin timeout, pero una cuarta empezará a fallar, confirmando el límite de singleton. - Costo: revisa la facturación en la consola de Cloud Billing; la instancia debe aparecer bajo “Cloud Run Instances” con el cargo esperado.
Notas adicionales
- Reinicios programados: Cloud Run Instances se reinician cada 1‑2 semanas. Programa una tarea de “warm‑up” que recargue datos críticos al iniciar.
- Límites de concurrencia: la configuración por defecto permite una sola petición concurrente. Si tu caso necesita 2‑3 peticiones simultáneas, usa la bandera
--concurrency(máximo 10) y ajusta la memoria en consecuencia. - Seguridad: habilita IAM para limitar quién puede invocar la URL. Usa
--no-allow-unauthenticatedy protege el endpoint con Cloud Endpoints o Identity-Aware Proxy. - Monitoreo: exporta métricas a Cloud Monitoring; crea alertas para uso de CPU > 80 % o para reinicios inesperados.
- Alternativas: si el requisito de “always‑on” es crítico y la carga puede crecer, considera una combinación: un Service para el tráfico público y una Instance dedicada para tareas de larga duración que no requieren alta concurrencia.