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

  1. Arquitectura fragmentada de los emuladores oficiales – Cada servicio expone su propia CLI y puerto, obligando a lanzar varios contenedores o procesos.
  2. 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.
  3. 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.
  4. 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.
  5. 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

  1. Descargar la imagen

    docker pull floci/floci-gcp:latest
    
  2. 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
    
  3. 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
    
  4. 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
    
  5. 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

  1. Comprobar conectividad

    curl -i http://localhost:4588/storage/v1/b
    

    Debería devolver un JSON con la lista de buckets (vacía al inicio).

  2. 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.

  3. 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-project
    

    Verificar con gcloud pubsub subscriptions pull ... que el mensaje se recibió.

  4. 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.

  5. 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_CREDENTIALS apunte 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 memory el contenedor consume RAM proporcional al número de objetos almacenados. Para pruebas que involucren cientos de megabytes, prefiera el modo persistent.
  • Compatibilidad con Terraform 1.9+ – La suite de pruebas incluye el provider google versión 5.x; versiones anteriores pueden requerir la variable TF_LOG=DEBUG para 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=info para ver si la cabecera :protocol está 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.