Tens els quatre serveis declarats i domines la CLI. Falta allò que de debò converteix quatre contenidors en una aplicació: com es troben entre ells, què pot parlar amb què, i en quin ordre han d'estar preparats.

En aquesta lliçó muntes la pila definitiva d'Aurora Libros: DNS intern, dues xarxes segmentades perquè la web no pugui tocar la base de dades, arrencada coordinada amb sondes de salut reals, un servei de migració que corre una vegada i acaba, i el patró de reintent que cal encara que tot l'anterior estigui bé. Al final, els quinze passos d'onboarding de la lliçó 01-07 s'hauran quedat en dos.

Contingut

  1. L'arquitectura final
  2. Com es troben els serveis: el DNS de Compose
  3. Qui parla amb qui i per quin port
  4. Segmentació en dues xarxes
  5. Demostració: la web no arriba a la base de dades
  6. L'ordre d'arrencada: per què depends_on a seques no n'hi ha prou
  7. Sondes de salut reals i condition: service_healthy
  8. Un servei que corre una vegada: service_completed_successfully
  9. Per què l'aplicació ha de reintentar igualment
  10. El compose.yaml complet i comentat
  11. Posada en marxa end-to-end
  12. El recompte de l'onboarding

  1. L'arquitectura final

graph TB
    U["Navegador<br/>localhost:8080"] --> W
    subgraph FRONTAL["xarxa frontal (bridge)"]
        W["aurora-web<br/>nginx:alpine<br/>proxy invers"]
        A1["aurora-api"]
    end
    subgraph POSTERIOR["xarxa posterior (internal: true)"]
        A2["aurora-api<br/>node:22-alpine :3000"]
        D["aurora-db<br/>postgres:16 :5432"]
        C["aurora-cache<br/>redis:7 :6379"]
        M["aurora-migracions<br/>corre una vegada"]
    end
    W -->|"proxy /api → aurora-api:3000"| A1
    A1 -.mateix contenidor.- A2
    A2 -->|"SQL :5432"| D
    A2 -->|"cache-aside :6379"| C
    M -->|"aplica l'esquema"| D
    D --> V[("volum<br/>aurora-dades")]

aurora-api és l'únic servei que està a les dues xarxes: és la frontera. Tot el que entra des de fora passa per aurora-web, i tot el que toca les dades viu en una xarxa sense sortida a l'exterior.

  1. Com es troben els serveis: el DNS de Compose

A la lliçó 03-05 vas veure que una xarxa bridge d'usuari incorpora un servidor DNS intern a 127.0.0.11. Compose es recolza exactament en això, amb una precisió que convé gravar-se:

El nom de host d'un servei és el nom del servei, no el del contenidor.

El contenidor es diu aurora-libros-aurora-db-1, però des de l'API et connectes a aurora-db. Compose registra al DNS de cada xarxa:

Nom registrat Origen
aurora-db Nom del servei (sempre)
base-de-dades Qualsevol aliases que declaris
aurora-libros-aurora-db-1 Nom del contenidor

Els àlies resulten molt útils en migrar: si el codi heretat busca postgres, hi afegeixes aliases: [postgres] i funciona sense tocar ni una línia de l'aplicació.

docker compose exec aurora-api getent hosts aurora-db aurora-cache
172.21.0.3   aurora-db
172.21.0.4   aurora-cache

Una conseqüència important: la resolució és per xarxa. Un servei només resol els noms dels serveis amb qui comparteix alguna xarxa. És la base de la segmentació de l'apartat 4.

  1. Qui parla amb qui i per quin port

Aquí hi ha l'error conceptual més repetit amb Compose: fer servir el port publicat en comptes de l'intern. La publicació (ports) és una porta des del host cap a dins; entre contenidors no hi intervé per a res.

Origen Destí Host que fa servir Port Xarxa
Navegador aurora-web localhost 8080 (publicat)
aurora-web aurora-api aurora-api 3000 (intern) frontal
aurora-api aurora-db aurora-db 5432 (intern) posterior
aurora-api aurora-cache aurora-cache 6379 (intern) posterior
aurora-migracions aurora-db aurora-db 5432 (intern) posterior

Només la primera fila fa servir un port publicat, perquè és l'única que entra des del host. La resta viatja per la xarxa interna de Docker.

Això es reflecteix al web/nginx.conf que ja vas escriure al mòdul 3, on proxy_pass http://aurora-api:3000/; fa servir nom de servei i port intern. I a les variables de l'API: DB_HOST=aurora-db, REDIS_HOST=aurora-cache. Res de localhost, res d'adreces IP, res de ports publicats.

  1. Segmentació en dues xarxes

Amb una sola xarxa, els quatre serveis es veuen entre ells. Funciona, però significa que si algú compromet el contenidor de nginx —el més exposat, l'únic que rep trànsit de fora— té accés directe al port 5432 de PostgreSQL. La bona pràctica és segmentar per zones:

Xarxa internal Serveis Funció
frontal no aurora-web, aurora-api Trànsit d'entrada i proxy invers
posterior aurora-api, aurora-db, aurora-cache, aurora-migracions Dades, sense sortida a Internet
networks:
  frontal:
    driver: bridge
  posterior:
    driver: bridge
    internal: true

internal: true fa dues coses: els contenidors d'aquesta xarxa no tenen ruta cap a l'exterior (ni tan sols poden fer ping a Internet) i ningú des de fora del host no els pot abastar. Si una dependència compromesa de PostgreSQL intentés trucar a casa, no tindria per on.

Efecte col·lateral que convé conèixer: un contenidor connectat només a xarxes internes no pot publicar ports al host. Per això aurora-db deixa de publicar 127.0.0.1:5432; per accedir a la base de dades faràs servir docker compose exec, i l'entorn de desenvolupament (lliçó 04-07) la tornarà a exposar afegint el servei a una xarxa no interna.

  1. Demostració: la web no arriba a la base de dades

La segmentació no és teoria: es comprova.

docker compose exec aurora-web getent hosts aurora-api
docker compose exec aurora-web getent hosts aurora-db
172.21.0.5   aurora-api

La primera resol; la segona no retorna res i acaba amb codi 2. aurora-web i aurora-db no comparteixen cap xarxa, així que per a nginx la base de dades senzillament no existeix al DNS. Amb la caixa d'eines de xarxa de la lliçó 03-04, la diferència entre les dues xarxes és taxativa:

docker run --rm --network aurora-libros_frontal nicolaka/netshoot nc -zv -w 2 aurora-db 5432
docker run --rm --network aurora-libros_posterior nicolaka/netshoot nc -zv -w 2 aurora-db 5432
docker compose exec aurora-db ping -c 1 -W 2 8.8.8.8
nc: getaddrinfo for host "aurora-db" port 5432: Name does not resolve
Connection to aurora-db (172.21.0.3) 5432 port [tcp/*] succeeded!
ping: sendto: Network is unreachable

Des de frontal el nom ni tan sols resol; des de posterior connecta; i la mateixa base de dades no té sortida a Internet. Aquesta és la diferència entre "funciona" i "funciona i a més conté el dany". L'enduriment complet arriba a la lliçó 05-03.

  1. L'ordre d'arrencada: per què depends_on a seques no n'hi ha prou

Prova què passa sense cap condició, amb depends_on: [aurora-db] a l'API:

docker compose down && docker compose up -d && sleep 2 && docker compose logs aurora-api --tail 3
aurora-api-1  | Error: connect ECONNREFUSED 172.21.0.3:5432
aurora-api-1  | Fallada en connectar amb la base de dades, sortint
aurora-api-1  | exited with code 1

Compose ha complert la seva promesa: va arrencar aurora-db abans. El problema és què significa "abans". Hi ha tres estats diferents i només el tercer serveix:

Estat Significa Accepta connexions?
Creat El contenidor existeix No
Arrencat El procés està corrent (depends_on curt) Encara no
Preparat (healthy) La sonda de salut passa

PostgreSQL triga entre 3 i 10 segons des que el procés arrenca fins que accepta connexions, i a la primera arrencada, amb init.sql per executar, força més. La distinció entre "arrencat" i "preparat" és la que trenca la meitat dels compose.yaml que circulen per aquí.

  1. Sondes de salut reals i condition: service_healthy

La solució té dues meitats. Primera: sondes que diguin la veritat sobre cada servei.

Servei test Per què aquesta comprovació
aurora-db pg_isready -U aurora -d aurora_llibres Eina oficial de PostgreSQL: retorna 0 només quan accepta connexions en aquesta base. Durant la inicialització, el port està obert però les connexions es rebutgen
aurora-cache redis-cli ping Retorna PONG i codi 0 només si el servidor respon de debò
aurora-api wget --spider -q http://localhost:3000/salut L'endpoint propi, que al seu torn comprova base de dades i memòria cau
aurora-web wget --spider -q http://localhost/ nginx serveix la pàgina

Segona meitat: declarar la dependència de la salut, no de l'arrencada.

  aurora-api:
    depends_on:
      aurora-db:
        condition: service_healthy
      aurora-cache:
        condition: service_healthy

Ara Compose no llança l'API fins que totes dues sondes passen. Si una no arriba mai a healthy, up --wait falla amb un missatge clar en comptes de deixar-te un servei reiniciant-se en bucle.

  1. Un servei que corre una vegada: service_completed_successfully

Aurora Libros necessita aplicar l'esquema i les migracions abans que l'API atengui peticions. Això no és un servei permanent: és una tasca que corre, acaba i desapareix.

  aurora-migracions:
    image: auroralibros/aurora-api:1.2.0   # mateixa imatge, una altra comanda
    command: ["node", "migrar.js"]
    environment:
      DB_HOST: aurora-db
      DB_USER: aurora
      DB_PASSWORD: aurora_secreta
      DB_NAME: aurora_llibres
    depends_on:
      aurora-db:
        condition: service_healthy
    restart: "no"          # que NO es reiniciï en acabar
    networks: [posterior]

I l'API espera que acabi bé:

    depends_on:
      aurora-migracions:
        condition: service_completed_successfully

Dos detalls que es passen per alt: restart: "no" és imprescindible —amb unless-stopped heretat d'una àncora, el contenidor es reiniciaria en bucle eternament en acabar—, i les cometes també, perquè no sense elles és el booleà false en YAML. A més, la condició exigeix codi de sortida 0: si la migració falla, l'API ni tan sols es crea, i aquest és justament el comportament correcte.

  1. Per què l'aplicació ha de reintentar igualment

Amb tot l'anterior, l'arrencada en fred està resolta. Però no la resta de casos: si aurora-db es reinicia a les tres de la matinada per una fallada de disc, l'API ja està corrent i depends_on no es torna a avaluar; en un orquestrador, els contenidors es reprogramen en qualsevol ordre; i una caiguda de xarxa de dos segons no l'arregla cap fitxer YAML.

depends_on resol l'arrencada; la reconnexió és responsabilitat de l'aplicació. Afegeix a api/server.js un reintent amb retrocés exponencial:

async function connectarAmbReintents(crearConnexio, { intents = 8, baseMs = 500 } = {}) {
  for (let i = 1; i <= intents; i++) {
    try {
      return await crearConnexio();
    } catch (err) {
      if (i === intents) throw err;
      // 0,5 s · 1 s · 2 s · 4 s ... amb jitter per no sincronitzar reintents
      const espera = Math.min(baseMs * 2 ** (i - 1), 30_000) + Math.random() * 250;
      console.warn(`Connexió fallida (intent ${i}/${intents}): ${err.message}. ` +
                   `Reintent en ${Math.round(espera)} ms`);
      await new Promise((r) => setTimeout(r, espera));
    }
  }
}

const pool = await connectarAmbReintents(async () => {
  const p = new Pool({ host: process.env.DB_HOST, user: process.env.DB_USER,
                       password: process.env.DB_PASSWORD, database: process.env.DB_NAME });
  await p.query('SELECT 1');   // no n'hi ha prou de crear el pool: cal provar-lo
  return p;
});

Tres decisions de disseny: el retrocés exponencial evita martellejar una base de dades que està arrencant, el jitter aleatori impedeix que deu rèpliques reintentin a l'uníson, i la prova SELECT 1 és el que distingeix "tinc un objecte de connexió" de "la base de dades em respon". La memòria cau mereix un tracte diferent: si Redis no hi és, /llibres ha de continuar servint des de PostgreSQL amb origen: db. Una memòria cau és un accelerador, mai una dependència dura.

  1. El compose.yaml complet i comentat

# compose.yaml — Aurora Libros S.L. — plataforma completa
name: aurora-libros

x-comu: &comu
  restart: unless-stopped
  logging:
    driver: json-file
    options: { max-size: "10m", max-file: "3" }
  labels: { com.auroralibros.projecte: "aurora-libros" }

services:

  # --- Dades: PostgreSQL amb el catàleg ----------------------------------
  aurora-db:
    <<: *comu
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: aurora
      POSTGRES_PASSWORD: aurora_secreta
      POSTGRES_DB: aurora_llibres
    volumes:
      - aurora-dades:/var/lib/postgresql/data
      - ./db/init.sql:/docker-entrypoint-initdb.d/init.sql:ro
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U aurora -d aurora_llibres"]
      interval: 5s
      timeout: 5s
      retries: 10
      start_period: 30s      # la primera inicialització executa init.sql
    stop_grace_period: 30s   # marge per tancar el checkpoint
    deploy:
      resources: { limits: { memory: 512M, cpus: "1.0", pids: 200 }, reservations: { memory: 256M } }
    networks: [posterior]    # NOMÉS a la xarxa interna

  # --- Memòria cau: Redis en mode cache-aside ----------------------------
  aurora-cache:
    <<: *comu
    image: redis:7-alpine
    command: ["redis-server", "--maxmemory", "200mb", "--maxmemory-policy", "allkeys-lru"]
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      timeout: 3s
      retries: 5
    deploy:
      resources: { limits: { memory: 256M, cpus: "0.5", pids: 100 } }
    networks: [posterior]

  # --- Migracions: corre una vegada i acaba ------------------------------
  aurora-migracions:
    image: auroralibros/aurora-api:1.2.0
    command: ["node", "migrar.js"]
    environment:
      DB_HOST: aurora-db
      DB_USER: aurora
      DB_PASSWORD: aurora_secreta
      DB_NAME: aurora_llibres
    depends_on:
      aurora-db: { condition: service_healthy }
    restart: "no"            # entre cometes: sense elles seria el booleà false
    networks: [posterior]

  # --- API: la frontera entre les dues xarxes ----------------------------
  aurora-api:
    <<: *comu
    build:
      context: ./api
      dockerfile: Dockerfile
    image: auroralibros/aurora-api:1.2.0
    environment:
      PORT: "3000"
      DB_HOST: aurora-db       # nom de SERVEI
      DB_USER: aurora
      DB_PASSWORD: aurora_secreta
      DB_NAME: aurora_llibres
      REDIS_HOST: aurora-cache
    depends_on:
      aurora-db: { condition: service_healthy }
      aurora-cache: { condition: service_healthy }
      aurora-migracions: { condition: service_completed_successfully }
    healthcheck:
      test: ["CMD", "wget", "--spider", "-q", "http://localhost:3000/salut"]
      interval: 10s
      timeout: 3s
      retries: 3
      start_period: 20s
    restart: on-failure:3
    deploy:
      resources: { limits: { memory: 256M, cpus: "1.0", pids: 100 }, reservations: { memory: 128M } }
    networks: [frontal, posterior]   # l'únic servei a totes dues

  # --- Web: pàgina estàtica i proxy invers -------------------------------
  aurora-web:
    <<: *comu
    image: nginx:alpine
    ports:
      - "8080:80"            # l'ÚNICA porta des del host
    volumes:
      - ./web/index.html:/usr/share/nginx/html/index.html:ro
      - ./web/nginx.conf:/etc/nginx/conf.d/default.conf:ro
    depends_on:
      aurora-api: { condition: service_healthy }
    stop_signal: SIGQUIT     # nginx s'atura ordenadament amb SIGQUIT
    healthcheck:
      test: ["CMD", "wget", "--spider", "-q", "http://localhost/"]
      interval: 15s
      timeout: 3s
      retries: 3
    deploy:
      resources: { limits: { memory: 128M, cpus: "0.5", pids: 50 } }
    networks: [frontal]      # sense accés a les dades

volumes:
  aurora-dades:

networks:
  frontal:
    driver: bridge
  posterior:
    driver: bridge
    internal: true           # sense sortida a Internet ni entrada des de fora

Cent trenta línies comentades que substitueixen cinquanta línies de bash i, sobretot, expressen coses que l'script no sabia dir: qui depèn de qui, què significa estar preparat, i què pot parlar amb què.

  1. Posada en marxa end-to-end

cd ~/aurora-libros
docker compose down -v          # partim de zero, és un entorn de proves
docker compose up -d --build --wait
[+] Running 8/8
 ✔ Network aurora-libros_frontal              Created
 ✔ Network aurora-libros_posterior            Created
 ✔ Volume "aurora-libros_aurora-dades"        Created
 ✔ Container aurora-libros-aurora-db-1         Healthy
 ✔ Container aurora-libros-aurora-cache-1      Healthy
 ✔ Container aurora-libros-aurora-migracions-1 Exited
 ✔ Container aurora-libros-aurora-api-1        Healthy
 ✔ Container aurora-libros-aurora-web-1        Healthy

Llegeix la seqüència: les dues xarxes i el volum primer, després base de dades i memòria cau fins a Healthy, tot seguit les migracions fins a Exited amb codi 0, i només llavors l'API i la web. L'ordre ja no l'escrius tu: el dedueix Compose del graf de dependències.

docker compose ps --format "table {{.Service}}\t{{.Status}}"
SERVICE              STATUS
aurora-api           Up 20 seconds (healthy)
aurora-cache         Up 46 seconds (healthy)
aurora-db            Up 46 seconds (healthy)
aurora-migracions    Exited (0) 32 seconds ago
aurora-web           Up 8 seconds (healthy)

La prova de foc: la cache-aside en funcionament i, després, la resiliència que vam exigir a l'apartat 9.

curl -s http://localhost:8080/api/llibres | jq '{origen, total: (.llibres|length)}'
curl -s http://localhost:8080/api/llibres | jq '{origen, total: (.llibres|length)}'
docker compose stop aurora-cache
curl -s http://localhost:8080/api/llibres | jq '{origen, total: (.llibres|length)}'
docker compose start aurora-cache
{ "origen": "db", "total": 9 }
{ "origen": "cache", "total": 9 }
{ "origen": "db", "total": 9 }

Primera petició des de PostgreSQL, segona des de Redis: els nou títols travessant web → api → db → cache exactament com es va dissenyar, i http://localhost:8080 al navegador mostra el catàleg complet. I amb la memòria cau aturada, l'API continua servint des de la base de dades: degradació elegant, es perd velocitat, no servei.

  1. El recompte de l'onboarding

Moment Passos per tenir Aurora Libros funcionant
Lliçó 01-07, sense Docker 15 passos manuals: Node, PostgreSQL, Redis, nginx, usuaris, esquema...
Mòdul 3, amb docker run Un script de 50 línies que cal mantenir a mà
Ara 2 comandes
git clone https://github.com/auroralibros/plataforma.git && cd plataforma
docker compose up -d --wait

Això és tot. Algú que entra avui a l'equip té la plataforma completa —quatre serveis, dues xarxes, un volum, migracions aplicades i sondes de salut verificades— en menys d'un minut, i amb exactament la mateixa configuració que la resta de l'equip, perquè és a Git.

Errors Habituals i Consells

Fer servir el port publicat entre contenidors. DB_HOST=localhost:5432 des de l'API no troba res: dins del contenidor, localhost és ell mateix. Nom de servei i port intern, sempre.

Confiar en depends_on sense condition. Només garanteix ordre d'arrencada. Sense sondes de salut, continuaràs tenint curses.

Sondar el port en comptes del servei. nc -z aurora-db 5432 dóna verd mentre PostgreSQL encara rebutja connexions. Fes servir pg_isready.

Oblidar restart: "no" en un servei d'un sol ús. Amb una política heretada, la migració es reinicia en bucle i el service_completed_successfully no es compleix mai.

Suposar que depends_on reconnecta. No es torna a avaluar després de l'arrencada. La reconnexió amb reintents és de l'aplicació.

Tractar la memòria cau com una dependència dura. Si la teva API mor perquè Redis no respon, has convertit un accelerador opcional en un punt únic de fallada.

Consell: quan dos serveis no es vegin, la primera comprovació és sempre docker compose exec <origen> getent hosts <destí>. Si no resol, no és un problema de l'aplicació: és que no comparteixen xarxa.

Exercicis

Exercici 1. Afegeix a aurora-db l'àlies de xarxa postgres i demostra que l'API es pot connectar indistintament per aurora-db o per postgres, però que aurora-web no en resol cap dels dos.

Exercici 2. Trenca l'arrencada expressament: fes que aurora-migracions acabi amb codi 1 (per exemple, command: ["sh", "-c", "echo 'migració fallida'; exit 1"]) i observa què fa docker compose up -d --wait amb l'API i amb la web. Explica el resultat i per què és el comportament desitjable.

Exercici 3. Demostra que l'aïllament de posterior és real en les dues direccions: (a) aurora-db no pot sortir a Internet, (b) des d'una altra màquina de la teva xarxa local no es pot abastar el 5432, i (c) aurora-api sí que pot sortir a Internet. Explica per què (c) funciona tot i estar a la xarxa interna.

Solucions

Solució 1.

  aurora-db:
    networks:
      posterior:
        aliases: [postgres]
docker compose up -d
docker compose exec aurora-api getent hosts aurora-db
docker compose exec aurora-api getent hosts postgres
docker compose exec aurora-web getent hosts postgres; echo "codi: $?"
172.21.0.3   aurora-db
172.21.0.3   postgres
codi: 2

La mateixa adreça per als dos noms: l'àlies és una entrada DNS addicional en aquesta xarxa, no un altre contenidor. Per a aurora-web, codi 2 (no resol), perquè els àlies viuen dins de la xarxa on es declaren i nginx no és a posterior. Aquest és el mecanisme amb què es migra un servei sense tocar el codi dels seus clients: primer s'afegeix l'àlies nou, després es canvia el nom del servei.

Solució 2.

docker compose up -d --wait; echo "codi de sortida: $?"
docker compose ps -a --format "table {{.Service}}\t{{.Status}}"
dependency failed to start: container aurora-libros-aurora-migracions-1 exited (1)
codi de sortida: 1

SERVICE              STATUS
aurora-cache         Up 15 seconds (healthy)
aurora-db            Up 15 seconds (healthy)
aurora-migracions    Exited (1) 3 seconds ago

Ni aurora-api ni aurora-web no arriben a crear-se. Compose talla la cadena: si una dependència amb service_completed_successfully surt amb codi diferent de 0, tot el que en depèn es cancel·la, i aquesta fallada es propaga en cascada a la web, que depèn de l'API.

És exactament el que vols. L'alternativa —arrencar l'API contra un esquema a mig migrar— produiria errors intermitents, dades corruptes i una hora de depuració. A més, --wait retorna codi 1, així que un pipeline de CI es posa en vermell automàticament en comptes de continuar sobre una base trencada. Compara-ho amb l'script de bash del mòdul 3: allà, si el pas 4 fallava, els passos 5, 6 i 7 s'executaven igualment.

Solució 3.

# (a) la base de dades no surt a Internet
docker compose exec aurora-db ping -c 1 -W 2 1.1.1.1
# (b) el port 5432 no està publicat en cap interfície
ss -ltnp | grep 5432 || echo "5432 no publicat al host"
# (c) l'API sí que surt
docker compose exec aurora-api wget -qO- -T 3 https://example.com > /dev/null \
  && echo "l'API sí que té sortida"
ping: sendto: Network is unreachable
5432 no publicat al host
l'API sí que té sortida

(a) i (b) confirmen el doble aïllament: internal: true elimina la ruta per defecte d'aquesta xarxa, i com que no hi ha ports a aurora-db, no existeix cap regla al host que hi redirigeixi —ni tan sols des del mateix host, i encara menys des d'una altra màquina de la xarxa local—.

(c) funciona perquè aurora-api és a les dues xarxes. Un contenidor connectat a diverses xarxes té una interfície a cadascuna, i la ruta per defecte l'hi dóna frontal, que no és interna. D'aquí que l'API sigui la "frontera": és l'únic punt pel qual la zona de dades es comunica amb el món, i per tant l'únic que cal vigilar de prop.

Conclusió

Aurora Libros està muntada de debò. Saps que els serveis es troben pel nom del servei —no el del contenidor— gràcies al DNS intern de cada xarxa, que els aliases afegeixen noms extra dins de la xarxa on es declaren, i que entre contenidors sempre es fa servir el port intern: la publicació de ports només obre una porta des del host, i en aquesta arquitectura n'hi ha exactament una, el 8080 de la web.

Has segmentat la plataforma en dues zones: una de frontal amb la web i l'API, i una de posterior marcada com a internal amb les dades i les migracions, i ho has demostrat en les dues direccions —nginx no resol ni tan sols el nom de la base de dades, i PostgreSQL no té ruta cap a Internet—, amb aurora-api com a única frontera per ser a totes dues xarxes. I tens resolta l'arrencada amb la distinció que separa un fitxer que funciona al teu portàtil d'un que funciona sempre: arrencat no és el mateix que preparat. pg_isready i redis-cli ping com a sondes honestes, condition: service_healthy per esperar que ho estiguin, service_completed_successfully amb restart: "no" per a la migració que corre una vegada, i el reintent amb retrocés exponencial i jitter a l'aplicació, perquè depends_on cobreix l'arrencada i res més. La memòria cau, tractada com el que és: un accelerador la caiguda del qual degrada el servei però no el tomba.

El recompte parla sol: de quinze passos manuals a un script de cinquanta línies, i d'aquí a git clone i docker compose up -d --wait. Però el fitxer encara té les contrasenyes escrites en clar, la versió de la imatge fixada a mà i els ports incrustats: exactament el que impedeix fer-lo servir en més d'un entorn. A la lliçó següent, Variables d'Entorn a Docker Compose, separaràs els dos sistemes que tothom confon —la substitució ${VAR} que Compose fa sobre el mateix YAML, i les variables que veu el procés dins del contenidor—, amb la seva taula de precedència completa, el fitxer .env del projecte i el seu .env.example versionat, i el bloc secrets: perquè les credencials deixin de ser visibles en un docker inspect.

Docker: De Principiant a Avançat

Mòdul 1: Introducció a Docker

Mòdul 2: Treballant amb Imatges Docker

Mòdul 3: Contenidors Docker

Mòdul 4: Docker Compose

Mòdul 5: Conceptes Avançats de Docker

Mòdul 6: Docker en Producció

Mòdul 7: Ecosistema i Eines de Docker

© Copyright 2026. Tots els drets reservats