Lab práctico · Semana 5: Almacenamiento y bases de datos

Cloud Storage a fondo — clases, ciclo de vida, versionado, retención y URLs firmadas

⏱ 60-90 minDificultad: básicaApartados: 2.21.3

Qué vas a construir

Un bucket regional en Madrid con acceso uniforme y prevención de acceso público, en el que practicarás las clases de almacenamiento, el versionado, las reglas de ciclo de vida, el soft delete, una política de retención (sin bloquear) y una URL firmada que permite descargar un objeto sin cuenta de Google. Al final crearás un segundo bucket con Autoclass para comparar.

flowchart LR
  U["Tú (Cloud Shell)"] -->|"gcloud storage"| B["Bucket regional europe-southwest1"]
  B --> V["Versionado + soft delete"]
  B --> L["Reglas de ciclo de vida"]
  B --> R["Política de retención (sin bloquear)"]
  SA["Cuenta de servicio firmadora"] -->|"firma"| URL["URL firmada (10 min)"]
  X["Usuario externo sin cuenta"] -->|"curl"| URL --> B

Antes de empezar

  • Proyecto dedicado con facturación (el de los labs anteriores sirve) y Cloud Shell abierto.
  • Rol de propietario (Owner) en el proyecto.

Define las variables (el nombre del bucket debe ser único en todo Google Cloud, por eso lleva el ID del proyecto):

export PROJECT_ID=$(gcloud config get-value project)
export REGION=europe-southwest1
export BUCKET=gs://${PROJECT_ID}-lab10
export SA=firmador-lab10
gcloud services enable storage.googleapis.com iamcredentials.googleapis.com

iamcredentials.googleapis.com hace falta para firmar URLs suplantando una cuenta de servicio.

Paso 1: crear el bucket con buenas prácticas de seguridad

Creas un bucket regional (los datos se quedan en Madrid, replicados entre zonas), con acceso uniforme a nivel de bucket (solo IAM, sin ACL) y prevención de acceso público (nadie podrá hacerlo público por error).

Por consola: Cloud Storage → Buckets → Crear, tipo de ubicación Region europe-southwest1, clase Standard, marca «Aplicar la prevención del acceso público» y control de acceso Uniforme.

Con gcloud:

gcloud storage buckets create $BUCKET \
  --location=$REGION \
  --default-storage-class=STANDARD \
  --uniform-bucket-level-access \
  --public-access-prevention

gcloud storage buckets describe $BUCKET \
  --format="yaml(location,default_storage_class,uniform_bucket_level_access,public_access_prevention,soft_delete_policy)"

Fíjate en soft_delete_policy: el soft delete viene activado por defecto con 7 días (604800 s).

Paso 2: clases de almacenamiento por objeto

La clase es una propiedad del objeto, no solo del bucket. Sube un fichero con la clase por defecto y otro directamente en Coldline:

echo "Informe trimestral v1" > informe.txt
echo "Copia de seguridad antigua" > backup-2025.txt

gcloud storage cp informe.txt $BUCKET/
gcloud storage cp backup-2025.txt $BUCKET/ --storage-class=COLDLINE

gcloud storage ls -L $BUCKET/** | grep -E "gs://|Storage Class"

Ahora cambia la clase de un objeto existente (esto reescribe el objeto):

gcloud storage objects update $BUCKET/backup-2025.txt --storage-class=ARCHIVE
gcloud storage objects describe $BUCKET/backup-2025.txt --format="value(storage_class)"

Paso 3: versionado de objetos

Activa el versionado, sobrescribe el informe y recupera la versión anterior:

gcloud storage buckets update $BUCKET --versioning

echo "Informe trimestral v2 (con un error)" > informe.txt
gcloud storage cp informe.txt $BUCKET/informe.txt

gcloud storage ls --all-versions $BUCKET/informe.txt

Verás dos líneas con el formato gs://…/informe.txt#GENERACIÓN. La de generación más baja es la versión no actual (v1). Restaúrala copiándola encima de la actual (sustituye GEN_V1 por su número; las comillas evitan que la shell interprete #):

gcloud storage cp "$BUCKET/informe.txt#GEN_V1" $BUCKET/informe.txt
gcloud storage cat $BUCKET/informe.txt
gcloud storage ls --all-versions $BUCKET/informe.txt

Ahora el contenido vuelve a ser v1 y hay tres versiones: cada versión ocupa y cuesta espacio. Por eso el siguiente paso limita cuántas se conservan.

Paso 4: reglas de ciclo de vida

Crea un fichero con tres reglas habituales: bajar a Nearline a los 30 días, conservar como máximo 2 versiones no actuales y borrar las no actuales a los 7 días, y abortar subidas multiparte incompletas.

cat > lifecycle.json <<'EOF'
{
  "lifecycle": {
    "rule": [
      { "action": { "type": "SetStorageClass", "storageClass": "NEARLINE" },
        "condition": { "age": 30, "matchesStorageClass": ["STANDARD"] } },
      { "action": { "type": "Delete" },
        "condition": { "isLive": false, "numNewerVersions": 2 } },
      { "action": { "type": "Delete" },
        "condition": { "isLive": false, "daysSinceNoncurrentTime": 7 } },
      { "action": { "type": "AbortIncompleteMultipartUpload" },
        "condition": { "age": 7 } }
    ]
  }
}
EOF

gcloud storage buckets update $BUCKET --lifecycle-file=lifecycle.json
gcloud storage buckets describe $BUCKET --format="default(lifecycle_config)"

Las reglas pueden tardar hasta 24 horas en aplicarse, así que no verás efectos inmediatos: lo importante es entender la configuración. Consola: pestaña Lifecycle del bucket.

Paso 5: soft delete

Borra un objeto y restáuralo desde el soft delete. Como hay versionado, primero borra también sus versiones no actuales para ver el efecto del soft delete sobre algo «borrado del todo»:

gcloud storage rm --all-versions $BUCKET/backup-2025.txt
gcloud storage ls --all-versions $BUCKET/backup-2025.txt   # ya no aparece

gcloud storage ls $BUCKET/backup-2025.txt --soft-deleted
gcloud storage restore $BUCKET/backup-2025.txt
gcloud storage ls $BUCKET/

El objeto vuelve. El soft delete protege incluso cuando no hay versionado o cuando se borran todas las versiones, durante 7 días por defecto.

Paso 6: política de retención (sin bloquear)

Pon una retención de 60 segundos: ningún objeto se puede borrar ni sobrescribir hasta que tenga 60 s de antigüedad.

gcloud storage buckets update $BUCKET --retention-period=60s
echo "Registro contable" > registro.txt
gcloud storage cp registro.txt $BUCKET/
gcloud storage rm --all-versions $BUCKET/registro.txt     # debe fallar: el objeto está retenido

Usamos --all-versions porque el bucket tiene versionado: un rm normal solo convertiría la versión actual en no actual (eso sí está permitido), pero la versión seguiría existiendo y protegida. Lo que la retención impide es eliminar la versión.

Espera un minuto y repite el rm: ahora funciona. Después quita la política:

gcloud storage rm --all-versions $BUCKET/registro.txt
gcloud storage buckets update $BUCKET --clear-retention-period

Paso 7: URL firmada para un usuario sin cuenta

Un proveedor externo sin cuenta de Google debe descargar informe.txt durante 10 minutos. Creas una cuenta de servicio que solo puede leer el bucket, te das permiso para suplantarla y firmas la URL con ella.

gcloud iam service-accounts create $SA --display-name="Firmador de URLs lab 10"
export SA_EMAIL=${SA}@${PROJECT_ID}.iam.gserviceaccount.com

# La cuenta de servicio puede leer objetos del bucket
gcloud storage buckets add-iam-policy-binding $BUCKET \
  --member=serviceAccount:$SA_EMAIL --role=roles/storage.objectViewer

# Tú puedes firmar en su nombre (el rol Owner no incluye este permiso)
gcloud iam service-accounts add-iam-policy-binding $SA_EMAIL \
  --member=user:$(gcloud config get-value account) \
  --role=roles/iam.serviceAccountTokenCreator

Los permisos de IAM pueden tardar uno o dos minutos en propagarse. Después:

gcloud storage sign-url $BUCKET/informe.txt \
  --duration=10m \
  --impersonate-service-account=$SA_EMAIL

Copia el valor de signed_url y pruébalo sin credenciales:

curl -s "PEGA_AQUÍ_LA_URL_FIRMADA"

Verás el contenido del informe. Prueba también a abrir https://storage.googleapis.com/${PROJECT_ID}-lab10/informe.txt sin firma: obtendrás un error de acceso, porque el bucket no es público (y no puede serlo).

Paso 8: Autoclass (comparación)

Crea un segundo bucket con Autoclass y clase terminal Archive:

gcloud storage buckets create ${BUCKET}-auto \
  --location=$REGION \
  --uniform-bucket-level-access \
  --enable-autoclass \
  --autoclass-terminal-storage-class=ARCHIVE

gcloud storage buckets describe ${BUCKET}-auto --format="yaml(autoclass)"

Con Autoclass no puedes añadir reglas de ciclo de vida con SetStorageClass: Google mueve los objetos según su uso real y no cobra tarifas de recuperación ni de borrado anticipado.

Comprueba que funciona

  • describe del bucket muestra ubicación EUROPE-SOUTHWEST1, acceso uniforme activado, prevención de acceso público enforced, versionado activo y las cuatro reglas de ciclo de vida.
  • informe.txt tiene varias versiones y su contenido actual es el de v1.
  • backup-2025.txt se restauró desde el soft delete y está en clase ARCHIVE.
  • La URL firmada descarga el fichero con curl; la URL sin firmar devuelve un error de acceso.
  • El bucket -auto muestra autoclass.enabled: true y terminalStorageClass: ARCHIVE.

Limpieza

gcloud storage rm --recursive --all-versions $BUCKET/** ${BUCKET}-auto/** 2>/dev/null
gcloud storage buckets delete $BUCKET ${BUCKET}-auto
gcloud iam service-accounts delete $SA_EMAIL --quiet
rm -f informe.txt backup-2025.txt registro.txt lifecycle.json

Los objetos borrados quedan 7 días en soft delete y se facturan, pero con unos bytes el coste es nulo en la práctica. Si el buckets delete falla porque aún hay objetos, repite el rm y comprueba con gcloud storage ls --all-versions $BUCKET/** que el bucket está vacío.

Preguntas para pensar como arquitecto

Un equipo guarda logs que se consultan mucho la primera semana y casi nunca después, pero deben conservarse 5 años. ¿Autoclass o ciclo de vida?

Ciclo de vida: el patrón es conocido. Reglas como Nearline a los 30 días, Archive a los 365 y borrado a los 1.825 días son más baratas que Autoclass (que cobra una tarifa de gestión por objeto) y dejan explícita la política de retención. Si además es obligatorio que no se borren antes de 5 años, añade una política de retención y valora bloquearla.

¿Por qué versionado y soft delete no sustituyen a una política de retención bloqueada?

Porque un usuario con permisos suficientes puede borrar versiones no actuales, desactivar el versionado o reducir el soft delete. Solo la retención bloqueada (Bucket Lock) u Object Retention Lock en modo Locked garantiza la inmutabilidad frente a cualquier usuario, incluido un administrador, que es lo que exige una normativa WORM.

Un socio necesita subir ficheros a tu bucket cada día desde su sistema, y sí tiene una identidad en Google Cloud. ¿Le das URLs firmadas?

No es lo ideal. Si el socio tiene identidad (o puede usar Workload Identity Federation desde su nube), concede IAM de mínimo privilegio (roles/storage.objectCreator en el bucket o una carpeta gestionada). Las URLs firmadas son para accesos puntuales de quien no tiene identidad, porque cualquiera que tenga la URL puede usarla mientras sea válida.

La empresa quiere que el bucket sobreviva a la caída de una región con RPO de 15 minutos y datos solo en la UE. ¿Qué configurarías?

Un bucket dual-región con dos regiones europeas (predefinida o configurable) y turbo replication, que garantiza replicar el 100 % de los objetos nuevos en 15 minutos. Multirregión EU también mantiene los datos en la UE, pero no ofrece turbo replication ni te deja elegir las regiones.


Volver al módulo