La xarxa d'AlpinaShop ja està construïda: alpinashop-vpc amb les seves subxarxes, el tallafoc que només deixa passar el necessari, Cloud NAT per a la sortida i la base de dades en IP privada. Però el catàleg continua servint-se per l'adreça d'una màquina concreta. Si aquesta màquina cau, la botiga cau. Si el trànsit creix, no hi ha res que reparteixi. I no hi ha HTTPS, ni domini, ni un punt únic on aplicar seguretat. Aquesta lliçó posa al davant la peça que resol tot això alhora: el balancejador de càrrega.

Cloud Load Balancing no és un aparell ni una màquina virtual que tu administres. És un servei distribuït, implementat a la mateixa infraestructura de xarxa de Google, sense instàncies que dimensionar ni escalar. La seva versió global té una propietat que convé entendre bé perquè canvia el disseny: una sola adreça IP anycast que s'anuncia des de més de cent punts de presència repartits pel món, de manera que un client de Madrid i un altre de Hèlsinki entren pel punt més proper a cadascun i viatgen la resta del trajecte per la xarxa privada de Google.

A més, el balancejador és el lloc on s'endolla tot la resta: el certificat TLS (03-07), la memòria cau de Cloud CDN (03-03) i el tallafoc d'aplicació Cloud Armor (03-05) es configuren sobre ell. Construir-lo bé és la condició prèvia de les quatre lliçons següents.

En aquesta lliçó aprendràs a orientar-te al catàleg de balancejadors sense perdre't en la nomenclatura, muntaràs peça a peça el balancejador HTTP(S) extern global d'AlpinaShop amb gcloud, encaminaràs /imagenes/* directament al bucket alpinashop-catalogo i la resta al MIG alpinashop-web-mig, i dominaràs les comprovacions d'estat, que són la causa del 90 % dels problemes d'un balancejador acabat de crear.

Contingut

  1. Per què un balancejador i quins problemes resol
  2. El catàleg de balancejadors de Google Cloud i com triar
  3. Anatomia d'un balancejador HTTP(S) extern global
  4. Preparatius: comprovació d'estat i port amb nom
  5. El servei de backend sobre el MIG
  6. El backend de bucket per a les imatges
  7. El mapa d'URL: encaminar per ruta
  8. Proxy de destí i regla de reenviament
  9. Comprovacions d'estat en profunditat: per què fallen
  10. Polítiques de balanceig, capacitat i afinitat de sessió
  11. Redirecció d'HTTP a HTTPS
  12. Escalat global: per què no cal "preescalfar"
  13. Balancejadors interns per a serveis interns
  14. NEG sense servidor: Cloud Run darrere del mateix balancejador
  15. Registre i mètriques del balancejador

  1. Per què un balancejador i quins problemes resol

Un balancejador de càrrega fa molt més que repartir peticions. En el cas d'AlpinaShop resol simultàniament:

Problema actual Com el resol el balancejador
Una sola VM: punt únic de fallada Reparteix entre les instàncies del MIG i treu de rotació les que fallen
El trànsit només entra per una zona El MIG regional cobreix tres zones; el balancejador reparteix entre elles
Clients llunyans amb latència alta IP anycast: s'entra pel PoP més proper i es viatja per la xarxa de Google
No hi ha HTTPS ni certificat Acaba TLS amb certificats gestionats (03-07)
Les imatges les serveix l'aplicació Un backend de bucket serveix alpinashop-catalogo sense tocar l'aplicació
No hi ha on posar WAF ni memòria cau Cloud Armor (03-05) i Cloud CDN (03-03) s'apliquen aquí
Actualitzar l'aplicació implica tall Drenatge de connexions i actualització progressiva del MIG

La idea clau: el balancejador és la vora de la teva arquitectura. Tot el que vulguis fer una sola vegada per a tot el trànsit —xifrar, filtrar, desar a la memòria cau, mesurar— es fa a la vora.

  1. El catàleg de balancejadors de Google Cloud i com triar

Aquí és on molta gent es perd, perquè la nomenclatura ha canviat amb els anys i els noms són llargs. Ho ordenarem amb tres preguntes.

Pregunta 1: capa 7 o capa 4? És a dir, el balancejador entén HTTP (rutes, capçaleres, galetes) o només mou paquets TCP/UDP?

Pregunta 2: extern o intern? El criden des d'internet o només des de dins de la teva VPC?

Pregunta 3: global o regional? Una IP única per a tot el món o una IP per regió?

Amb aquestes tres respostes, el balancejador queda determinat:

Balancejador Capa Extern/intern Abast Protocols Cas d'ús
Balancejador d'aplicacions extern global 7 Extern Global HTTP, HTTPS, HTTP/2, HTTP/3 El d'AlpinaShop. Web pública mundial, CDN, WAF
Balancejador d'aplicacions extern regional 7 Extern Regional HTTP, HTTPS Requisits de residència de les dades en una regió
Balancejador d'aplicacions intern 7 Intern Regional (o entre regions) HTTP, HTTPS Microserveis interns amb encaminament per ruta
Balancejador de xarxa de proxy extern 4 (proxy) Extern Global o regional TCP, SSL Protocols no HTTP que necessiten terminació TLS
Balancejador de xarxa de pas a través extern 4 (pas a través) Extern Regional TCP, UDP, ESP, ICMP Preserva la IP d'origen; jocs, protocols estranys
Balancejador de xarxa de pas a través intern 4 Intern Regional TCP, UDP Backend intern d'alt rendiment; base de dades, gRPC

Dues distincions que convé tenir clares:

  • Proxy davant de pas a través. Un balancejador proxy acaba la connexió del client i n'obre una altra cap al backend; el backend veu la IP del balancejador i rep la del client a la capçalera X-Forwarded-For. Un balancejador de pas a través no acaba res: el paquet arriba al backend amb la IP d'origen original.
  • Global davant de regional. El global fa servir anycast i un únic mapa d'URL per a totes les regions; el regional viu en una sola regió i la seva IP també. El global és el que permet CDN i Cloud Armor a escala mundial.

Regla pràctica per triar: si és trànsit web públic, és el balancejador d'aplicacions extern global. Només se'n baixa per un requisit concret: residència de les dades, un protocol que no és HTTP o la necessitat de conservar la IP d'origen a nivell de paquet.

  1. Anatomia d'un balancejador HTTP(S) extern global

El balancejador no és un objecte: és una cadena d'objectes que es creen per separat i s'enllacen. Entendre la cadena és entendre el servei.

flowchart TB
    C([Client a Europa])
    IP["Adreça IP global anycast<br/>alpinashop-lb-ip"]
    FR["Regla de reenviament<br/>port 443"]
    TP["Proxy HTTPS de destí<br/>+ certificat TLS (03-07)<br/>+ política SSL"]
    UM["Mapa d'URL<br/>alpinashop-url-map"]
    BS["Servei de backend<br/>bs-catalogo-web"]
    BB["Backend de bucket<br/>bb-catalogo-imagenes"]
    HC["Comprovació d'estat<br/>hc-catalogo /salud"]
    MIG["MIG alpinashop-web-mig<br/>2–10 instàncies, 3 zones"]
    GCS[("Bucket alpinashop-catalogo")]

    C --> IP --> FR --> TP --> UM
    UM -->|"/imagenes/*"| BB --> GCS
    UM -->|"resta"| BS --> MIG
    HC -.comprova.-> MIG
    BS -.usa.-> HC

Peça a peça, de fora cap a dins:

Peça Què fa Recurs gcloud
Adreça IP global La IP anycast pública que anuncien els PoP compute addresses --global
Regla de reenviament Uneix IP + port amb un proxy de destí compute forwarding-rules
Proxy de destí Acaba la connexió; en HTTPS, aplica el certificat i la política SSL compute target-https-proxies
Mapa d'URL Decideix, segons host i ruta, quin servei de backend atén compute url-maps
Servei de backend Agrupa backends, defineix política de balanceig, CDN, Cloud Armor, timeouts compute backend-services
Backend El grup real: MIG, NEG o bucket backend-services add-backend
Comprovació d'estat Decideix quines instàncies reben trànsit compute health-checks

Es construeixen de dins cap a fora: primer la comprovació d'estat, després els serveis de backend, després el mapa d'URL, el proxy i finalment la regla de reenviament. Si ho intentes al revés, gcloud es queixa que el recurs referenciat no existeix.

  1. Preparatius: comprovació d'estat i port amb nom

L'aplicació Flask d'AlpinaShop escolta al port 8080 sota gunicorn i exposa una ruta de salut. Abans de res, assegura't que aquesta ruta existeix i és barata:

# app.py — ruta de salut d'AlpinaShop
@app.route("/salud")
def salut():
    """Comprovació d'estat per al balancejador.

    Ha de ser BARATA i RÀPIDA: s'invoca cada pocs segons des de
    diversos comprovadors. No consultis la base de dades aquí tret
    que vulguis que un problema de BD tregui de rotació tota la flota.
    """
    return {"estat": "ok"}, 200

Aquest matís mereix una explicació, perquè és una decisió d'arquitectura i no un detall:

  • Si /salud no consulta la base de dades, un problema de base de dades deixa els backends "sans" i els clients reben errors 500 de l'aplicació. Dolent, però acotat.
  • Si /salud sí que consulta la base de dades, un problema de base de dades marca tots els backends com a no sans alhora, el balancejador es queda sense destins i retorna 502 a tothom. Pitjor: has convertit una fallada parcial en una caiguda total.

La pràctica habitual és tenir dues rutes: /salud superficial (per al balancejador) i /salud/completo profunda (per a la monitoratge de 06-04, que alerta però no treu de rotació).

Ara, la comprovació d'estat i el port amb nom:

gcloud config set project alpinashop-prod

# Comprovacio d'estat HTTP contra /salud al 8080
gcloud compute health-checks create http hc-catalogo \
  --port=8080 \
  --request-path=/salud \
  --check-interval=10s \
  --timeout=5s \
  --healthy-threshold=2 \
  --unhealthy-threshold=3 \
  --description="Salut del cataleg d'AlpinaShop"

# El grup d'instancies ha de declarar un "port amb nom":
# el servei de backend es referira al nom, no al numero.
gcloud compute instance-groups managed set-named-ports alpinashop-web-mig \
  --region=europe-west1 \
  --named-ports=http-catalogo:8080

El port amb nom és una font habitual de confusió. El servei de backend no diu "envia el trànsit al 8080"; diu "envia el trànsit al port anomenat http-catalogo", i cada grup d'instàncies tradueix aquest nom a un número. L'avantatge és que pots canviar el port de l'aplicació sense tocar el balancejador. L'inconvenient és que si oblides definir el port amb nom, el balancejador fa servir l'http per defecte (port 80), no hi troba res i tot apareix com a no saludable.

Paràmetres de la comprovació, explicats:

Paràmetre Valor Significat
--check-interval 10s Cada quant es comprova
--timeout 5s Quant s'espera la resposta abans de comptar-la com a fallada
--healthy-threshold 2 Comprovacions correctes seguides per tornar a rotació
--unhealthy-threshold 3 Fallades seguides per sortir de rotació

Amb aquests valors, una instància trencada triga uns 30 segons a sortir de rotació i una de recuperada uns 20 a tornar. Baixar l'interval detecta abans però genera més càrrega; apujar el llindar de fallada evita expulsions per un pic transitori.

  1. El servei de backend sobre el MIG

El servei de backend és la peça central: defineix com es reparteix el trànsit, quina comprovació es fa servir, quant s'espera una resposta, si hi ha CDN i si hi ha Cloud Armor.

# 1) Crear el servei de backend (esquema EXTERNAL_MANAGED = balancejador global modern)
gcloud compute backend-services create bs-catalogo-web \
  --global \
  --load-balancing-scheme=EXTERNAL_MANAGED \
  --protocol=HTTP \
  --port-name=http-catalogo \
  --health-checks=hc-catalogo \
  --timeout=30s \
  --connection-draining-timeout=60s \
  --description="Backend del cataleg web d'AlpinaShop"

# 2) Afegir el MIG regional com a backend
gcloud compute backend-services add-backend bs-catalogo-web \
  --global \
  --instance-group=alpinashop-web-mig \
  --instance-group-region=europe-west1 \
  --balancing-mode=RATE \
  --max-rate-per-instance=80 \
  --capacity-scaler=1.0

Opcions importants:

  • --load-balancing-scheme=EXTERNAL_MANAGED: és l'esquema del balancejador d'aplicacions extern global actual, basat en el proxy Envoy de Google. El valor antic EXTERNAL correspon al balancejador clàssic. Fes servir sempre EXTERNAL_MANAGED per a desenvolupaments nous: admet encaminament avançat, reintents, reescriptura de capçaleres i mirall de trànsit.
  • --timeout=30s: quant espera el balancejador la resposta completa del backend. Si l'aplicació té alguna operació lenta (una exportació de catàleg, per exemple), apuja'l o mou aquesta operació a un procés asíncron. Un 502 amb backend_timeout als registres és exactament això.
  • --connection-draining-timeout=60s: quan una instància surt del grup (per autoescalat o per una actualització), el balancejador deixa d'enviar-li peticions noves però li dona 60 segons per acabar les que té en curs. Sense drenatge, cada reducció de l'autoescalat talla connexions vives.
  • --balancing-mode=RATE amb --max-rate-per-instance=80: cada instància es considera "plena" a 80 peticions per segon. Ho desenvolupem a l'apartat 10.

  1. El backend de bucket per a les imatges

Els 60 GB d'imatges del catàleg no han de passar per l'aplicació Flask. Un backend de bucket connecta el balancejador directament amb alpinashop-catalogo: Google serveix l'objecte sense que cap instància hi intervingui.

gcloud compute backend-buckets create bb-catalogo-imagenes \
  --gcs-bucket-name=alpinashop-catalogo \
  --description="Imatges de producte servides directament des de Cloud Storage"

Avantatges davant de servir-les des de l'aplicació:

Aspecte Des de Flask Des de backend de bucket
Consum de CPU de les VM Alt (cada imatge ocupa un worker) Zero
Escalat Limitat pel MIG Il·limitat, és Cloud Storage
Memòria cau a CDN Requereix configurar capçaleres a l'aplicació S'activa amb una opció (03-03)
Cost Instàncies més grans Només emmagatzematge i egress
Latència Un salt extra Directe

Perquè el bucket sigui servible pel balancejador, els seus objectes han de ser llegibles públicament o bé accessibles mitjançant galetes signades (que es veuen a 03-03). Les imatges de producte d'AlpinaShop són públiques per naturalesa —són a la botiga—, així que:

# Lectura publica NOMES del prefix de les imatges web i miniatures.
# Els originals d'alta resolucio NO han de ser publics.
gcloud storage buckets add-iam-policy-binding gs://alpinashop-catalogo \
  --member=allUsers \
  --role=roles/storage.objectViewer \
  --condition='expression=resource.name.startsWith("projects/_/buckets/alpinashop-catalogo/objects/productos/") && (resource.name.contains("/web/") || resource.name.contains("/thumb/")),title=solo-web-y-thumb,description=Public nomes el contingut optimitzat per a la botiga'

Fixa't en la condició IAM: fa pública només la part web/ i thumb/ del conveni de rutes que vam fixar a 02-02, deixant original/ privat. Les condicions d'IAM s'expliquen a 03-04; aquí serveixen per no exposar els originals d'alta resolució per descuit.

  1. El mapa d'URL: encaminar per ruta

El mapa d'URL és el cervell del balancejador: mira el host i la ruta de cada petició i decideix quin backend l'atén.

# 1) Crear el mapa amb el servei per defecte (l'aplicacio)
gcloud compute url-maps create alpinashop-url-map \
  --default-service=bs-catalogo-web \
  --description="Encaminament del lloc d'AlpinaShop"

# 2) Afegir el matcher de rutes que desvia /imagenes/* al bucket
gcloud compute url-maps add-path-matcher alpinashop-url-map \
  --path-matcher-name=matcher-principal \
  --default-service=bs-catalogo-web \
  --backend-bucket-path-rules="/imagenes/*=bb-catalogo-imagenes,/estatico/*=bb-catalogo-imagenes" \
  --new-hosts="alpinashop.example,www.alpinashop.example"

El resultat es pot veure i editar com a YAML, que és la manera més clara de raonar-hi:

gcloud compute url-maps describe alpinashop-url-map --format=yaml
name: alpinashop-url-map
defaultService: .../backendServices/bs-catalogo-web
hostRules:
- hosts:
  - alpinashop.example
  - www.alpinashop.example
  pathMatcher: matcher-principal
pathMatchers:
- name: matcher-principal
  defaultService: .../backendServices/bs-catalogo-web
  pathRules:
  - paths:
    - /imagenes/*
    service: .../backendBuckets/bb-catalogo-imagenes
  - paths:
    - /estatico/*
    service: .../backendBuckets/bb-catalogo-imagenes

Com s'avalua una petició, amb exemples reals d'AlpinaShop:

Petició Regla que s'aplica Backend
https://alpinashop.example/ defaultService del matcher bs-catalogo-web
https://alpinashop.example/producto/moc-40 defaultService del matcher bs-catalogo-web
https://alpinashop.example/imagenes/productos/moc-40/web/frontal-800.webp /imagenes/* bb-catalogo-imagenes
https://alpinashop.example/estatico/css/tienda.css /estatico/* bb-catalogo-imagenes
https://otrodominio.example/ Cap hostRule no coincideix defaultService del mapa

Tres detalls que estalvien hores de depuració:

  • La coincidència de rutes és per prefix amb * al final, no una expressió regular. /imagenes/* coincideix amb tot el que comença per /imagenes/.
  • La ruta no es reescriu per defecte. Una petició a /imagenes/productos/moc-40/web/frontal-800.webp busca al bucket l'objecte imagenes/productos/moc-40/web/frontal-800.webp, amb el prefix imagenes/ inclòs. Com que el nostre conveni de 02-02 desa els objectes a productos/<sku>/web/..., sense aquest prefix, cal reescriure:
# Exportar, editar i tornar a importar el mapa per afegir urlRewrite
gcloud compute url-maps export alpinashop-url-map \
  --destination=/tmp/url-map.yaml --global
# Fragment a afegir dins de la pathRule de /imagenes/*
  pathRules:
  - paths:
    - /imagenes/*
    service: .../backendBuckets/bb-catalogo-imagenes
    routeAction:
      urlRewrite:
        pathPrefixRewrite: /
gcloud compute url-maps import alpinashop-url-map \
  --source=/tmp/url-map.yaml --global --quiet

Amb pathPrefixRewrite: /, la petició /imagenes/productos/moc-40/web/frontal-800.webp es converteix en /productos/moc-40/web/frontal-800.webp abans d'arribar al bucket, que és exactament la ruta de l'objecte.

  • gcloud compute url-maps validate comprova el mapa abans d'aplicar-lo, incloent-hi proves d'encaminament declarades al mateix YAML. És molt recomanable en un pipeline (06-01).

  1. Proxy de destí i regla de reenviament

Les dues últimes peces. Aquí muntem primer la versió HTTP per validar que tot funciona; el certificat i el proxy HTTPS són de 03-07, però deixem ja escrita la forma final.

# --- Versio HTTP (per validar la cadena abans de tenir certificat) ---
gcloud compute target-http-proxies create alpinashop-http-proxy \
  --url-map=alpinashop-url-map

gcloud compute forwarding-rules create alpinashop-fr-http \
  --global \
  --address=alpinashop-lb-ip \
  --target-http-proxy=alpinashop-http-proxy \
  --ports=80 \
  --load-balancing-scheme=EXTERNAL_MANAGED
# --- Versio HTTPS (es completa a 03-07, amb el certificat gestionat) ---
# gcloud compute target-https-proxies create alpinashop-https-proxy \
#   --url-map=alpinashop-url-map \
#   --ssl-certificates=alpinashop-cert \
#   --ssl-policy=alpinashop-ssl-policy
#
# gcloud compute forwarding-rules create alpinashop-fr-https \
#   --global --address=alpinashop-lb-ip \
#   --target-https-proxy=alpinashop-https-proxy \
#   --ports=443 --load-balancing-scheme=EXTERNAL_MANAGED

Comprovació d'extrem a extrem:

IP=$(gcloud compute addresses describe alpinashop-lb-ip --global --format="value(address)")
echo "IP del balancejador: $IP"

# L'aplicacio
curl -s -o /dev/null -w "%{http_code} %{time_total}s\n" "http://$IP/"

# Una imatge des del bucket
curl -s -o /dev/null -w "%{http_code}\n" "http://$IP/imagenes/productos/moc-40/web/frontal-800.webp"

# Estat dels backends
gcloud compute backend-services get-health bs-catalogo-web --global

Tingues paciència: un balancejador global acabat de crear triga entre 5 i 10 minuts a propagar-se per tots els punts de presència. Durant aquest temps és normal rebre 404 o 502. Si al cap de quinze minuts continua fallant, aleshores sí que hi ha un problema real.

  1. Comprovacions d'estat en profunditat: per què fallen

Aquest apartat és el més rendible de la lliçó. La majoria dels balancejadors trencats no estan mal construïts: tenen els backends en UNHEALTHY.

Primer punt, i el que més vegades és la causa: les comprovacions d'estat no s'originen a internet ni a la teva subxarxa. Google les llança des de dos rangs fixos:

  • 130.211.0.0/22
  • 35.191.0.0/16

Si el tallafoc de VPC no els permet, la comprovació no arriba mai, la instància es marca com a no saludable i el balancejador retorna 502 Server Error sense dir per què. Ja vam crear la regla a 03-01 (fw-allow-lb-health-web), però convé saber verificar-la:

gcloud compute firewall-rules list \
  --filter="network:alpinashop-vpc AND sourceRanges:(130.211.0.0/22 OR 35.191.0.0/16)" \
  --format="table(name, allowed[].map().firewall_rule().list(), targetTags.list(), disabled)"

Llista de comprovació completa quan un backend està UNHEALTHY:

Núm. Causa Com confirmar-la Solució
1 El tallafoc no permet 130.211.0.0/22 i 35.191.0.0/16 La comanda anterior no retorna res Crear la regla
2 L'etiqueta de xarxa de la regla no és a les instàncies gcloud compute instances list --format="table(name,tags.items.list())" Afegir l'etiqueta a la plantilla del MIG
3 Port amb nom mal definit gcloud compute instance-groups managed describe alpinashop-web-mig --region=europe-west1 --format="value(namedPorts)" set-named-ports
4 La ruta /salud no existeix o retorna 301/302 curl -i http://IP_INTERNA:8080/salud des d'una altra VM Corregir l'aplicació; la comprovació exigeix 200, no segueix redireccions
5 L'aplicació escolta a 127.0.0.1 en lloc de 0.0.0.0 ss -tlnp dins de la VM gunicorn --bind 0.0.0.0:8080
6 /salud consulta la base de dades i aquesta va lenta Latència de /salud més gran que el timeout Fer /salud superficial
7 La instància encara està arrencant Acabada de crear per l'autoescalat Ajustar --initial-delay del MIG
8 Protocol equivocat (HTTPS a la comprovació, HTTP a l'aplicació) describe de la health check Fer servir http

Diagnòstic des de dins d'una VM, sense IP pública, gràcies a IAP:

gcloud compute ssh alpinashop-web-1 --zone=europe-west1-b --tunnel-through-iap

# Dins de la VM
curl -i http://localhost:8080/salud        # respon l'aplicacio?
ss -tlnp | grep 8080                       # a quina interficie escolta?
sudo journalctl -u gunicorn -n 50 --no-pager

I la vista agregada, que diu quina instància concreta està malament:

gcloud compute backend-services get-health bs-catalogo-web --global \
  --format="table(status.healthStatus[].instance, status.healthStatus[].healthState)"

Hi ha dos tipus de comprovació a la plataforma i convé no confondre'ls:

Tipus Per a què serveix Efecte d'una fallada
Comprovació del balancejador Decidir si una instància rep trànsit Surt de rotació
Comprovació d'autoreparació del MIG Decidir si una instància es recrea Es destrueix i se'n crea una altra

Si fas servir la mateixa comprovació agressiva per a totes dues coses, una fallada transitòria de l'aplicació pot provocar que el MIG destrueixi tota la flota en cadena. La pràctica recomanada és que la comprovació d'autoreparació sigui més laxa (llindar de fallada més alt i ruta més simple) que la del balancejador.

  1. Polítiques de balanceig, capacitat i afinitat de sessió

Mode de balanceig

Cada backend declara quan es considera ple. Hi ha tres modes:

Mode Mètrica Quan fer-lo servir
RATE Peticions per segon Aplicacions web amb peticions homogènies. El d'AlpinaShop
UTILIZATION Ús de CPU del grup Càrregues amb cost de CPU molt variable per petició
CONNECTION Connexions simultànies Balancejadors de capa 4 i connexions llargues

Per a AlpinaShop, RATE amb --max-rate-per-instance=80 significa: "cada instància es dona per plena a 80 req/s". Aquest número no s'inventa; es mesura. La manera correcta d'obtenir-lo:

  1. Llançar una prova de càrrega contra una sola instància.
  2. Apujar la taxa fins que la latència del percentil 95 es degradi o apareguin errors.
  3. Prendre entre el 60 % i el 70 % d'aquesta xifra com a max-rate-per-instance, deixant marge per a pics.

Si el número és massa alt, el balancejador satura les instàncies abans de desbordar cap a altres regions. Si és massa baix, l'autoescalat afegeix màquines innecessàries i apuja el cost.

capacity-scaler: la palanca d'operació més útil

--capacity-scaler és un multiplicador entre 0.0 i 1.0 sobre la capacitat declarada. Serveix per buidar un backend sense esborrar-lo:

# Drenar el backend a la meitat (per exemple, durant un desplegament)
gcloud compute backend-services update-backend bs-catalogo-web \
  --global \
  --instance-group=alpinashop-web-mig \
  --instance-group-region=europe-west1 \
  --capacity-scaler=0.5

# Buidar-lo del tot (deixa de rebre transit nou, respecta el drenatge)
gcloud compute backend-services update-backend bs-catalogo-web \
  --global --instance-group=alpinashop-web-mig \
  --instance-group-region=europe-west1 \
  --capacity-scaler=0.0

És la manera neta de fer un desplegament blau/verd o de treure de servei una regió amb problemes.

Afinitat de sessió

Per defecte no hi ha afinitat: cada petició pot anar a una instància diferent. Això és el correcte per a una aplicació sense estat, que és com s'ha de dissenyar.

Tipus d'afinitat Basada en Cost
NONE (per defecte) Res Repartiment òptim
CLIENT_IP IP del client Mal repartiment després d'un NAT corporatiu
GENERATED_COOKIE Galeta que genera el balancejador Repartiment acceptable
HEADER_FIELD Una capçalera concreta Requereix control del client

AlpinaShop no necessita afinitat, i és important entendre per què: la cistella i la sessió viuen a Firestore (decidit a 02-06), no a la memòria de la instància. L'afinitat és un pedaç per a aplicacions amb estat local, i sempre degrada el repartiment: una instància pot quedar sobrecarregada mentre unes altres estan ocioses, i l'autoescalat no la pot alleujar perquè els clients ja hi estan enganxats.

Altres opcions del servei de backend

# Politica de reintents i deteccio de valors atipics (nomes EXTERNAL_MANAGED)
gcloud compute backend-services update bs-catalogo-web --global \
  --custom-request-header="X-Cliente-Region:{client_region}" \
  --custom-response-header="X-Cache-Estado:{cdn_cache_status}"

Les capçaleres personalitzades amb variables ({client_region}, {client_city}, {cdn_cache_status}) són molt útils: permeten que l'aplicació sàpiga des de quin país entra el client sense geolocalitzar res, i que puguis depurar la memòria cau mirant una capçalera de resposta (ho farem servir a 03-03).

  1. Redirecció d'HTTP a HTTPS

Servir la botiga per HTTP sense xifrar no és acceptable. La redirecció es fa al mateix balancejador, no a l'aplicació, perquè ni un sol byte de contingut viatgi en clar.

S'implementa amb un mapa d'URL específic que no té backends, només una acció de redirecció:

# Mapa d'URL que nomes redirigeix
gcloud compute url-maps import alpinashop-redir-map --global --quiet --source=- <<'EOF'
name: alpinashop-redir-map
defaultUrlRedirect:
  httpsRedirect: true
  redirectResponseCode: MOVED_PERMANENTLY_DEFAULT
  stripQuery: false
EOF

# El proxy HTTP apunta a aquest mapa en lloc del mapa real
gcloud compute target-http-proxies update alpinashop-http-proxy \
  --url-map=alpinashop-redir-map

Explicació de les opcions:

  • httpsRedirect: true: canvia l'esquema a https conservant host i ruta.
  • redirectResponseCode: MOVED_PERMANENTLY_DEFAULT: és un 301. Els navegadors el desen a la memòria cau, així que les visites següents ni tan sols fan la petició en clar. Si estàs provant i no vols que es quedi enganxat al navegador, fes servir FOUND (302) mentrestant.
  • stripQuery: false: conserva la cadena de consulta. Posar-lo a true trencaria els enllaços de campanya amb paràmetres utm_*.

La capçalera HSTS, que fa que el navegador ni tan sols intenti HTTP, l'emet l'aplicació i s'explica a 03-07.

  1. Escalat global: per què no cal "preescalfar"

Qui ve d'altres proveïdors està acostumat a "preescalfar" el balancejador abans d'una campanya: avisar el proveïdor que arribarà trànsit perquè ampliï la capacitat del balancejador. A Google Cloud això no existeix i no cal.

El motiu és arquitectònic:

  • El balancejador global no és un conjunt d'instàncies teves. És una funció de la infraestructura de xarxa de Google, la mateixa que atén Cerca, YouTube i Gmail. La seva capacitat no està dimensionada per a tu.
  • La IP anycast s'anuncia des de tots els PoP simultàniament. Un pic de trànsit no arriba a un punt, sinó que es reparteix geogràficament per construcció.
  • El balancejador absorbeix a més els atacs volumètrics de capa 3 i 4 sense que tu configuris res, perquè la mitigació és a la mateixa xarxa.

El que sí que cal dimensionar és el que hi ha darrere: el MIG alpinashop-web-mig està configurat per a 2–10 instàncies, i si la campanya de tardor en necessita més, el límit s'apuja a l'autoescalat. El coll d'ampolla mai no serà el balancejador; serà la teva aplicació o la teva base de dades.

Conseqüència pràctica per a l'equip d'AlpinaShop: abans de la campanya, la llista de comprovació no inclou "avisar el balancejador", sinó apujar max-num-replicas del MIG, revisar max-rate-per-instance amb dades reals i comprovar que la rèplica de lectura de Cloud SQL aguanta.

  1. Balancejadors interns per a serveis interns

No tot el trànsit és públic. Quan AlpinaShop tingui serveis interns —el motor de recomanacions, l'API d'estoc del magatzem, el generador d'informes de la Lucía— aquests serveis no han de tenir IP pública. La peça correcta és un balancejador intern:

# Balancejador d'aplicacions intern (capa 7) per a un servei intern
gcloud compute health-checks create http hc-stock-interno \
  --port=8080 --request-path=/salud --region=europe-west1

gcloud compute backend-services create bs-stock-interno \
  --region=europe-west1 \
  --load-balancing-scheme=INTERNAL_MANAGED \
  --protocol=HTTP \
  --health-checks=hc-stock-interno \
  --health-checks-region=europe-west1

# Subxarxa de nomes proxy: obligatoria per als balancejadors interns de capa 7.
# Es un rang reservat del qual prenen IP els proxies gestionats per Google.
gcloud compute networks subnets create sn-proxy-euw1 \
  --network=alpinashop-vpc \
  --region=europe-west1 \
  --range=10.10.10.0/24 \
  --purpose=REGIONAL_MANAGED_PROXY \
  --role=ACTIVE

Dues coses noves i un advertiment:

  • L'esquema és INTERNAL_MANAGED, no EXTERNAL_MANAGED.
  • Els balancejadors interns de capa 7 necessiten una subxarxa de només proxy (--purpose=REGIONAL_MANAGED_PROXY) per regió. És un rang que no allotja VM teves: el fan servir els proxies de Google. Sense ella, la creació falla amb un missatge que no sempre és evident. Al pla d'adreçament de 03-01 cal reservar-li lloc: aquí hem fet servir 10.10.10.0/24, dins del bloc lliure previst.
  • Regles de tallafoc: el trànsit arriba des del rang de la subxarxa de només proxy, no des del client original. Cal permetre 10.10.10.0/24 cap als backends.

Quan fer servir cadascun:

Servei d'AlpinaShop Balancejador
Catàleg públic Aplicacions extern global (el que hem construït)
API d'estoc consumida només per la botiga Aplicacions intern
Base de dades de sessió amb protocol propi Xarxa de pas a través intern

  1. NEG sense servidor: Cloud Run darrere del mateix balancejador

A 02-07 vam decidir (DA-001) que el catàleg acabarà a Cloud Run. Cloud Run ja dona una URL HTTPS pròpia, així que per a què posar-li un balancejador al davant? Per quatre motius que ja coneixes:

  1. Domini propi i certificat gestionat unificats (03-07).
  2. Cloud CDN sobre el contingut de l'aplicació (03-03).
  3. Cloud Armor: la URL nativa de Cloud Run no es pot protegir amb WAF; la del balancejador sí (03-05).
  4. Un únic mapa d'URL que reparteix entre Cloud Run, el bucket i el que vingui.

La peça que ho fa possible és el NEG sense servidor (serverless network endpoint group): un backend que no apunta a màquines, sinó a un servei gestionat.

# NEG sense servidor que apunta al futur servei Cloud Run alpinashop-web
gcloud compute network-endpoint-groups create neg-alpinashop-web \
  --region=europe-west1 \
  --network-endpoint-type=serverless \
  --cloud-run-service=alpinashop-web

# Servei de backend que el fa servir (sense comprovacio d'estat: la gestiona Cloud Run)
gcloud compute backend-services create bs-alpinashop-run \
  --global \
  --load-balancing-scheme=EXTERNAL_MANAGED

gcloud compute backend-services add-backend bs-alpinashop-run \
  --global \
  --network-endpoint-group=neg-alpinashop-web \
  --network-endpoint-group-region=europe-west1

I aleshores la migració del MIG a Cloud Run és un canvi al mapa d'URL, no una reconstrucció:

# Enviar nomes /nuevo/* a Cloud Run per provar en produccio amb transit real
gcloud compute url-maps add-path-matcher alpinashop-url-map \
  --path-matcher-name=matcher-migracion \
  --default-service=bs-catalogo-web \
  --path-rules="/nuevo/*=bs-alpinashop-run" \
  --new-hosts="alpinashop.example"

Això és el que fa que el treball d'avui no es llenci a les escombraries demà: el balancejador sobreviu al canvi de backend. La IP, el domini, el certificat, les regles de Cloud Armor i la configuració de CDN es mantenen; només canvia cap a on apunta el mapa. Els detalls de Cloud Run són de 07-02.

Existeixen NEG de diversos tipus, i convé tenir-los ubicats:

Tipus de NEG Apunta a Ús
Zonal (GCE_VM_IP_PORT) IP:port de VM o pods GKE amb Ingress/Gateway natiu
Sense servidor Cloud Run, Cloud Functions, App Engine El cas de dalt
Internet (INTERNET_FQDN_PORT) Un servei extern per nom Posar CDN o Armor davant d'un origen aliè
Híbrid Un servidor local Migracions (07-03)

  1. Registre i mètriques del balancejador

Sense observabilitat, un balancejador és una caixa negra. Activa'l des del principi:

gcloud compute backend-services update bs-catalogo-web \
  --global \
  --enable-logging \
  --logging-sample-rate=1.0

--logging-sample-rate=1.0 registra el 100 % de les peticions. En un lloc amb trànsit alt es baixa a 0.1 per contenir el cost de registres; a AlpinaShop, amb pics de 20 req/s, el 100 % és perfectament assumible i facilita molt el diagnòstic.

Consultes útils:

# Peticions que van acabar en 5xx a l'ultima hora
gcloud logging read '
  resource.type="http_load_balancer"
  AND httpRequest.status>=500
' --project=alpinashop-prod --limit=20 --freshness=1h \
  --format="table(
    timestamp,
    httpRequest.requestUrl,
    httpRequest.status,
    jsonPayload.statusDetails
  )"

El camp jsonPayload.statusDetails és el més valuós del registre del balancejador. Tradueix el 502 genèric a una causa concreta:

statusDetails Significat Què mirar
failed_to_pick_backend Cap backend saludable Comprovacions d'estat (apartat 9)
backend_timeout El backend no ha respost a temps --timeout i el rendiment de l'aplicació
backend_connection_closed_before_data_sent_to_client El backend ha tallat la connexió Keepalive de gunicorn menor que el del balancejador
response_sent_by_backend Tot normal El codi l'ha retornat l'aplicació
denied_by_security_policy Bloquejat per Cloud Armor 03-05
client_disconnected_before_any_response El client se n'ha anat Normal en trànsit mòbil

El cas de backend_connection_closed_before_data_sent_to_client mereix un apunt perquè és subtil i molt comú: el balancejador global manté connexions persistents amb el backend durant 10 minuts. Si gunicorn tanca la connexió abans (el seu keepalive per defecte són 2 segons), el balancejador es troba la connexió tancada a mitges i retorna 502 esporàdics, impossibles de reproduir a mà. La solució és configurar gunicorn amb un keepalive superior al del balancejador:

gunicorn --bind 0.0.0.0:8080 --workers 4 --keep-alive 620 app:app

Mètriques recomanades per al tauler de la Marta (es construeix a 06-04):

Mètrica Per a què
loadbalancing.googleapis.com/https/request_count Volum, per codi de resposta
loadbalancing.googleapis.com/https/total_latencies Latència d'extrem a extrem (p50, p95, p99)
loadbalancing.googleapis.com/https/backend_latencies Latència només del backend: separa el problema
loadbalancing.googleapis.com/https/backend_request_count Peticions que van arribar al backend (davant de les servides per CDN)

La diferència entre total_latencies i backend_latencies és la que diu si el problema és teu o de la xarxa: si la total puja i la del backend no, el problema és al trajecte fins al client.

Errors habituals i consells

  • Oblidar el tallafoc de 130.211.0.0/22 i 35.191.0.0/16. És la causa número u d'un balancejador que retorna 502 acabat de crear. Abans de depurar res més, comprova aquesta regla.
  • No definir el port amb nom al MIG. El servei de backend fa servir --port-name, i si el grup no el declara, el trànsit va al port 80 i no hi respon res.
  • Impacientar-se. Un balancejador global triga entre 5 i 10 minuts a propagar-se. Els primers 404 i 502 són normals.
  • Que /salud consulti la base de dades. Converteix una fallada parcial en una caiguda total: tots els backends es marquen com a no saludables alhora.
  • Fer servir la mateixa comprovació per al balancejador i per a l'autoreparació del MIG. Una fallada transitòria pot provocar que el grup destrueixi i recreï tota la flota.
  • Confondre EXTERNAL amb EXTERNAL_MANAGED. Per a desenvolupaments nous fes servir EXTERNAL_MANAGED; barrejar-los entre el servei de backend i la regla de reenviament produeix errors de validació confusos.
  • Oblidar l'urlRewrite del backend de bucket. Si el mapa envia /imagenes/* al bucket sense reescriure, es busca un objecte el nom del qual comença per imagenes/ i s'obté un 404 sistemàtic.
  • Activar l'afinitat de sessió "per si de cas". Degrada el repartiment i amaga un problema de disseny. Si l'aplicació necessita afinitat, el que cal arreglar és l'estat, no el balancejador.
  • max-rate-per-instance posat a ull. Mesura'l amb una prova de càrrega sobre una instància i aplica un marge del 30-40 %.
  • No activar el registre del balancejador. Sense statusDetails estàs depurant a cegues.
  • keepalive de gunicorn massa baix. Provoca 502 esporàdics irreproduïbles. Posa'l per damunt dels 600 segons del balancejador.
  • No esborrar els recursos d'una prova. La cadena té set objectes; s'esborren en ordre invers al de creació. Les IP estàtiques òrfenes se segueixen facturant.

Exercicis

Exercici 1 — Triar el balancejador correcte

Per a cada escenari d'AlpinaShop, indica quin balancejador correspon i justifica-ho en una línia:

  1. La botiga pública alpinashop.example, amb clients a tot Europa, HTTPS, memòria cau d'imatges i WAF.
  2. Una API interna d'estoc que només consumeixen les instàncies del catàleg dins d'alpinashop-vpc.
  3. Un servei de sincronització amb el magatzem que parla un protocol propi sobre TCP al port 9000 i necessita veure la IP real del client.
  4. Un tauler intern d'informes per a la Lucía que encamina /informes/* a un servei i /exportar/* a un altre, accessible només des de la xarxa interna.

Exercici 2 — Depurar un balancejador trencat

La Marta ha construït el balancejador seguint la lliçó, però curl http://$IP/ retorna 502 Server Error i gcloud compute backend-services get-health bs-catalogo-web --global mostra totes les instàncies en UNHEALTHY. Se sap que:

  • Des d'una altra VM de sn-web-euw1, curl http://10.10.0.7:8080/salud retorna 200 OK.
  • La regla fw-allow-lb-health-web existeix i permet tcp:80,tcp:8080 des dels rangs correctes cap a l'etiqueta web-catalogo.

Escriu la seqüència de comandes de diagnòstic i la causa més probable.

Exercici 3 — Ampliar el mapa d'URL

AlpinaShop vol tres coses noves:

  1. Que blog.alpinashop.example vagi a un servei de backend diferent anomenat bs-blog.
  2. Que /api/* tingui un temps d'espera de 120 segons perquè hi ha una exportació lenta, sense canviar el de la resta del lloc.
  3. Que /descargas/manual-mochilas.pdf redirigeixi de forma permanent a /imagenes/documentos/manual-mochilas-2026.pdf.

Indica quins recursos cal crear o modificar per a cada punt i escriu el fragment de YAML del mapa d'URL per al punt 3.


Solucions

Solució 1

  1. Balancejador d'aplicacions extern global. És trànsit HTTP públic mundial, i és l'únic que admet Cloud CDN i Cloud Armor amb IP anycast global.
  2. Balancejador d'aplicacions intern (INTERNAL_MANAGED), regional a europe-west1. No ha de tenir IP pública i l'encaminament per ruta pot ser útil més endavant. Requereix subxarxa de només proxy.
  3. Balancejador de xarxa de pas a través extern (capa 4, regional). És l'únic que no acaba la connexió i per tant conserva la IP d'origen real; a més, el protocol no és HTTP, així que un balancejador de capa 7 no serveix.
  4. Balancejador d'aplicacions intern. Necessita encaminament per ruta (capa 7) i no ha de ser accessible des d'internet. Si a més calgués accés des de fora de l'oficina, l'alternativa correcta seria el balancejador extern global protegit amb IAP (03-04), no obrir-lo.

Solució 2

# 1) Estan les instancies etiquetades com a web-catalogo?
gcloud compute instances list \
  --filter="name~alpinashop-web-mig" \
  --format="table(name, zone, status, tags.items.list())"

# 2) Te el MIG definit el port amb nom?
gcloud compute instance-groups managed describe alpinashop-web-mig \
  --region=europe-west1 \
  --format="value(namedPorts)"

# 3) A quin port i ruta apunta la comprovacio?
gcloud compute health-checks describe hc-catalogo \
  --format="yaml(type, httpHealthCheck)"

# 4) Quin port-name fa servir el servei de backend?
gcloud compute backend-services describe bs-catalogo-web --global \
  --format="value(portName, protocol, timeoutSec)"

Causa més probable: falta el port amb nom. El tallafoc està bé i l'aplicació respon al 8080 des de dins de la subxarxa, així que el paquet de comprovació sí que pot arribar. El que passa és que el servei de backend demana el port anomenat http-catalogo i el grup d'instàncies no el té definit, amb la qual cosa el balancejador dirigeix la comprovació i el trànsit al port per defecte (80), on no hi ha res escoltant.

gcloud compute instance-groups managed set-named-ports alpinashop-web-mig \
  --region=europe-west1 \
  --named-ports=http-catalogo:8080

La segona causa candidata seria que la plantilla d'instància del MIG no apliqui l'etiqueta web-catalogo a les màquines noves: la regla existiria però no s'aplicaria a ningú. Ho confirma la comanda 1.

Solució 3

Punt 1 — blog.alpinashop.example. Es crea el servei de backend bs-blog i s'afegeix al mapa una hostRule nova amb el seu propi pathMatcher. A més cal incloure el nom al certificat gestionat (03-07) i crear el registre DNS corresponent.

gcloud compute url-maps add-path-matcher alpinashop-url-map \
  --path-matcher-name=matcher-blog \
  --default-service=bs-blog \
  --new-hosts=blog.alpinashop.example

Punt 2 — temps d'espera de /api/*. El timeout és una propietat del servei de backend, no de la regla de ruta. Per tant cal crear un segon servei de backend (per exemple bs-catalogo-api) apuntant al mateix MIG però amb --timeout=120s, i encaminar /api/* cap a ell.

gcloud compute backend-services create bs-catalogo-api \
  --global --load-balancing-scheme=EXTERNAL_MANAGED \
  --protocol=HTTP --port-name=http-catalogo \
  --health-checks=hc-catalogo --timeout=120s

gcloud compute backend-services add-backend bs-catalogo-api \
  --global --instance-group=alpinashop-web-mig \
  --instance-group-region=europe-west1 \
  --balancing-mode=RATE --max-rate-per-instance=80

Punt 3 — redirecció permanent. Es fa al mateix mapa d'URL, sense tocar l'aplicació:

pathMatchers:
- name: matcher-principal
  defaultService: .../backendServices/bs-catalogo-web
  pathRules:
  - paths:
    - /descargas/manual-mochilas.pdf
    urlRedirect:
      pathRedirect: /imagenes/documentos/manual-mochilas-2026.pdf
      redirectResponseCode: MOVED_PERMANENTLY_DEFAULT
      stripQuery: true

Nota: una pathRule amb urlRedirect no porta service. I compte amb l'ordre: les rutes més específiques han de poder guanyar les genèriques; el balancejador aplica la coincidència més llarga, així que /descargas/manual-mochilas.pdf guanya /descargas/*.

Conclusió

AlpinaShop ja no depèn d'una màquina. Has entès el catàleg de balancejadors de Google Cloud reduint-lo a tres preguntes —capa 7 o capa 4, extern o intern, global o regional— i saps que per a trànsit web públic la resposta gairebé sempre és el balancejador d'aplicacions extern global, amb la distinció clara entre proxy i pas a través.

Has construït la cadena completa de dins cap a fora: la comprovació hc-catalogo contra /salud al 8080, el port amb nom http-catalogo al MIG, el servei de backend bs-catalogo-web amb drenatge de connexions i mode RATE a 80 req/s per instància, el backend de bucket bb-catalogo-imagenes que serveix les imatges d'alpinashop-catalogo sense tocar l'aplicació, el mapa alpinashop-url-map que envia /imagenes/* i /estatico/* al bucket amb pathPrefixRewrite i la resta al MIG, i la regla de reenviament sobre la IP global alpinashop-lb-ip que vam reservar a 03-01.

Has après a diagnosticar l'única cosa que es trenca de veritat: les comprovacions d'estat, amb la seva llista de vuit causes encapçalada pels rangs 130.211.0.0/22 i 35.191.0.0/16, i la distinció entre la comprovació del balancejador i la d'autoreparació del MIG. Saps ajustar la capacitat amb max-rate-per-instance mesurat, buidar un backend amb capacity-scaler, i per què AlpinaShop no necessita afinitat de sessió: l'estat viu a Firestore, no a les instàncies. Has redirigit HTTP a HTTPS a la vora, has vist per què no cal preescalfar res de cara a la campanya de tardor, on encaixen els balancejadors interns i la seva subxarxa de només proxy, i com un NEG sense servidor permetrà moure el catàleg a Cloud Run (DA-001) canviant només el mapa d'URL, conservant IP, domini, certificat, memòria cau i WAF. I has activat el registre, que converteix un 502 opac en un statusDetails accionable.

Queda una ineficiència evident. Cada vegada que un client de Lisboa obre una fitxa de producte, les imatges viatgen des d'europe-west1 fins a Portugal, encara que siguin els mateixos bytes que es van enviar fa un minut a un altre client. Amb 60 GB de catàleg i clients repartits per tot Europa, això és latència innecessària i una factura de sortida de dades que creix amb cada visita. A la lliçó següent, 03-03, Cloud CDN, activem la memòria cau sobre el mateix servei de backend que acabem de construir: les imatges passaran a servir-se des del punt de presència més proper al client, aprendràs a controlar què es desa a la memòria cau i durant quant de temps, a evitar que els paràmetres utm_* de les campanyes arruïnin la ràtio d'encerts, i a mesurar l'estalvi real en latència i en euros.

Curs de Google Cloud Platform (GCP)

Mòdul 1: Introducció a Google Cloud Platform

Mòdul 2: Serveis principals de GCP

Mòdul 3: Xarxes i seguretat

Mòdul 4: Dades i anàlisi

Mòdul 5: Aprenentatge automàtic i IA

Mòdul 6: DevOps i monitoratge

Mòdul 7: Temes avançats de GCP

Mòdul 8: Projecte final

© Copyright 2026. Tots els drets reservats