StackPractices
intermediate Por Mathias Paulenko

Guía de Blob Storage: S3, GCS y Azure para ingenieros

Guía práctica de almacenamiento blob en la nube: diseño de buckets, control de acceso, políticas de ciclo de vida, subidas multiparte, URLs firmadas y optimización de costos para S3, Google Cloud Storage y Azure Blob.

Visión General

Todo equipo acaba chocando con el mismo muro: las subidas de usuarios, los backups, los logs y los archivos multimedia se empiezan a acumular en los servidores de aplicación o en blobs de base de datos, y de repente el almacenamiento es el cuello de botella — o peor, la factura. El almacenamiento blob (binary large object) es la respuesta a la que convergieron las tres grandes nubes: espacios de nombres planos de objetos inmutables detrás de una API HTTP, con precio por GB-mes y por petición.

AWS S3, Google Cloud Storage y Azure Blob Storage resuelven el mismo problema con conceptos casi idénticos pero nombres, límites y precios distintos. Esta guía recorre las decisiones que importan — diseño de buckets, control de acceso, subidas multiparte, políticas de ciclo de vida y optimización de costos — con código funcional para los tres proveedores.

Cuándo Usar

Recurre al blob storage cuando manejes:

  • Contenido generado por usuarios: avatares, documentos, vídeos, exportaciones de reportes. Cualquier cosa que un navegador suba y que tu base de datos no debería alojar.
  • Backups y archivado: volcados de base de datos, rotación de logs, archivos de cumplimiento que deben sobrevivir a la aplicación.
  • Assets estáticos a escala: archivos públicos servidos directamente o a través de una CDN, sin pasar por tu capa web.
  • Entrada de data lakes: zona de aterrizaje cruda para ETL — JSON, Parquet, CSV que los trabajos downstream leen en bloque.
  • Artefactos multi-región: objetos que deben sobrevivir a una caída de región mediante replicación.

Cuándo NO Usar

El blob storage es la herramienta equivocada cuando:

  • Necesitas un sistema de archivos: los objetos son inmutables; no hay append, ni escritura parcial, ni rename. Montar un bucket como unidad oculta esto muy mal.
  • Necesitas acceso clave-valor de baja latencia: Redis o una base de datos responden en microsegundos; un bucket, en decenas de milisegundos.
  • Los datos son estructurados y se consultan: usa una base de datos. “Escanear el bucket y parsear” no es un motor de consultas.
  • Las escrituras son diminutas y constantes: el precio por petición hace que millones de PUTs pequeños salgan sorprendentemente caros — agrúpalos en lotes.

Conceptos Clave

Los tres proveedores comparten un modelo con vocabulario distinto:

ConceptoAWS S3GCSAzure Blob
ContenedorBucketBucketContenedor
Ruta del objetoKeyNombre de objetoNombre de blob
Clase de archivo barataGlacier / Deep ArchiveArchiveNivel Archive
Clase caliente por defectoStandardStandardHot
Acceso infrecuenteStandard-IANearline / ColdlineCool / Cold
URL de acceso delegadoPresigned URLSigned URLToken SAS
PUT único máximo5 GB5 GB256 MiB (block blob)
Umbral multiparte5 MB+ recomendado5 MB+100 MiB+

Tres hechos que conviene interiorizar pronto:

  1. El espacio de nombres es plano. reports/2025/01/ es solo un prefijo de clave, no una carpeta — listar “directorios” es una consulta por prefijo y está limitada en tasa.
  2. Los objetos son inmutables. “Actualizar” un archivo significa subir una versión nueva; activa el versionado si las sobrescrituras necesitan red de seguridad.
  3. Pagas por almacenamiento Y por peticiones. Un millón de GETs pequeños puede costar más que los propios datos. Importan tanto la clase de almacenamiento como el patrón de peticiones.

Comparativa de Proveedores

S3GCSAzure Blob
Durabilidad11 nueves11 nueves11 nueves (LRS+)
Egress gratisNoNoNo — el egress es la factura real en todas
Auto-nivelIntelligent-TieringAutoclassNo integrado (reglas de ciclo de vida)
Tamaño máximo de objeto5 TB5 TB~190 TB (block blob)
Notificaciones de eventosEventBridge / SQS / LambdaPub/SubEvent Grid
Consistencia fuerteSí (desde 2020)SíSí

Hoy los tres son fuertemente consistentes: un PUT es legible de inmediato en todas partes. El dinero se va en el egress: servir 1 TB/mes desde un bucket cuesta normalmente 80–120 USD con cualquier proveedor, mientras que almacenarlo cuesta ~2–25 USD según la clase.

flowchart diagram: Subida del cliente

Implementación de Blob Storage Paso a Paso

1. Diseña la estructura de tus buckets

Los buckets tienen nombre global y son prácticamente gratis: crea más de uno. Una distribución que envejece bien:

myapp-uploads-prod        # subidas de usuarios, privado, versionado ON
myapp-reports-prod        # exportaciones generadas, ciclo de vida a archivo
myapp-static-assets       # público detrás de CDN, nombres aptos para caché
myapp-backups-prod        # volcados cifrados, retención bloqueada

Reglas que ahorran dolores después:

  • Prefija las claves con yyyy/mm/dd/ o el ID de tenant: los prefijos determinan el rendimiento de los listados y facilitan apuntar las reglas de ciclo de vida.
  • Nunca mezcles PII o secretos en un bucket que también sirva assets públicos. Sepáralos.
  • Activa Block Public Access (S3) / acceso uniforme a nivel de bucket (GCS) / deshabilita el acceso público a blobs (Azure) en todas partes, y abre solo los buckets concretos que lo necesiten.
# S3: bloquear todo acceso público a nivel de bucket
aws s3api put-public-access-block \
  --bucket myapp-uploads-prod \
  --public-access-block-configuration \
    "BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true"

2. Implementa el acceso seguro

Tres patrones de acceso cubren casi todo:

  • SDK con credenciales: servicios backend con roles IAM (nunca claves en variables de entorno).
  • URL firmada (presigned): subidas y descargas desde el navegador sin pasar por tu API.
  • Bucket policy / ACL: la peligrosa; úsala solo para contenido genuinamente público.

Flujo de subida con URL firmada: el cliente recibe de tu API una URL con caducidad y hace PUT directo al almacenamiento:

# boto3 — genera una presigned URL de PUT válida 15 minutos
import boto3

s3 = boto3.client("s3")
url = s3.generate_presigned_url(
    "put_object",
    Params={
        "Bucket": "myapp-uploads-prod",
        "Key": "uploads/2025/10/report.pdf",
        "ContentType": "application/pdf",
    },
    ExpiresIn=900,
)
# El cliente luego: requests.put(url, data=file_bytes, headers={"Content-Type": "application/pdf"})
# Azure — SAS de delegación de usuario para lectura de 1 hora
from datetime import datetime, timedelta, timezone
from azure.storage.blob import BlobServiceClient, generate_blob_sas, BlobSasPermissions

sas = generate_blob_sas(
    account_name="mystorageacct",
    container_name="uploads",
    blob_name="reports/q3.pdf",
    user_delegation_key=service_client.get_user_delegation_key(
        datetime.now(timezone.utc),
        datetime.now(timezone.utc) + timedelta(hours=1),
    ),
    permission=BlobSasPermissions(read=True),
    expiry=datetime.now(timezone.utc) + timedelta(hours=1),
)
url = f"https://mystorageacct.blob.core.windows.net/uploads/reports/q3.pdf?{sas}"

Trampas habituales: restringe los PUTs firmados por ContentType y Content-Length (si no, alguien subirá un vídeo de 10 GB a tu endpoint de avatares), mantén caducidades cortas, y prefiere políticas POST sobre PUTs simples cuando necesites límites de tamaño aplicados del lado del servidor.

Otra trampa de navegador que todos pisan una vez: las subidas directas necesitan CORS en el bucket. Un PUT firmado desde https://tudominio.com falla con un error opaco y confuso a menos que el bucket lo permita:

[
  {
    "AllowedHeaders": ["*"],
    "AllowedMethods": ["PUT", "POST"],
    "AllowedOrigins": ["https://tudominio.com"],
    "ExposeHeaders": ["ETag"],
    "MaxAgeSeconds": 3000
  }
]

Se aplica con aws s3api put-bucket-cors --bucket myapp-uploads-prod --cors-configuration file://cors.json — en GCS y Azure existe la misma idea como reglas CORS. Acota AllowedOrigins a tu dominio real; nunca * en un bucket con datos privados.

3. Sube archivos grandes con multiparte

El PUT único toca techo en 5 GB en S3/GCS y 256 MiB en Azure block blobs — y cualquier corte de red desperdicia la subida entera. La subida multiparte divide el archivo en trozos que se suben de forma independiente (y en paralelo) y se ensamblan del lado del servidor. AWS y Azure reintentan las partes fallidas; el archivo completo no se reenvía.

# boto3 — multiparte automática por encima del umbral
import boto3
from boto3.s3.transfer import TransferConfig

config = TransferConfig(
    multipart_threshold=50 * 1024 * 1024,   # 50 MB
    multipart_chunksize=25 * 1024 * 1024,   # partes de 25 MB
    max_concurrency=10,
)
s3 = boto3.client("s3")
s3.upload_file("backup-2025-10.tar.gz", "myapp-backups-prod",
               "dumps/backup-2025-10.tar.gz", Config=config)
# Azure — el SDK trocea el block blob automáticamente
from azure.storage.blob import BlobClient

blob = BlobClient.from_connection_string(
    conn_str, container_name="backups", blob_name="dumps/backup.tar.gz"
)
with open("backup.tar.gz", "rb") as f:
    blob.upload_blob(f, max_concurrency=4, overwrite=True)

Notas operativas: las subidas multiparte abortadas dejan partes huérfanas invisibles que siguen facturando almacenamiento. Añade siempre una regla de ciclo de vida que expire las subidas incompletas a los 7 días (AbortIncompleteMultipartUpload en S3). Esa sola regla ha ahorrado miles de dólares a más de un equipo.

4. Implementa políticas de ciclo de vida

Los objetos se enfrían de forma predecible: escritura intensiva los primeros días, lectura ocasional durante semanas, y después frío real. Las reglas de ciclo de vida automatizan el descenso a clases más baratas:

{
  "Rules": [{
    "ID": "reports-lifecycle",
    "Status": "Enabled",
    "Filter": {"Prefix": "reports/"},
    "Transitions": [
      {"Days": 30, "StorageClass": "STANDARD_IA"},
      {"Days": 90, "StorageClass": "GLACIER"},
      {"Days": 365, "StorageClass": "DEEP_ARCHIVE"}
    ],
    "AbortIncompleteMultipartUpload": {"DaysAfterInitiation": 7},
    "Expiration": {"Days": 2555}
  }]
}

Se aplica con aws s3api put-bucket-lifecycle-configuration --bucket myapp-reports-prod --lifecycle-configuration file://lifecycle.json. Equivalente en Azure: Management → reglas de Lifecycle management con acciones tierToCool / tierToArchive. En GCS: reglas lifecycle en el bucket.

Dos errores que salen caros:

  • Archivar objetos pequeños: Glacier tiene un mínimo de 90 días y un recargo de metadatos por objeto; los objetos de menos de ~128 KB suelen costar MÁS en clases de archivo. Filtra las reglas por tamaño o prefijo.
  • Susto de recuperación: leer un objeto archivado exige una petición de restauración (horas) más tarifas de recuperación. Archive es para desastre y cumplimiento, no para “cosas viejas que quizá mostremos a usuarios”.

5. Optimiza costos y rendimiento

PalancaCuándo compensaOjo con
Standard-IA / Nearline / CoolDatos leídos < 1 vez/mesTarifas de recuperación por GB
Intelligent-Tiering (S3) / Autoclass (GCS)Patrones de acceso desconocidosTarifa de monitorización por objeto
Niveles archivoBackups, cumplimientoHoras de restauración + tarifas
CDN delanteAssets públicos calientesDisciplina de invalidación de caché
Multiparte + paraleloArchivos > 100 MBPartes huérfanas sin regla de ciclo de vida

Para acceso desconocido o irregular, S3 Intelligent-Tiering (~0,0025 USD por 1000 objetos de monitorización) suele ganar a equivocarse de clase. Para assets públicos, pon CloudFront/Cloud CDN/Front Door delante: un acierto de caché evita la tarifa de petición y el egress, y recorta la latencia en un orden de magnitud para usuarios lejanos.

Un ejemplo con números concreta las compensaciones. Supongamos que tu app guarda 500 GB de subidas de usuarios, recibe 2 millones de PUTs/mes, sirve 3 millones de GETs/mes y empuja 800 GB de egress a través de una CDN:

ConceptoStandardCon ciclo de vida + CDN
500 GB almacenamiento~11,50 USD~5,75 USD (mitad migrada a IA a los 30 días)
2M peticiones PUT~10,00 USD~10,00 USD
3M peticiones GET~1,20 USD~0,30 USD (la CDN absorbe casi todo)
800 GB egress~96,00 USD~8,00 USD (el egress al borde de la CDN se tarifa distinto)
Total~118 USD/mes~24 USD/mes

La línea de almacenamiento es un redondeo. La factura son las peticiones y el egress — por eso “optimizar costos de blob storage” casi nunca significa “buscar una clase más barata” y casi siempre significa “poner una caché delante y dejar de martillear la API”.

6. Conecta las notificaciones de eventos

Sondear un bucket por calendario es el patrón con el que todos empiezan y del que todos se arrepienten. Los tres proveedores emiten eventos cuando se crean, borran o restauran objetos — conecta esos eventos a tu pipeline en su lugar:

S3     → EventBridge / SQS / SNS / Lambda
GCS    → notificaciones Pub/Sub en el bucket
Azure  → Event Grid → Function / Queue / Webhook

Un flujo típico: el usuario sube uploads/raw/report.csv → se dispara el evento → un worker lo valida y lo transforma → escribe uploads/processed/report.csv → marca la subida como completa en tu base de datos. El payload del evento trae bucket, clave, tamaño y etag — suficiente para despachar un trabajo sin hacer un listado.

Dos reglas prácticas: los eventos son at-least-once, así que haz los consumidores idempotentes (el patrón idempotent-consumer lo cubre), y los eventos pueden tardar segundos — no leas los metadatos del objeto esperando que la escritura esté asentada si lo necesitas al instante.

7. Replica y recupera

Para cualquier cosa que dolería perder, activa la replicación entre regiones (S3 CRR, GCS dual-region, Azure GRS/RA-GRS) en los buckets críticos. Replica asíncronamente en segundo plano: los objetos nuevos aparecen en la región secundaria en minutos. Combínala con versionado para que los borrados y las sobrescrituras no se repliquen a ciegas: la replicación conserva la copia borrada solo si configuras la replicación de marcadores de borrado a propósito.

Y luego haz la parte que todos se saltan: un simulacro de restauración cada trimestre. Elige un objeto al azar de la clase archivo, restáuralo, cronométralo y apunta el número junto a tu RTO. El simulacro casi no cuesta y es la única prueba de que el backup existe de verdad y no solo de nombre.

Lo que funciona

  • Versionado en buckets de escritura única: las sobrescrituras accidentales y el ransomware duelen menos cuando sobreviven las versiones antiguas. Combínalo con reglas que expiren versiones viejas o la factura se duplica.
  • Cifrado del lado del servidor por defecto: SSE-S3 / claves gestionadas de Google / cifrado de Azure son gratis y están siempre activos; trae tu propia clave KMS solo si el cumplimiento lo exige.
  • Tags de objetos para asignar costos: cost-center, env, data-class hacen la factura explicable y alimentan los filtros de ciclo de vida.
  • Notificaciones de eventos para pipelines: S3 → Lambda, GCS → Pub/Sub, Azure → Event Grid. Dispara el procesamiento al subir en lugar de sondear el bucket por calendario.
  • Prueba las restauraciones cada trimestre: el backup que no puedes restaurar dentro de tu RTO es una donación a tu factura de nube.
  • Nombra los archivos para máquinas, no para humanos: report-2025-10-28-a3f9.csv ordena bien, desduplica solo y sobrevive al encoding de URL. Informe Final (2) FINAL.pdf no hace nada de eso — pon el nombre legible en los metadatos del objeto, que es donde pertenece.

Errores Comunes

  1. Buckets públicos por accidente: una bucket policy con comodín o un ACL mal puesto y tu bucket de subidas sale en las noticias. Mantén Block Public Access activado; audita con aws s3control / Access Analyzer con regularidad.
  2. URLs firmadas sin restricciones: sin límites de ContentType y tamaño, el navegador de un usuario puede subir lo que sea bajo esa clave.
  3. Partes multiparte huérfanas: las subidas abortadas facturan en silencio hasta que añades AbortIncompleteMultipartUpload.
  4. Reglas de ciclo de vida sobre objetos diminutos: por debajo de ~128 KB cuestan más archivados que en Standard.
  5. Ignorar la cuenta del egress: mover 5 TB/mes fuera del almacenamiento cuesta más que guardar 200 TB. Diseña para lecturas en la misma región o con CDN.
  6. PII en los nombres de archivo: las claves de objeto se filtran a logs, URLs y analíticas de CDN — nunca pongas datos de usuario en la propia clave.
  7. Replicación sin disciplina de versionado: activar la replicación entre regiones en un bucket versionado también replica cada versión antigua — una segunda copia de cada edición que hayas hecho, facturada a la tarifa del destino. Acota las reglas de replicación por prefijo (backups/, no todo el bucket) o pagarás el doble por datos que nadie leerá jamás.
  8. Fiarte de los defaults del SDK: los reintentos y timeouts por defecto varían muchísimo entre proveedores y versiones. Una tormenta de reintentos contra un bucket limitado empeora las caídas en lugar de salvarlas — acota los reintentos, añade backoff con jitter (la receta de retry y backoff cubre la cuenta) y registra los contadores para ver las tormentas cuando ocurren.

Variantes

EscenarioForma recomendada
Subidas masivas desde navegadoresPOST firmado + directo a bucket + lectura vía CDN
Zona de aterrizaje de data lakeBuckets raw/clean/curated, Parquet, particionado por fecha
Archivo de cumplimiento (WORM)Object Lock / Bucket Lock + clase archivo
Híbrido / on-premAPI compatible S3 (MinIO, Ceph) o servicios híbridos (Storage Gateway, Azure File Sync)
Redundancia multi-nubeLa replicación entre regiones de un proveedor gana a la doble escritura en dos nubes en casi todos los casos

Lectura Adicional

Ver También

Preguntas frecuentes

¿Cuánto cuesta realmente el blob storage?

El almacenamiento es lo barato: ~0,023 USD/GB-mes en Standard (S3/GCS/Azure difieren en décimas), ~0,0125 en acceso infrecuente, ~0,00099–0,004 en archivo. La factura se esconde en las peticiones (~0,40 USD por millón de PUTs) y sobre todo en el egress (0,08–0,12 USD/GB que sale del proveedor). Un bucket que guarda 1 TB y lo sirve dos veces al mes cuesta 3-4 veces más que los mismos datos almacenados y nunca leídos.

¿URL firmada o pasar las subidas por mi API?

Firma siempre que el archivo supere ~10 MB. Pasar por tu API significa que tu capa web absorbe el doble de ancho de banda (entrada del cliente, salida al almacenamiento), necesita sus propios timeouts y reintentos, y se convierte en cuello de botella bajo carga. Las URLs firmadas dejan que el cliente hable directo con el almacenamiento a velocidad de línea; tu API solo firma una URL en microsegundos. Mantén el proxy solo cuando debas inspeccionar o transformar bytes en tránsito (análisis de virus, transcodificación).

¿S3 Standard o Intelligent-Tiering — cuándo compensa el auto-nivel?

Intelligent-Tiering vigila el acceso y mueve objetos entre niveles por ~0,0025 USD por 1000 objetos/mes. Compensa cuando los patrones son desconocidos o irregulares (contenido de usuarios, unidades compartidas): si no, pagarías tarifas de recuperación IA en objetos que de repente se ponen calientes. Sáltalo en objetos de menos de 128 KB (de todos modos no se nivela) y en patrones predecibles, donde una regla de ciclo de vida fija es más simple y gratis.

¿Qué resuelve realmente la subida multiparte?

Dos cosas: los límites de tamaño (5 GB por PUT único en S3/GCS, 256 MiB en Azure) y la recuperación ante fallos (una parte mala se reintenta en lugar de reiniciar 50 GB). Orientación: multiparte por encima de 50 MB, partes de 25–100 MB como equilibrio entre paralelismo y overhead por parte. Limpia siempre las subidas abortadas con una regla de ciclo de vida: las partes huérfanas siguen facturando.

¿Versionado del bucket o borrado por ciclo de vida, qué va primero?

El versionado protege de sobrescrituras y borrados; el ciclo de vida protege del acaparamiento. Quieres ambos, configurados juntos: NoncurrentVersionTransitions mueve versiones viejas a clases baratas y ExpiredObjectDeleteMarker elimina los marcadores de borrado. Versionado sin expiración convierte cada edición en almacenamiento duplicado permanente: una sorpresa de factura típica a los seis meses.

¿Cómo sirvo archivos públicos desde un bucket de forma segura?

No hagas público el bucket: pon una CDN delante (CloudFront con OAC, Cloud CDN, Front Door) y mantén el bucket privado para que solo la CDN pueda leer. La lectura pública debería ser una excepción explícita y revisada para assets pensados para ser legibles por cualquiera, nunca el defecto. Las cookies/URLs firmadas en la CDN te dan control de acceso por usuario incluso en una distribución "pública".