Problema
Desarrollar y testear aplicaciones que consumen varios servicios de Google Cloud suele requerir que cada API se ejecute en su propio proceso o binario. Los emuladores oficiales de Cloud Storage, Pub/Sub, Firestore y Datastore se distribuyen como ejecutables independientes, cada uno escuchando en puertos diferentes. En entornos CI o en máquinas de desarrollo con recursos limitados, mantener varios procesos activos genera complejidad: hay que coordinar puertos, manejar variables de entorno distintas y, en el peor de los casos, algunos servicios (por ejemplo, Secret Manager o Managed Kafka) no tienen emulador oficial. El patrón recurrente es la fragmentación del entorno local, que dificulta la reproducibilidad de pruebas y ralentiza los pipelines.
Causa
- Arquitectura fragmentada de los emuladores oficiales – Cada servicio expone su propia CLI y puerto, obligando a lanzar varios contenedores o procesos.
- Falta de emuladores para servicios auxiliares – Secret Manager, IAM y soluciones de streaming no están cubiertas oficialmente, lo que lleva a usar mocks ad‑hoc o a omitir pruebas.
- Incompatibilidad de versiones entre SDKs y los binarios – Los SDK de Java, Python, Node y Go esperan ciertos comportamientos que pueden variar entre versiones del emulador, provocando errores sutiles en consultas o en la generación de firmas V4.
- Aislamiento de proyectos insuficiente – Cuando varios proyectos comparten la misma instancia del emulador, los recursos se mezclan y las pruebas pierden aislamiento, generando falsos positivos o negativos.
- Gestión de persistencia – Los emuladores que guardan datos en memoria pierden estado entre ejecuciones, lo que complica pruebas de migración o de versiones de objetos.
Solución
Utilizar un contenedor Docker que consolide varios servicios de GCP en un único puerto mediante HTTP/2 ALPN. El contenedor expone Cloud Storage (REST XML/JSON), Pub/Sub (gRPC), Firestore, Datastore, Secret Manager, IAM y un mock de Managed Kafka. La arquitectura se basa en:
- Negociación ALPN – gRPC y REST comparten el mismo puerto (ej. 4588). El cliente decide el protocolo mediante la cabecera
:protocol. - Aislamiento por ruta – Cada recurso se direcciona bajo
/projects/{project}/..., lo que permite ejecutar varios proyectos simultáneamente dentro de la misma instancia. - Modos de almacenamiento – Memoria (rápido, volátil), persistente (montaje de volumen), híbrido (memoria + snapshot) y WAL (write‑ahead log) para casos de uso de CI donde se necesita reproducir estado entre etapas.
- Compatibilidad SDK – El contenedor incluye una suite de pruebas que cubre los principales SDKs y el proveedor de Terraform/OpenTofu, garantizando que las llamadas habituales (creación de buckets, publicación de mensajes, consultas Firestore) se comporten como en la nube real.
Pasos de implementación
-
Descargar la imagen
docker pull floci/floci-gcp:latest -
Definir variables de entorno – Todas apuntan al mismo host y puerto.
export PUBSUB_EMULATOR_HOST=localhost:4588 export FIRESTORE_EMULATOR_HOST=localhost:4588 export STORAGE_EMULATOR_HOST=http://localhost:4588 export SECRET_MANAGER_EMULATOR_HOST=localhost:4588 export GOOGLE_CLOUD_PROJECT=local-dev -
Ejecutar el contenedor – En modo persistente se monta un volumen para que los datos sobrevivan a reinicios.
docker run -d \ -p 4588:4588 \ -e GOOGLE_CLOUD_PROJECT=local-dev \ -v $(pwd)/gcp-data:/data \ floci/floci-gcp:latest -
Integrar en CI – Añadir el mismo bloque a la fase de setup del pipeline. En GitHub Actions, por ejemplo:
- name: Start GCP emulator run: | docker run -d -p 4588:4588 -e GOOGLE_CLOUD_PROJECT=ci-test floci/floci-gcp:latest -
Configurar SDKs – Los SDK de Go, Java, Python y Node detectan automáticamente las variables de entorno y redirigen las peticiones al contenedor.
Cuándo aplicar esta solución
- Desarrollo local con múltiples servicios – Cuando una aplicación necesita Cloud Storage, Pub/Sub y Firestore simultáneamente y no se quiere lanzar tres contenedores diferentes.
- Pipelines CI/CD que ejecutan pruebas de integración – Ideal para pipelines que requieren estado persistente entre etapas (por ejemplo, crear un bucket, subir objetos y luego validar versiones).
- Pruebas de Terraform/OpenTofu – El proveedor de Google Cloud puede apuntar al emulador sin cambios en la configuración, lo que permite validar módulos sin tocar la nube real.
- Escenarios donde los emuladores oficiales faltan – Si se necesita Secret Manager o IAM en pruebas, el contenedor los provee sin necesidad de mocks externos.
No aplicar cuando se necesita alta fidelidad de latencia de red o pruebas de rendimiento a gran escala; el emulador está pensado para funcionalidad, no para benchmarking de throughput.
Código
# Docker Compose snippet for a reproducible dev environment
services:
gcp-emulator:
image: floci/floci-gcp:latest
ports:
- "4588:4588"
environment:
GOOGLE_CLOUD_PROJECT: dev-project
volumes:
- ./gcp-data:/data
Verificación
-
Comprobar conectividad
curl -i http://localhost:4588/storage/v1/bDebería devolver un JSON con la lista de buckets (vacía al inicio).
-
Crear un bucket con gsutil
gsutil mb -p dev-project -l us-central1 gs://test-bucket/Luego listar:
gsutil ls -p dev-project. El bucket debe aparecer. -
Publicar un mensaje en Pub/Sub
gcloud pubsub topics create test-topic --project=dev-project gcloud pubsub topics publish test-topic --message="hello" --project=dev-projectVerificar con
gcloud pubsub subscriptions pull ...que el mensaje se recibió. -
Ejecutar una consulta Firestore (Python SDK)
from google.cloud import firestore db = firestore.Client(project="dev-project") doc_ref = db.collection("users").document("alice") doc_ref.set({"age": 30}) print(db.collection("users").where("age", ">", 20).stream())La salida debe listar el documento creado.
-
Terraform plan
terraform init terraform plan -var='project=dev-project'El plan debe resolverse sin errores de autenticación ni de recursos inexistentes.
Notas adicionales
- Gestión de versiones de firma V4 – Cuando se usan URLs firmadas, el contenedor genera claves RSA‑2048 bajo el endpoint IAM. Asegúrese de que la variable
GOOGLE_APPLICATION_CREDENTIALSapunte a un JSON vacío o a un archivo con la clave simulada; de lo contrario, el SDK intentará buscar credenciales reales. - Límites de memoria – En modo
memoryel contenedor consume RAM proporcional al número de objetos almacenados. Para pruebas que involucren cientos de megabytes, prefiera el modopersistent. - Compatibilidad con Terraform 1.9+ – La suite de pruebas incluye el provider
googleversión 5.x; versiones anteriores pueden requerir la variableTF_LOG=DEBUGpara diagnosticar errores de endpoint. - Depuración de gRPC – Si el cliente falla al negociar ALPN, habilite el registro de tráfico con
GRPC_GO_LOG_SEVERITY_LEVEL=infopara ver si la cabecera:protocolestá presente. - Actualizaciones de la imagen – La comunidad publica versiones semánticas (
0.1.0,0.2.0, …). Revise el changelog antes de actualizar en CI para evitar rupturas inesperadas en la API de Secret Manager.