De tot el que AlpinaShop té al seu servidor físic, la base de dades tienda és el més valuós i el més fràgil. Conté les comandes, els clients, l'estoc i els preus. Corre sobre un únic PostgreSQL que la Marta va instal·lar fa anys, sense rèplica, sense failover, amb un pg_dump nocturn que ningú no ha provat mai de restaurar i amb l'actualització de versió major pendent des de fa dos cicles perquè "no és bon moment". Si aquell disc falla un dissabte d'octubre, AlpinaShop no ven.
Cloud SQL és el servei de bases de dades relacionals gestionades de Google Cloud: PostgreSQL, MySQL i SQL Server amb còpies automàtiques, alta disponibilitat regional, rèpliques de lectura, pedaços gestionats i recuperació a un punt en el temps. En aquesta lliçó migrarem la base de dades tienda a la instància alpinashop-pedidos, connectarem l'aplicació Flask de manera segura i deixarem a la Lucía una rèplica on llançar els seus informes sense castigar la botiga.
Contingut
- Què deixa de fer la Marta: base de dades gestionada davant d'autogestionada
- Motors, edicions i dimensionament
- Crear la instància
alpinashop-pedidos - Bases de dades, usuaris i contrasenyes
- Alta disponibilitat regional i failover
- Rèpliques de lectura per als informes de la Lucía
- Còpies de seguretat, retenció i recuperació a un punt en el temps
- Connectivitat: IP pública, IP privada i l'Auth Proxy
- Connectar l'aplicació Flask
- Migrar les dades actuals
- Manteniment programat i finestres
- Escalat vertical i límits
- Quan Cloud SQL es queda curt: AlloyDB i Spanner
- Què deixa de fer la Marta: base de dades gestionada davant d'autogestionada
La manera més honesta d'explicar el valor de Cloud SQL és enumerar tasques i veure qui les fa.
| Tasca | PostgreSQL de la Marta avui | Cloud SQL |
|---|---|---|
| Instal·lar i configurar el motor | La Marta, a mà | Google, en minuts |
| Aplicar pedaços de seguretat del motor | La Marta, quan troba un forat | Google, a la finestra de manteniment |
| Apedaçar el sistema operatiu | La Marta | Google (no hi ha SO accessible) |
| Còpies de seguretat | Script pg_dump nocturn |
Automàtiques, incrementals, gestionades |
| Verificar que les còpies serveixen | Ningú | Restauració provada amb una comanda |
| Recuperar a un instant concret | Impossible | PITR amb WAL, al segon |
| Failover si cau el servidor | Manual, hores | Automàtic, de l'ordre d'un minut |
| Rèpliques de lectura | Configuració manual complexa | Una comanda |
| Xifratge en repòs i en trànsit | Manual | Per defecte |
| Mètriques i alertes | El que la Marta hagi muntat | Cloud Monitoring integrat |
| Escalar CPU o memòria | Comprar maquinari | Canviar el tipus i reiniciar |
| Actualitzar de versió major | Projecte de setmanes | Operació assistida |
Ajust fi del postgresql.conf |
Control total | Només flags permesos |
Extensions i superuser |
Control total | Llista d'extensions suportades, sense superusuari real |
Les dues últimes files són el preu a pagar: perds accés de superusuari i control total del motor. A Cloud SQL no hi ha SSH a la màquina, no pots instal·lar qualsevol extensió ni tocar tots els paràmetres. Per a la immensa majoria d'aplicacions —inclosa AlpinaShop— és un intercanvi excel·lent. Si el teu producte depèn d'una extensió exòtica o d'un pg_hba.conf recargolat, hauràs de comprovar la compatibilitat abans o quedar-te a Compute Engine.
Hi ha també un canvi de cost que convé dir sense adorns: una VM amb PostgreSQL instal·lat és més barata que una instància Cloud SQL equivalent. El que compres amb aquesta diferència és el temps de la Marta i l'eliminació d'un risc que avui no està cobert. Si poses preu a una tarda d'indisponibilitat en plena campanya, el compte surt sol.
- Motors, edicions i dimensionament
Motors disponibles:
| Motor | Versions habituals | Notes |
|---|---|---|
| PostgreSQL | 13 a 17 | El més complet a Cloud SQL; extensions populars suportades (pg_stat_statements, postgis, pgvector) |
| MySQL | 8.0, 8.4 | Molt utilitzat; rèpliques i grups de lectura madurs |
| SQL Server | 2019, 2022 (Express a Enterprise) | Llicència inclosa al preu per hora; el més car |
AlpinaShop ja utilitza PostgreSQL, així que l'elecció és evident: PostgreSQL 16, mateixa família que la seva instal·lació actual, sense canvis a l'aplicació.
Edicions. Cloud SQL s'ofereix en dues edicions que convé distingir:
| Enterprise | Enterprise Plus | |
|---|---|---|
| Rendiment | Estàndard | Més gran (memòria cau de dades, màquines més potents) |
| Failover | De l'ordre d'1 minut | Molt inferior (segons) |
| Manteniment | Amb reinici breu | Gairebé sense interrupció |
| Retenció de còpies | Fins a 365 dies | Més gran, amb més granularitat |
| Cost | Menor | Notablement més gran |
Per a l'arrencada d'AlpinaShop, Enterprise és suficient. Si en el futur la botiga no tolera ni un minut de tall, Enterprise Plus és la via d'escapament sense canviar de servei.
Dimensionament. Es tria un tipus de màquina igual que a Compute Engine. Criteris pràctics per a una base de dades:
- La memòria és el primer. L'objectiu és que el conjunt de dades "calent" (índexs i taules consultades amb freqüència) càpiga a la RAM. Una base de dades que fa lectures de disc constantment va lenta per molta CPU que tingui.
- El disc determina les IOPS. Com vam veure a 02-01, en discos persistents el rendiment creix amb la mida. Un disc de 10 GB té molt poques IOPS encara que les teves dades ocupin 8 GB.
- Activa el creixement automàtic d'emmagatzematge. Que la base de dades s'aturi per disc ple és un incident evitable.
- Comença modest i escala. Canviar el tipus de màquina és una operació de minuts amb reinici.
La base de dades tienda d'AlpinaShop ocupa uns 12 GB. Triem db-custom-2-7680 (2 vCPU, 7,5 GB de RAM) amb 50 GB de disc SSD i creixement automàtic: espai de sobres perquè el disc no limiti les IOPS i RAM suficient per cachejar el conjunt actiu.
- Crear la instància
alpinashop-pedidos
alpinashop-pedidosgcloud services enable sqladmin.googleapis.com
gcloud sql instances create alpinashop-pedidos \
--project=alpinashop-prod \
--database-version=POSTGRES_16 \
--edition=enterprise \
--tier=db-custom-2-7680 \
--region=europe-west1 \
--storage-type=SSD \
--storage-size=50GB \
--storage-auto-increase \
--availability-type=REGIONAL \
--backup-start-time=03:00 \
--retained-backups-count=14 \
--enable-point-in-time-recovery \
--retained-transaction-log-days=7 \
--maintenance-window-day=SUN \
--maintenance-window-hour=4 \
--maintenance-release-channel=production \
--database-flags=max_connections=200,log_min_duration_statement=1000 \
--labels=entorno=prod,equipo=plataforma,centro-coste=tienda,aplicacion=catalogoAquesta comanda concentra gairebé totes les decisions de la lliçó, així que la desglossem:
--region=europe-west1(no zona): Cloud SQL és un servei regional. La instància principal viu en una zona, però l'elecció s'expressa a escala de regió.--availability-type=REGIONALactiva l'alta disponibilitat: una instància en espera en una altra zona amb replicació síncrona. És el flag que converteix un punt únic de fallada en una arquitectura tolerant.--storage-auto-increase: el disc creix sol quan s'omple. Mai no decreix, així que continua vigilant-ne el creixement.--backup-start-time=03:00(hora UTC): còpia diària en horari de baix trànsit.--enable-point-in-time-recovery+--retained-transaction-log-days=7: desa els WAL per poder restaurar a qualsevol instant dels últims 7 dies.--maintenance-window-*: Google aplicarà les actualitzacions els diumenges a les 4:00 UTC, no un dimarts a les 11:00.--maintenance-release-channel=production: rep les versions ja madures, no les més recents.--database-flags: els paràmetres del motor s'ajusten aquí.log_min_duration_statement=1000registra tota consulta que trigui més d'un segon, que és la millor eina de diagnòstic de rendiment que existeix i costa zero configurar-la.
La creació triga uns minuts. En acabar:
gcloud sql instances describe alpinashop-pedidos \
--format="table(name, state, databaseVersion, settings.tier, settings.availabilityType, ipAddresses[].ipAddress)"
- Bases de dades, usuaris i contrasenyes
Una instància és el servidor; a dins hi viuen les bases de dades i els usuaris.
# Crear la base de dades 'tienda'
gcloud sql databases create tienda \
--instance=alpinashop-pedidos \
--charset=UTF8 \
--collation=es_ES.UTF8
# Fixar la contrasenya de l'usuari administratiu 'postgres'
gcloud sql users set-password postgres \
--instance=alpinashop-pedidos \
--prompt-for-password
# Usuari de l'aplicacio, amb permisos limitats
gcloud sql users create app_catalogo \
--instance=alpinashop-pedidos \
--prompt-for-password
# Usuari de nomes lectura per als informes de la Lucia
gcloud sql users create informes_lectura \
--instance=alpinashop-pedidos \
--prompt-for-passwordUtilitza sempre --prompt-for-password: escriure la contrasenya a la línia de comandes la deixa a l'historial de bash i als registres d'auditoria.
Crear l'usuari no li dóna permisos dins de la base: això es fa amb SQL. Connecta't (apartat 8) i executa:
-- Esquema propi de l'aplicacio, en lloc de fer servir 'public'
CREATE SCHEMA IF NOT EXISTS tienda AUTHORIZATION app_catalogo;
-- L'aplicacio: lectura i escriptura de dades, res de DDL destructiu
GRANT USAGE ON SCHEMA tienda TO app_catalogo;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA tienda TO app_catalogo;
ALTER DEFAULT PRIVILEGES IN SCHEMA tienda
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO app_catalogo;
-- La Lucia: nomes lectura, i nomes sobre aquest esquema
GRANT USAGE ON SCHEMA tienda TO informes_lectura;
GRANT SELECT ON ALL TABLES IN SCHEMA tienda TO informes_lectura;
ALTER DEFAULT PRIVILEGES IN SCHEMA tienda
GRANT SELECT ON TABLES TO informes_lectura;
-- Impedir que qualsevol creï objectes a l'esquema public
REVOKE CREATE ON SCHEMA public FROM PUBLIC;La clàusula ALTER DEFAULT PRIVILEGES és la que sol faltar: sense ella, els permisos s'apliquen només a les taules que existien en aquell moment, i qualsevol taula creada després queda inaccessible per a l'aplicació. És una font clàssica d'errors després d'un desplegament.
Cloud SQL també admet autenticació IAM: en lloc de contrasenyes, els usuaris i els comptes de servei s'autentiquen amb la seva identitat de Google Cloud i un token de vida curta. És l'opció recomanada perquè elimina les contrasenyes del sistema. Ho deixem apuntat aquí i es desenvolupa a 03-04.
- Alta disponibilitat regional i failover
Amb --availability-type=REGIONAL, Cloud SQL manté una instància en espera en una altra zona d'europe-west1, amb replicació síncrona del disc: cada escriptura es confirma a totes dues zones abans de donar el commit per bo.
graph LR
APP[App Flask<br/>europe-west1] -->|escriptures i lectures| P[Principal<br/>europe-west1-b]
P -.->|replicacio sincrona| S[En espera<br/>europe-west1-c]
P -->|replicacio asincrona| R[Replica de lectura<br/>europe-west1-d]
R -->|nomes lectura| L[Informes de la Lucia]
S -.->|failover automatic<br/>mateixa IP i nom| P
Què significa a la pràctica:
- La instància en espera no serveix trànsit. No és una rèplica de lectura: existeix només per prendre el relleu. Pagues per ella i no la utilitzes, i aquesta és precisament l'assegurança que compres.
- El failover és automàtic i conserva l'adreça de connexió. L'aplicació no canvia la seva configuració; només veu un tall de connexions d'aproximadament un minut (menys a Enterprise Plus).
- L'aplicació ha de saber reconnectar. Aquest és el punt que s'oblida: si el teu pool de connexions no reintenta, el failover es converteix en un error visible per al client. Configura reintents al pool (ho veurem a l'apartat 9).
- Duplica aproximadament el cost de la instància. És la decisió econòmica més clara d'aquesta lliçó: per a
alpinashop-prodsí; per aalpinashop-dev, no.
Es pot provar de debò, i cal provar-ho:
Executa aquesta comanda en un entorn de proves mentre l'aplicació està funcionant i observa quant triga a recuperar-se. Un pla d'alta disponibilitat que mai no s'ha exercitat és una hipòtesi, no un pla.
- Rèpliques de lectura per als informes de la Lucía
La Lucía llança consultes d'agregació sobre les comandes: vendes per categoria, evolució mensual, productes sense rotació. Executades contra la base principal, competeixen per CPU i memòria amb la botiga, i un informe pesat un dissabte de campanya pot alentir el checkout.
La solució immediata és una rèplica de lectura: una còpia asíncrona que accepta consultes SELECT.
gcloud sql instances create alpinashop-pedidos-replica-informes \
--master-instance-name=alpinashop-pedidos \
--region=europe-west1 \
--tier=db-custom-2-7680 \
--labels=entorno=prod,equipo=datos,centro-coste=analitica,aplicacion=catalogoCaracterístiques que cal tenir clares:
- La replicació és asíncrona: la rèplica va lleugerament endarrerida (normalment mil·lisegons o pocs segons). Per a informes és irrellevant; per llegir una comanda just després de crear-la, no serveix.
- És de només lectura. Qualsevol escriptura falla.
- Pot tenir un tipus de màquina diferent del principal: si els informes de la Lucía necessiten més memòria, els la pots donar sense tocar producció.
- Pot estar en una altra regió, cosa que serveix a més com a pla de recuperació davant de desastre geogràfic.
- Es pot promoure a instància independent amb
gcloud sql instances promote-replica. És una operació irreversible que trenca la replicació, útil en una recuperació real o per crear un entorn de proves amb dades actuals.
Vigila el retard de replicació:
gcloud sql instances describe alpinashop-pedidos-replica-informes \
--format="value(replicaConfiguration)"A Cloud Monitoring, la mètrica rellevant és database/replication/replica_lag (06-04). Una alerta quan superi uns minuts evita informes que semblen correctes però estan desactualitzats.
On és el límit. Una rèplica de lectura resol el problema d'avui, però les consultes analítiques sobre una base transaccional sempre seran ineficients: PostgreSQL desa per files, i un informe que agrega una columna sobre milions de comandes llegeix files senceres. La solució definitiva per a la Lucía és exportar les dades a BigQuery, amb emmagatzematge columnar i motor pensat per agregar. Això és el mòdul 4. De moment, la rèplica és la resposta correcta i barata.
- Còpies de seguretat, retenció i recuperació a un punt en el temps
Ja vam configurar còpies diàries amb 14 dies de retenció i PITR de 7 dies en crear la instància. Vegem què implica cada mecanisme.
Còpies automàtiques. Diàries, incrementals, emmagatzemades fora de la instància i sense cost de rendiment apreciable. No són pg_dump: són còpies de l'emmagatzematge.
# Llistar copies disponibles
gcloud sql backups list --instance=alpinashop-pedidos
# Copia manual abans d'una operacio arriscada
gcloud sql backups create --instance=alpinashop-pedidos \
--description="Abans de migrar l'esquema de comandes v3"Aquesta última comanda és un hàbit que val el seu pes en or: abans de qualsevol migració d'esquema, una còpia manual amb descripció explícita.
Restaurar. Hi ha dos escenaris molt diferents:
# 1. Restaurar una copia SOBRE la instancia original (sobreescriu: destructiu)
gcloud sql backups restore <BACKUP_ID> \
--restore-instance=alpinashop-pedidos
# 2. Restaurar a una instancia NOVA (el recomanable en un incident)
gcloud sql instances clone alpinashop-pedidos alpinashop-pedidos-restaurada \
--point-in-time="2026-08-05T09:15:00Z"Gairebé sempre vols el segon. Restaurar sobre la instància original destrueix l'estat actual, incloses les dades posteriors a l'incident, que potser vols conservar. Clonar a una instància nova et deixa comparar, extreure només el necessari i decidir amb calma.
Recuperació a un punt en el temps (PITR). És la diferència entre "recupero la còpia d'anit" i "recupero l'estat exacte de les 09:14:59, un segon abans que el script esborrés la taula de preus". Funciona combinant l'última còpia amb els registres de transaccions (WAL). Requereix tenir PITR habilitat abans de l'incident; activar-lo després no permet viatjar al passat.
Un exemple realista d'ús: el Dani executa a les 09:15 un UPDATE sense WHERE sobre productos. A les 09:20 es detecta. La seqüència correcta és clonar a alpinashop-pedidos-restaurada amb --point-in-time a les 09:14:59, verificar-hi que els preus són correctes, exportar únicament la taula afectada i reimportar-la a producció. La botiga no s'atura i no es perd cap venda d'aquells cinc minuts.
Exportacions lògiques. A més de les còpies gestionades, convé exportar periòdicament a Cloud Storage en format SQL: serveix per migrar, per endur-se les dades fora de Google Cloud i com a còpia independent del servei.
gcloud sql export sql alpinashop-pedidos \
gs://alpinashop-backups/tienda/tienda-$(date +%Y%m%d).sql.gz \
--database=tiendaLes còpies gestionades van lligades a la instància: si algú esborra la instància, s'esborren amb ella (excepte còpies finals). Una exportació en un bucket amb versionatge i retenció és una capa de protecció diferent. Aquesta és la raó, per cert, per la qual vam dedicar la lliçó anterior a Cloud Storage abans que aquesta.
- Connectivitat: IP pública, IP privada i l'Auth Proxy
Connectar-se a Cloud SQL és on més gent s'encalla, així que anem per parts.
| Mètode | Com funciona | Seguretat | Quan utilitzar-lo |
|---|---|---|---|
| IP pública + xarxes autoritzades | La instància té IP pública; només s'accepten connexions de les IP que autoritzis | Mitjana: depèn d'una llista d'IP, que en oficines amb IP dinàmica és un problema | Accés puntual d'administració |
| IP privada (VPC) | La instància rep una IP dins de la teva xarxa privada; no és abastable des d'internet | Alta | Producció, quan el client és a la mateixa VPC |
| Cloud SQL Auth Proxy | Un procés local obre un túnel xifrat i autenticat amb IAM cap a la instància | Molt alta: sense exposar IP, amb identitat i xifratge gestionats | La recomanació general, especialment en desenvolupament |
| Connectors de llenguatge | Biblioteca que integra l'Auth Proxy dins de l'aplicació | Molt alta | Producció amb Python, Java, Go o Node |
Xarxes autoritzades (IP pública). Només per a accés administratiu, i mai 0.0.0.0/0:
gcloud sql instances patch alpinashop-pedidos \
--authorized-networks="88.20.13.45/32" \
--no-assign-ip # elimina la IP publica quan ja no calguiCloud SQL Auth Proxy. És la peça clau. Un binari que s'executa al costat de la teva aplicació, escolta a localhost i reenvia les connexions a Cloud SQL per un túnel TLS, autenticant-se amb les teves credencials de Google Cloud. Avantatges: la instància no necessita IP pública, no cal gestionar certificats ni llistes d'IP, i l'accés es controla amb el rol IAM roles/cloudsql.client.
# Descarregar el proxy (versio 2)
curl -o cloud-sql-proxy \
https://storage.googleapis.com/cloud-sql-connectors/cloud-sql-proxy/v2.11.0/cloud-sql-proxy.linux.amd64
chmod +x cloud-sql-proxy
# Arrencar-lo: escolta a localhost:5432
./cloud-sql-proxy --port 5432 \
alpinashop-prod:europe-west1:alpinashop-pedidosAquest identificador projecte:regio:instancia és el nom de connexió de la instància, i el necessitaràs constantment:
Amb el proxy en marxa, qualsevol client PostgreSQL apunta a localhost com si la base fos a la teva màquina:
I si ets a Cloud Shell, no cal ni descarregar-lo:
- Connectar l'aplicació Flask
En producció no es llança un procés proxy a part: s'utilitza el connector de Python, que fa el mateix dins del procés de l'aplicació.
import os
import sqlalchemy
from flask import Flask, jsonify
from google.cloud.sql.connector import Connector, IPTypes
app = Flask(__name__)
NOM_CONNEXIO = os.environ["INSTANCIA_SQL"] # alpinashop-prod:europe-west1:alpinashop-pedidos
USUARI = os.environ["DB_USER"] # app_catalogo
PASSWORD = os.environ["DB_PASS"] # injectada des de Secret Manager (03-06)
BASE_DADES = os.environ.get("DB_NAME", "tienda")
connector = Connector()
def _crear_connexio():
"""Obre una connexio nova a traves del connector de Cloud SQL."""
return connector.connect(
NOM_CONNEXIO,
"pg8000",
user=USUARI,
password=PASSWORD,
db=BASE_DADES,
ip_type=IPTypes.PUBLIC, # IPTypes.PRIVATE si la instancia nomes te IP privada
)
# El pool de connexions es crea UNA vegada, en arrencar l'aplicacio.
engine = sqlalchemy.create_engine(
"postgresql+pg8000://",
creator=_crear_connexio,
pool_size=5, # connexions permanents per proces
max_overflow=2, # connexions extra en pics
pool_timeout=30, # segons esperant una connexio lliure
pool_recycle=1800, # recicla connexions cada 30 min
pool_pre_ping=True, # comprova la connexio abans d'utilitzar-la
)
@app.route("/productos")
def llistar_productes():
consulta = sqlalchemy.text(
"""
SELECT sku, nombre, precio, stock
FROM tienda.productos
WHERE activo = true
ORDER BY nombre
LIMIT 50
"""
)
with engine.connect() as conn:
files = conn.execute(consulta).mappings().all()
return jsonify([dict(f) for f in files])
@app.route("/producto/<sku>")
def detall_producte(sku):
consulta = sqlalchemy.text(
"SELECT sku, nombre, precio, stock FROM tienda.productos WHERE sku = :sku"
)
with engine.connect() as conn:
fila = conn.execute(consulta, {"sku": sku}).mappings().first()
if fila is None:
return {"error": "no trobat"}, 404
return dict(fila)Aspectes del codi que cal entendre bé:
pool_pre_ping=Trueés el flag que fa sobreviure al failover. Abans de lliurar una connexió del pool, comprova que continua viva; si el failover la va tancar, la descarta i n'obre una altra. Sense això, després d'un failover l'aplicació retorna errors fins que es reinicia.pool_recycle=1800evita connexions caducades pels temps d'inactivitat dels intermediaris de xarxa.pool_sizes'ha de multiplicar pel nombre de processos. Si gunicorn arrenca 4 workers ambpool_size=5, són 20 connexions per instància. Amb 10 instàncies del MIG en un pic de tardor, 200 connexions: exactament elmax_connectionsque vam configurar. Aquest càlcul és la causa número u de caigudes per esgotament de connexions, i cal fer-lo abans, no després.- Consultes parametritzades (
:sku). No concatenis mai cadenes en SQL: és la porta de la injecció SQL. - La contrasenya ve d'una variable d'entorn, i en producció aquesta variable s'omple des de Secret Manager (03-06), mai d'un fitxer al repositori.
Si el nombre de connexions es converteix en un problema, el patró habitual és posar PgBouncer al davant, o utilitzar Cloud SQL Enterprise Plus amb el seu gestor de connexions integrat.
- Migrar les dades actuals
Ha arribat el moment de moure la base tienda real. Per a 12 GB, el camí més simple i controlat és pg_dump més importació des de Cloud Storage.
Pas 1: bolcat al servidor d'origen.
# Format SQL pla, sense propietaris ni privilegis (els usuaris son uns altres al desti)
pg_dump \
--host=localhost \
--username=postgres \
--dbname=tienda \
--no-owner \
--no-acl \
--format=plain \
--file=/tmp/tienda.sql
gzip /tmp/tienda.sql--no-owner i --no-acl eviten que el bolcat intenti assignar propietaris i permisos a usuaris que no existeixen a Cloud SQL. És la causa més freqüent d'errors durant una importació.
Pas 2: pujar el bolcat al bucket.
Pas 3: autoritzar Cloud SQL a llegir el bucket. La instància té el seu propi compte de servei, i necessita permís explícit:
SA=$(gcloud sql instances describe alpinashop-pedidos \
--format="value(serviceAccountEmailAddress)")
gcloud storage buckets add-iam-policy-binding gs://alpinashop-backups \
--member="serviceAccount:$SA" \
--role="roles/storage.objectViewer"Aquest pas s'oblida sempre i produeix un error de permisos poc descriptiu. Recorda-ho: la instància de Cloud SQL és una identitat més, i sense permís no llegeix el teu bucket.
Pas 4: importar.
gcloud sql import sql alpinashop-pedidos \
gs://alpinashop-backups/migracion/tienda.sql.gz \
--database=tienda \
--user=postgresPas 5: verificar. No donis mai per bona una migració sense contrastar:
-- Nombre de files per taula
SELECT relname AS tabla, n_live_tup AS filas_aprox
FROM pg_stat_user_tables
ORDER BY n_live_tup DESC;
-- Comprovacions de negoci: totals que han de coincidir amb l'origen
SELECT count(*) AS pedidos, sum(total) AS importe_total FROM tienda.pedidos;
SELECT count(*) AS productos FROM tienda.productos;
-- Actualitzar estadistiques del planificador despres d'una carrega massiva
ANALYZE;Aquest ANALYZE final importa: després d'una importació massiva, les estadístiques del planificador estan buides i les consultes poden anar absurdament lentes fins que es recalculen.
El problema del tall. Aquest procediment implica aturar les escriptures mentre es bolca i s'importa: per a 12 GB, potser 30-40 minuts de botiga en mode lectura. És assumible si es fa de matinada.
Quan no és assumible, l'eina és Database Migration Service (DMS): crea una rèplica contínua des del teu PostgreSQL d'origen cap a Cloud SQL, manté la sincronització mentre la botiga continua funcionant i permet un tall final de segons. Requereix que l'origen tingui replicació lògica habilitada i connectivitat amb Google Cloud. Per a una migració de desenes o centenars de GB amb exigència de disponibilitat, és l'opció correcta; per als 12 GB d'AlpinaShop en una matinada de diumenge, pg_dump és més simple i suficient.
- Manteniment programat i finestres
Google actualitza el motor i la infraestructura periòdicament. Aquestes actualitzacions impliquen un reinici breu, i vols decidir quan passa.
gcloud sql instances patch alpinashop-pedidos \
--maintenance-window-day=SUN \
--maintenance-window-hour=4 \
--maintenance-release-channel=production \
--deny-maintenance-period-start-date=2026-10-01 \
--deny-maintenance-period-end-date=2026-11-15 \
--deny-maintenance-period-time=00:00:00Els tres últims flags són especialment valuosos per a AlpinaShop: defineixen un període de denegació de manteniment que bloqueja les actualitzacions durant la campanya de tardor. Google les posposarà fins després del 15 de novembre.
| Canal | Què rep | Recomanació |
|---|---|---|
preview |
Versions noves abans | Només entorns de prova |
production |
Versions ja estabilitzades | Producció |
Bona pràctica: mantén alpinashop-dev a preview amb la finestra un dia abans que producció. Així els canvis arriben primer a desenvolupament i tens marge de reacció.
- Escalat vertical i límits
Cloud SQL escala verticalment per a escriptures: no hi ha manera de repartir les escriptures entre diverses instàncies.
# Canviar el tipus de maquina (implica reinici, uns minuts de tall)
gcloud sql instances patch alpinashop-pedidos --tier=db-custom-4-15360
# Ampliar el disc (en calent, sense tall; mai no es pot reduir)
gcloud sql instances patch alpinashop-pedidos --storage-size=100GBLímits i consideracions que cal conèixer:
- El disc creix però no es redueix. Si actives el creixement automàtic i una càrrega anòmala infla el disc a 2 TB, continuaràs pagant 2 TB. L'única sortida és exportar i recrear.
- Les connexions són un recurs escàs.
max_connectionsdepèn de la memòria de la instància; cada connexió de PostgreSQL és un procés amb la seva memòria. - No hi ha superusuari. L'usuari
postgresde Cloud SQL té privilegis amplis però no ésSUPERUSER. Algunes extensions i operacions no estan disponibles. - Extensions limitades a la llista suportada. Comprova la teva abans de migrar.
- Escalar té sostre. El tipus de màquina més gran disponible marca el límit físic de la teva base de dades. Si t'hi acostes, és senyal que necessites una altra arquitectura.
- Quan Cloud SQL es queda curt: AlloyDB i Spanner
Tres senyals que Cloud SQL se t'ha quedat petit: les escriptures saturen la instància més gran disponible; necessites escriptura activa en diverses regions simultàniament; o les consultes analítiques sobre la mateixa base de dades són inevitables i molt pesades.
| Servei | Què és | Quan justifica el canvi |
|---|---|---|
| Cloud SQL | PostgreSQL/MySQL/SQL Server gestionat, una instància principal | El cas general. Fins a uns pocs TB i una regió |
| AlloyDB | Compatible amb PostgreSQL, rearquitecturat per Google: emmagatzematge distribuït, molt més rendiment transaccional i un motor columnar integrat per a analítica | Necessites molt més rendiment sense sortir de l'ecosistema PostgreSQL, o barrejar càrrega transaccional i analítica |
| Cloud Spanner | Base de dades relacional distribuïda globalment, amb transaccions fortament consistents i escalat horitzontal d'escriptures | Escala global, escriptures en diverses regions, disponibilitat extrema. Cost base molt superior |
Per a AlpinaShop, Cloud SQL cobreix amb escreix l'horitzó previsible: una botiga espanyola amb pics estacionals és molt lluny d'aquests límits. La ruta de creixement natural seria, primer, descarregar l'analítica a BigQuery (mòdul 4); després, si el volum transaccional ho exigís, AlloyDB, que permet migrar amb canvis mínims per la seva compatibilitat amb PostgreSQL. Spanner s'estudia amb detall a la lliçó 02-06, dins del panorama de bases de dades no relacionals i distribuïdes.
Errors habituals i consells
- Crear la instància sense alta disponibilitat "i ja l'activarem". Canviar a REGIONAL després és possible, però implica reinici i se sol posposar indefinidament.
- Confiar en còpies que mai no s'han restaurat. Programa una restauració de prova trimestral a una instància clonada.
- Activar PITR després de l'incident. No serveix: cal tenir-lo abans.
- Restaurar sobre la instància original en un incident. Clona a una instància nova i decideix amb calma.
- Oblidar
ALTER DEFAULT PRIVILEGES. Les taules creades després delGRANTqueden inaccessibles. - No donar permís de lectura del bucket al compte de servei de la instància abans d'importar.
- Importar sense
--no-owner --no-acl. Falla per usuaris inexistents al destí. - No fer
ANALYZEdesprés de la importació. Consultes absurdament lentes per estadístiques buides. - Dimensionar el pool sense multiplicar per processos i instàncies. Esgotament de connexions en el pitjor moment possible.
- Ometre
pool_pre_ping. L'aplicació no sobreviu a un failover. - Exposar la IP pública amb xarxes autoritzades àmplies. Utilitza l'Auth Proxy o el connector.
- Consell: activa
log_min_duration_statementdes del primer dia. És diagnòstic gratuït. - Consell: fes una còpia manual amb descripció abans de cada migració d'esquema.
- Consell: defineix un període de denegació de manteniment que cobreixi la campanya de tardor.
- Consell: exporta periòdicament a un bucket amb versionatge, com a còpia independent del cicle de vida de la instància.
Exercicis
Exercici 1: crear i assegurar la instància de desenvolupament
- Crea
alpinashop-pedidos-devamb PostgreSQL 16,db-g1-smallodb-custom-1-3840, aeurope-west1, amb disponibilitat zonal (no regional) i còpies diàries amb 7 dies de retenció. Justifica per què en desenvolupament no posem alta disponibilitat. - Crea la base de dades
tiendai els usuarisapp_catalogoiinformes_lectura. - Connecta't amb
gcloud sql connecti crea l'esquematiendaamb una taulaproductos(sku, nombre, precio, stock, activo). - Concedeix a
app_catalogopermisos de lectura/escriptura i ainformes_lecturanomés lectura, incloent-hi els privilegis per defecte. - Comprova des d'
informes_lecturaque unINSERTfalla i unSELECTfunciona.
Exercici 2: còpies, PITR i recuperació
- Insereix tres productes i anota l'hora exacta.
- Simula un accident:
DELETE FROM tienda.productos;senseWHERE. - Clona la instància a un instant anterior a l'esborrat.
- Verifica al clon que les dades hi són i explica com retornaries només aquella taula a la instància original.
- Esborra el clon i calcula què t'hauria costat tenir-lo un mes encès.
Exercici 3: connexió des de Flask amb tolerància a fallades
- Escriu una aplicació Flask mínima que es connecti amb el connector de Python i exposi
/productos. - Configura el pool amb
pool_pre_ping,pool_recyclei unpool_sizejustificat per a 4 workers de gunicorn i fins a 6 instàncies. - Calcula el nombre total de connexions en el pitjor cas i compara'l amb
max_connections. - Explica què passaria durant un failover amb i sense
pool_pre_ping. - Indica d'on hauria de venir la contrasenya en producció i per què no d'una variable al codi.
Solucions
Solució 1
gcloud sql instances create alpinashop-pedidos-dev \
--database-version=POSTGRES_16 \
--tier=db-custom-1-3840 \
--region=europe-west1 \
--availability-type=ZONAL \
--storage-type=SSD --storage-size=20GB --storage-auto-increase \
--backup-start-time=02:00 --retained-backups-count=7 \
--labels=entorno=dev,equipo=plataforma,centro-coste=tienda,aplicacion=catalogo
gcloud sql databases create tienda --instance=alpinashop-pedidos-dev
gcloud sql users create app_catalogo --instance=alpinashop-pedidos-dev --prompt-for-password
gcloud sql users create informes_lectura --instance=alpinashop-pedidos-dev --prompt-for-passwordEn desenvolupament no posem alta disponibilitat perquè duplica el cost per protegir-nos d'un risc que en desenvolupament no té conseqüències: si la instància cau unes hores, ningú no perd una venda. L'alta disponibilitat es paga on hi ha ingressos en joc. És la mateixa lògica de repartiment de recursos que vam aplicar amb els labels de centro-coste a 01-04.
-- 3 i 4
CREATE SCHEMA IF NOT EXISTS tienda;
CREATE TABLE tienda.productos (
sku TEXT PRIMARY KEY,
nombre TEXT NOT NULL,
precio NUMERIC(10,2) NOT NULL CHECK (precio >= 0),
stock INTEGER NOT NULL DEFAULT 0,
activo BOOLEAN NOT NULL DEFAULT true
);
GRANT USAGE ON SCHEMA tienda TO app_catalogo, informes_lectura;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA tienda TO app_catalogo;
GRANT SELECT ON ALL TABLES IN SCHEMA tienda TO informes_lectura;
ALTER DEFAULT PRIVILEGES IN SCHEMA tienda
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO app_catalogo;
ALTER DEFAULT PRIVILEGES IN SCHEMA tienda
GRANT SELECT ON TABLES TO informes_lectura;# 5. Comprovacio amb l'usuari de nomes lectura
gcloud sql connect alpinashop-pedidos-dev --user=informes_lectura --database=tiendaSELECT count(*) FROM tienda.productos; -- funciona
INSERT INTO tienda.productos(sku, nombre, precio)
VALUES ('X-1', 'prova', 1.00); -- ERROR: permission deniedSolució 2
-- 1
INSERT INTO tienda.productos (sku, nombre, precio, stock) VALUES
('MOC-40', 'Motxilla Trekking 40L', 89.90, 25),
('BOT-GTX', 'Botes Gore-Tex Alpina', 149.00, 12),
('TDA-2P', 'Tenda 2 places Ultralight', 219.50, 8);
SELECT now(); -- anotar aquesta marca temporal# 3. Clonar a un instant anterior (requereix PITR habilitat a la instancia)
gcloud sql instances clone alpinashop-pedidos-dev alpinashop-pedidos-rescate \
--point-in-time="2026-08-05T09:14:59Z"
# 4. Verificar al clon
gcloud sql connect alpinashop-pedidos-rescate --user=postgres --database=tiendaPer retornar només aquella taula a la instància original, s'exporta del clon i s'importa a producció, sense tocar la resta:
gcloud sql export sql alpinashop-pedidos-rescate \
gs://alpinashop-backups/rescate/productos.sql.gz \
--database=tienda --table=tienda.productos
gcloud sql import sql alpinashop-pedidos-dev \
gs://alpinashop-backups/rescate/productos.sql.gz \
--database=tienda --user=postgresAquest és l'avantatge de clonar en lloc de restaurar a sobre: es recupera exactament la taula danyada i es conserven totes les dades correctes escrites després de l'incident.
Un clon és una instància completa i es factura com a tal des del moment en què existeix: una db-custom-1-3840 amb 20 GB d'SSD volta les desenes de dòlars al mes. Deixar clons de rescat oblidats encesos és una despesa silenciosa molt comuna; esborra'ls tan bon punt acabi la recuperació.
Solució 3
import os
import sqlalchemy
from flask import Flask, jsonify
from google.cloud.sql.connector import Connector
app = Flask(__name__)
connector = Connector()
def _connectar():
return connector.connect(
os.environ["INSTANCIA_SQL"],
"pg8000",
user=os.environ["DB_USER"],
password=os.environ["DB_PASS"],
db="tienda",
)
engine = sqlalchemy.create_engine(
"postgresql+pg8000://",
creator=_connectar,
pool_size=5,
max_overflow=2,
pool_recycle=1800,
pool_pre_ping=True,
)
@app.route("/productos")
def productes():
with engine.connect() as conn:
files = conn.execute(
sqlalchemy.text("SELECT sku, nombre, precio FROM tienda.productos")
).mappings().all()
return jsonify([dict(f) for f in files])- El càlcul en el pitjor cas:
Amb max_connections=200 hi ha marge, però és estret: queden 32 connexions per a les tasques de manteniment, els informes de la Lucía i les sessions administratives. Si el MIG pogués arribar a 10 instàncies, serien 280 connexions i la base de dades les rebutjaria. Les opcions són baixar pool_size a 3, augmentar la memòria de la instància per elevar max_connections, o introduir PgBouncer.
-
Durant un failover, les connexions obertes del pool queden trencades. Sense
pool_pre_ping, SQLAlchemy lliura aquestes connexions mortes i cada petició falla amb un error de connexió fins que el pool es recicla o l'aplicació es reinicia: minuts d'errors 500 visibles per al client. Ambpool_pre_ping, cada connexió es verifica abans d'utilitzar-se; les trencades es descarten i se n'obren de noves de manera transparent, i l'usuari només percep una latència una mica més gran durant uns segons. -
En producció la contrasenya ha de venir de Secret Manager (lliçó 03-06), injectada com a variable d'entorn o llegida a l'arrencada amb el compte de servei de l'aplicació. Mai al codi ni al repositori perquè quedaria a l'historial de Git per sempre, seria visible per a qualsevol amb accés al repositori i no podria rotar-se sense tornar a desplegar. L'opció òptima és directament l'autenticació IAM de Cloud SQL, que elimina la contrasenya.
Conclusió
La base de dades tienda ha deixat de ser el punt feble d'AlpinaShop. Has vist en una taula concreta quines tasques deixa de fer la Marta —pedaços, còpies, failover, rèpliques, xifratge— i què es perd a canvi: el superusuari i el control total del motor, un intercanvi que per a aquesta aplicació és clarament favorable. Has triat PostgreSQL 16, edició Enterprise i un dimensionament raonat en què la memòria i la mida del disc importen més que la CPU. Has creat alpinashop-pedidos a europe-west1 amb una sola comanda que concentra les decisions importants: alta disponibilitat regional, còpies diàries amb 14 dies de retenció, PITR de 7 dies, finestra de manteniment el diumenge de matinada i registre de consultes lentes des del primer minut.
Has creat la base tienda amb usuaris diferenciats i permisos mínims, sense oblidar ALTER DEFAULT PRIVILEGES. Has entès que la instància en espera de l'alta disponibilitat no serveix trànsit i que el seu valor és en el failover automàtic que conserva l'adreça de connexió, i que aquest failover només és transparent si l'aplicació reconnecta —d'aquí pool_pre_ping. Has creat una rèplica de lectura perquè els informes de la Lucía no competeixin amb la botiga, sabent que és una solució correcta però temporal, perquè l'analítica de debò viu a BigQuery. Has configurat còpies, has après a clonar a un punt en el temps en lloc de restaurar destructivament, i has vist per què una exportació a un bucket amb versionatge és una capa de protecció diferent de les còpies gestionades. Has comparat IP pública, IP privada, Auth Proxy i connector de Python, i has connectat el catàleg Flask amb un pool ben dimensionat i consultes parametritzades. I has fet la migració real amb pg_dump, importació des del bucket, verificació i ANALYZE, amb Database Migration Service anotat per quan el tall no sigui acceptable.
AlpinaShop ja té el seu còmput en màquines virtuals, les seves imatges en un bucket i les seves dades en una base gestionada. Però la Marta continua mantenint sistemes operatius, plantilles d'instància i startup scripts. A 02-04, App Engine, veurem què passa quan s'elimina també aquesta capa: què és un PaaS, en què es diferencien l'entorn estàndard i el flexible, com es descriu una aplicació sencera en un app.yaml de vint línies, com es despleguen versions i es divideix el trànsit per fer un canary, com es configura l'escalat —inclòs l'escalat a zero— i com la mateixa aplicació Flask accedeix a Cloud SQL i a Cloud Storage sense que ningú administri ni un sol servidor.
Curs de Google Cloud Platform (GCP)
Mòdul 1: Introducció a Google Cloud Platform
- Què és Google Cloud Platform?
- Configuració del teu compte de GCP
- Descripció general de la consola de GCP
- Projectes, jerarquia de recursos i facturació
- Regions, zones i model de responsabilitat compartida
- Cloud Shell i la CLI de gcloud
Mòdul 2: Serveis principals de GCP
- Compute Engine: màquines virtuals a Google Cloud
- Cloud Storage: emmagatzematge d'objectes
- Cloud SQL: bases de dades relacionals gestionades
- App Engine: plataforma com a servei
- Google Kubernetes Engine (GKE)
- Bases de dades NoSQL: Firestore, Bigtable i Spanner
- Com triar el servei de còmput adequat
Mòdul 3: Xarxes i seguretat
- Xarxes VPC
- Balanceig de càrrega al núvol
- Cloud CDN
- Gestió d'identitat i accés (IAM)
- Cloud Armor
- Secrets i xifratge: Secret Manager i Cloud KMS
- Cloud DNS, certificats TLS i publicació segura de serveis
Mòdul 4: Dades i anàlisi
- BigQuery: el magatzem de dades analític
- Cloud Dataflow: processament de dades per lots i en temps real
- Cloud Dataproc: Spark i Hadoop gestionats
- Cloud Pub/Sub: missatgeria asíncrona
- Cloud Data Fusion: integració de dades sense codi
- Orquestració de pipelines amb Cloud Composer i Workflows
- Govern de les dades i taulers amb Dataplex i Looker Studio
Mòdul 5: Aprenentatge automàtic i IA
- Vertex AI: la plataforma d'aprenentatge automàtic de GCP
- AutoML: models a mida sense escriure codi
- TensorFlow a GCP: entrenament i servei de models
- API de llenguatge natural
- API de visió
- IA generativa a Vertex AI: models Gemini i incrustacions
- MLOps: del model al producte amb Vertex AI Pipelines
Mòdul 6: DevOps i monitoratge
- Cloud Build: integració contínua a GCP
- Cloud Source Repositories i gestió del codi font
- Cloud Functions: funcions sense servidor
- Cloud Monitoring (abans Stackdriver): mètriques, taulers i alertes
- Cloud Deployment Manager i infraestructura com a codi nativa
- Cloud Logging i Cloud Trace: registres, traces i diagnòstic
- Terraform a GCP: infraestructura com a codi a la pràctica
Mòdul 7: Temes avançats de GCP
- Híbrid i multinúvol amb Anthos
- Computació sense servidor amb Cloud Run
- Xarxes avançades: VPC compartida, aparellament i connectivitat híbrida
- Bones pràctiques de seguretat
- Gestió i optimització de costos
- Fiabilitat: SLO, alta disponibilitat i recuperació de desastres
- Govern a escala: organització, polítiques i auditoria
