multi-clouds3gcsazuredata-lake

Un Data Lake, tres nubes: Analitica multi-cloud con DataSpoc

Michael San Martim · 2026-04-28

Tu empresa adquirio una startup que corre en GCP. Tu plataforma principal esta en AWS. El equipo de finanzas almacena exportaciones en Azure Blob Storage. Ahora necesitas unir datos de clientes de las tres nubes para un solo reporte.

DataSpoc Lens registra buckets de S3, GCS y Azure cómo un catálogo unificado. DuckDB consulta todos ellos con SQL estandar. Una consulta, tres nubes, sin movimiento de datos.

El escenario

Una empresa mediana con datos dispersos en proveedores de nube:

NubeBucketDatosEquipo
AWS S3s3://acme-productionDatos core de la app (usuarios, ordenes)Ingenieria
GCSgs://acme-acquired-appDatos del producto adquiridoEquipo de la ex-startup
Azureaz://acme-financeExportaciones financieras, facturasFinanzas

Cada equipo eligio su nube. Mover todo a un solo proveedor es un proyecto de 6 meses. Necesitas analitica ahora.

Paso 1: Registrar todos los buckets

Terminal window
# AWS S3 bucket
dataspoc-lens add-bucket s3://acme-production --name production
# Google Cloud Storage bucket
dataspoc-lens add-bucket gs://acme-acquired-app --name acquired
# Azure Blob Storage bucket
dataspoc-lens add-bucket az://acme-finance/data --name finance

Verifica que los tres estan conectados:

Terminal window
dataspoc-lens tables
production.raw.users
production.raw.orders
production.raw.products
production.curated.daily_revenue
acquired.raw.app_users
acquired.raw.app_events
acquired.raw.subscriptions
finance.raw.invoices
finance.raw.payments
finance.raw.budget_forecast

Tres nubes, un catálogo.

Paso 2: Configurar credenciales para cada proveedor

AWS S3

Usa un perfil de AWS o variables de entorno:

Terminal window
# Option 1: AWS profile (recommended)
export AWS_PROFILE=production
# Option 2: Explicit credentials
export AWS_ACCESS_KEY_ID="AKIA..."
export AWS_SECRET_ACCESS_KEY="..."
export AWS_REGION="us-east-1"

O usa roles IAM si ejecutas en EC2/ECS — no se necesitan credenciales.

Google Cloud Storage

Usa una llave de service account o credenciales predeterminadas de aplicación:

Terminal window
# Option 1: Service account key
export GOOGLE_APPLICATION_CREDENTIALS="/path/to/service-account.json"
# Option 2: Application default credentials (if on GCP or gcloud configured)
gcloud auth application-default login

Azure Blob Storage

Usa una llave de storage account o identidad administrada:

Terminal window
# Option 1: Storage account key
export AZURE_STORAGE_ACCOUNT="acmefinance"
export AZURE_STORAGE_KEY="..."
# Option 2: Connection string
export AZURE_STORAGE_CONNECTION_STRING="DefaultEndpointsProtocol=https;AccountName=..."
# Option 3: Managed identity (if running on Azure)
# No env vars needed -- uses Azure Instance Metadata

DataSpoc Lens detecta el proveedor desde el prefijo URI del bucket (s3://, gs://, az://) y usa las credenciales correspondientes.

Paso 3: Consultas cross-cloud

Ahora la parte interesante. Consulta datos de las tres nubes con una sola sentencia SQL:

-- Match acquired app users with production users by email
SELECT
p.name AS production_name,
p.email,
p.plan AS production_plan,
a.subscription_tier AS acquired_plan,
a.last_active AS acquired_last_active,
f.total_invoiced
FROM production.raw.users p
JOIN acquired.raw.app_users a
ON LOWER(p.email) = LOWER(a.email)
LEFT JOIN (
SELECT customer_email, SUM(amount) AS total_invoiced
FROM finance.raw.invoices
GROUP BY customer_email
) f ON LOWER(p.email) = LOWER(f.customer_email)
ORDER BY f.total_invoiced DESC NULLS LAST
LIMIT 20;

Esta consulta une datos de AWS con datos de GCS y datos de Azure en una sola sentencia. DuckDB maneja las lecturas cross-cloud de forma transparente.

Ejecutala:

Terminal window
dataspoc-lens query "
SELECT
p.name, p.email, p.plan,
a.subscription_tier,
f.total_invoiced
FROM production.raw.users p
JOIN acquired.raw.app_users a ON LOWER(p.email) = LOWER(a.email)
LEFT JOIN (
SELECT customer_email, SUM(amount) AS total_invoiced
FROM finance.raw.invoices
GROUP BY customer_email
) f ON LOWER(p.email) = LOWER(f.customer_email)
ORDER BY f.total_invoiced DESC NULLS LAST
LIMIT 10
"
┌─────────────────┬──────────────────────┬────────┬───────────────────┬────────────────┐
│ name │ email │ plan │ subscription_tier │ total_invoiced │
├─────────────────┼──────────────────────┼────────┼───────────────────┼────────────────┤
│ Acme Corp │ admin@acme.com │ enterprise │ premium │ 125,400.00 │
│ TechFlow Inc │ ops@techflow.io │ business │ pro │ 89,200.00 │
│ Global Systems │ data@globalsys.com │ business │ premium │ 67,800.00 │
└─────────────────┴──────────────────────┴────────┴───────────────────┴────────────────┘

Paso 4: Cache para rendimiento

Las consultas cross-cloud pueden ser lentas porque los datos se leen por red desde tres proveedores diferentes. El cache resuelve esto:

Terminal window
# Cache the most-queried tables locally
dataspoc-lens cache refresh production.raw.users
dataspoc-lens cache refresh production.raw.orders
dataspoc-lens cache refresh acquired.raw.app_users
dataspoc-lens cache refresh finance.raw.invoices

Verifica el estado del cache:

Terminal window
dataspoc-lens cache status
Table Status Size Last Refreshed
production.raw.users cached 1.2 MB 2 min ago
production.raw.orders cached 8.4 MB 2 min ago
acquired.raw.app_users cached 3.1 MB 1 min ago
finance.raw.invoices cached 5.6 MB 1 min ago
production.raw.products stale 42 KB 3 days ago
acquired.raw.app_events not cached -- --

Las consultas cacheadas se ejecutan contra archivos Parquet locales — sin llamadas de red, sin latencia cross-cloud. El mismo JOIN cross-cloud que tomaba 12 segundos sin cache corre en 200ms con cache.

Refresca caches desactualizados en un solo comando:

Terminal window
dataspoc-lens cache refresh-stale

Paso 5: Control de acceso por bucket via IAM

DataSpoc nunca implementa autenticacion. El IAM de cada proveedor de nube controla quien puede acceder a que:

AWS S3 (production):
└── IAM Policy: DataEngineers group → full access
└── IAM Policy: Analysts group → read-only
GCS (acquired):
└── IAM Policy: IntegrationTeam → read-only
└── IAM Policy: AcquiredTeam → full access
Azure (finance):
└── IAM Policy: FinanceTeam → full access
└── IAM Policy: Executives → read-only

Cuando un analista ejecuta una consulta que toca el bucket de finanzas, necesita credenciales de Azure con acceso de lectura. Si no las tiene, la consulta falla con un error de permisos. DataSpoc no intenta sortear el IAM de la nube — lo respeta.

Ejemplo practico: Customer 360 unificado

Construye una vista completa de cliente a traves de las tres nubes:

-- transforms/01_customer_360.sql
CREATE OR REPLACE TABLE gold.customer_360 AS
WITH production_data AS (
SELECT
LOWER(email) AS email,
name,
plan AS production_plan,
created_at AS production_signup,
DATE_DIFF('day', created_at, CURRENT_DATE) AS days_as_customer
FROM production.raw.users
),
acquired_data AS (
SELECT
LOWER(email) AS email,
subscription_tier AS acquired_plan,
last_active AS acquired_last_active,
feature_usage_score
FROM acquired.raw.app_users
),
finance_data AS (
SELECT
LOWER(customer_email) AS email,
SUM(amount) AS total_invoiced,
COUNT(*) AS invoice_count,
MAX(invoice_date) AS last_invoice_date
FROM finance.raw.invoices
WHERE status = 'paid'
GROUP BY 1
),
order_data AS (
SELECT
LOWER(u.email) AS email,
COUNT(*) AS total_orders,
SUM(o.amount) AS total_revenue,
MAX(o.order_date) AS last_order_date
FROM production.raw.orders o
JOIN production.raw.users u ON o.user_id = u.user_id
WHERE o.status = 'completed'
GROUP BY 1
)
SELECT
p.email,
p.name,
p.production_plan,
p.production_signup,
p.days_as_customer,
a.acquired_plan,
a.acquired_last_active,
a.feature_usage_score,
f.total_invoiced,
f.invoice_count,
o.total_orders,
o.total_revenue,
o.last_order_date,
CASE
WHEN a.email IS NOT NULL THEN 'both_products'
ELSE 'production_only'
END AS product_usage,
COALESCE(o.total_revenue, 0) + COALESCE(f.total_invoiced, 0) AS total_ltv
FROM production_data p
LEFT JOIN acquired_data a ON p.email = a.email
LEFT JOIN finance_data f ON p.email = f.email
LEFT JOIN order_data o ON p.email = o.email;

Ejecuta esta transformacion:

Terminal window
dataspoc-lens transform run --file 01_customer_360.sql

Ahora consulta la vista unificada:

Terminal window
dataspoc-lens query "
SELECT product_usage, COUNT(*) as customers, ROUND(AVG(total_ltv), 2) as avg_ltv
FROM gold.customer_360
GROUP BY product_usage
"
┌────────────────┬───────────┬──────────┐
│ product_usage │ customers │ avg_ltv │
├────────────────┼───────────┼──────────┤
│ both_products │ 2,340 │ 1,245.80 │
│ production_only│ 10,110 │ 342.50 │
└────────────────┴───────────┴──────────┘

Los clientes que usan ambos productos tienen 3.6x mayor LTV. Esa es una oportunidad de venta cruzada que solo podrias encontrar consultando a traves de las nubes.

Uso del SDK

La misma configuración multi-cloud funcióna desde Python:

from dataspoc_lens import LensClient
lens = LensClient()
# Tables from all clouds appear in one catalog
tables = lens.tables()
print(tables)
# ['production.raw.users', 'acquired.raw.app_users', 'finance.raw.invoices', ...]
# Cross-cloud query
result = lens.query("""
SELECT COUNT(DISTINCT p.email) AS overlap
FROM production.raw.users p
JOIN acquired.raw.app_users a ON LOWER(p.email) = LOWER(a.email)
""")
print(f"Users on both platforms: {result}")

Consideraciones de costo

La transferencia de datos cross-cloud cuesta dinero. Esto es lo que puedes esperar:

TransferenciaCosto por GB
S3 a internet$0.09
GCS a internet$0.12
Azure a internet$0.087
Cache local (despues de la primera lectura)$0.00

Estrategia para minimizar costos:

  1. Cachea agresivamente — la primera consulta lee de la nube, las consultas subsiguientes leen del cache local
  2. Refresca cache por programaciondataspoc-lens cache refresh-stale una vez por hora
  3. Pre-agrega — construye tablas gold que resuman datos cross-cloud, cachea solo las tablas gold
  4. Filtra temprano — clausulas WHERE con pushdown de Parquet reducen los bytes leidos

Para una empresa con 10 GB a traves de tres nubes, refrescar cache diariamente cuesta apróximadamente $3/mes en egress. Eso es mas barato que un solo credito de Snowflake.

Un catálogo, tres nubes, cero movimiento de datos. Registra tus buckets, escribe SQL y deja que DuckDB se encargue del resto.

Recomendados