Tens una imatge professional a la teva màquina: auroralibros/aurora-api:1.1.0, amb usuari sense privilegis, etiquetes OCI, healthcheck i una aturada neta en 0,3 segons. I saps mantenir el magatzem on viu. Li falta l'única cosa que justificava tot el mòdul: sortir del teu portàtil. Una imatge que només existeix a la màquina on es va construir no resol el problema d'Aurora Libros; resol el teu problema local, que no és el mateix. Aquesta lliçó tanca el cicle. Entendràs què fa exactament docker image tag —que no és copiar—, decidiràs una estratègia d'etiquetatge que no t'exploti d'aquí a sis mesos, publicaràs a Docker Hub i també a GitHub Container Registry, i aprendràs per què en producció es desplega per etiqueta immutable o per digest i mai, mai de la vida, per latest. En acabar, qualsevol persona del món amb Docker instal·lat podrà executar l'API d'Aurora Libros amb una sola comanda.

Contingut

  1. docker image tag: referències, no còpies
  2. El nom complet i el registre implícit
  3. Estratègia d'etiquetatge
  4. latest i per què no s'hi desplega
  5. Publicar a Docker Hub amb docker push
  6. Verificar el que s'ha publicat
  7. Publicar a GitHub Container Registry
  8. Repositoris privats i accés d'equip
  9. Esborrar etiquetes i per què trenca desplegaments
  10. Convencions de nomenclatura per a Aurora Libros

  1. docker image tag: referències, no còpies

Comencem desfent el malentès més estès. docker image tag no copia una imatge, no duplica capes i no ocupa espai addicional. Crea un nom nou que apunta a la mateixa imatge.

docker image tag auroralibros/aurora-api:1.1.0 auroralibros/aurora-api:1.1
docker image tag auroralibros/aurora-api:1.1.0 auroralibros/aurora-api:1
docker image tag auroralibros/aurora-api:1.1.0 auroralibros/aurora-api:latest
docker image ls auroralibros/aurora-api --format "table {{.Tag}}\t{{.ID}}\t{{.Size}}"
TAG      IMAGE ID       SIZE
1        4e9c7d2a8f31   167MB
1.1      4e9c7d2a8f31   167MB
1.1.0    4e9c7d2a8f31   167MB
latest   4e9c7d2a8f31   167MB

Quatre files, un sol IMAGE ID. Són quatre noms de la mateixa imatge. I el disc ho confirma:

docker system df --format "table {{.Type}}\t{{.TotalCount}}\t{{.Size}}" | head -2
TYPE      TOTAL     SIZE
Images    9         842.3MB

Abans de crear les tres etiquetes eren 842,3 MB; després, exactament els mateixos 842,3 MB. És la mateixa demostració que vas fer a l'exercici 2 de la lliçó anterior, i el mateix model de l'apartat 1 de la 02-01: les etiquetes són punters a digests, no contenidors de dades.

La sintaxi:

docker image tag <origen>[:etiqueta] <destinació>[:etiqueta]

L'origen pot ser un nom existent o un IMAGE ID:

docker image tag 4e9c7d2a8f31 auroralibros/aurora-api:candidata
docker image tag auroralibros/aurora-api:1.1.0 ghcr.io/auroralibros/aurora-api:1.1.0
docker image tag auroralibros/aurora-api:1.1.0 registre.intern.auroralibros.example/aurora-api:1.1.0

Les tres són gratuïtes i instantànies. La conseqüència pràctica és important: pots publicar la mateixa imatge en diversos registres sense reconstruir-la. Ho faràs a l'apartat 7.

Regles dels noms d'etiqueta:

Regla Vàlid No vàlid
Fins a 128 caràcters 1.1.0-rc.1 Una cadena de 200 caràcters
Lletres, dígits, _, ., - v1.1.0, main_2026-08-04 1.1.0/rc, versió-1
No pot començar per . ni - 1.1.0 .oculta, -beta
Majúscules permeses a l'etiqueta 1.1.0-RC1
El nom del repositori va en minúscules aurora-api Aurora-API

L'última fila causa errors reals: docker push AuroraLibros/Aurora-API falla amb invalid reference format: repository name must be lowercase.

  1. El nom complet i el registre implícit

Ja ho vas veure a la lliçó 02-01, però ara és quan importa de debò, perquè el nom determina on va el push:

[registre/][usuari/]repositori[:etiqueta]
docker push auroralibros/aurora-api:1.1.0

Docker expandeix aquest nom a docker.io/auroralibros/aurora-api:1.1.0 i envia la imatge a Docker Hub. No hi ha cap opció --registry: si vols publicar en un altre lloc, canvies el nom.

Nom Destinació del push
aurora-api:1.1.0 docker.io/library/aurora-apifalla: ningú no pot escriure a library/
auroralibros/aurora-api:1.1.0 Docker Hub, espai auroralibros
ghcr.io/auroralibros/aurora-api:1.1.0 GitHub Container Registry
localhost:5000/aurora-api:1.1.0 El teu registre local de la lliçó 02-01
123456789012.dkr.ecr.eu-west-1.amazonaws.com/aurora-api:1.1.0 Amazon ECR

La primera fila és l'error denied: requested access to the resource is denied que vas diagnosticar a l'exercici 3 de la lliçó 02-01. Ara ja saps que gairebé mai no és un problema de credencials: és un nom sense espai de noms.

Docker distingeix el registre de l'usuari amb una heurística senzilla: si el primer segment conté un punt o dos punts, o és exactament localhost, el tracta com a nom de servidor. Per això ghcr.io/… és un registre i auroralibros/… és un usuari de Docker Hub.

  1. Estratègia d'etiquetatge

Aquí hi ha la decisió que més problemes evita a mitjà termini. Etiquetar malament no trenca res avui; ho trenca tot d'aquí a sis mesos, quan ningú no sàpiga quina versió hi ha en producció.

Versionatge semàntic: etiquetes fixes i mòbils

El versionatge semàntic (MAJOR.MENOR.PEDAÇ) dona tres números amb significat:

Component Quan puja Exemple a Aurora Libros
MAJOR Canvi incompatible a l'API /llibres canvia el format de resposta
MENOR Funcionalitat nova compatible S'afegeix /llibres/cercar
PEDAÇ Correcció compatible S'arregla el càlcul del preu amb IVA

I d'aquí neixen dues classes d'etiqueta que cal distingir amb claredat:

Classe Exemples Canvia a quina imatge apunta? Per a què serveix
Fixa (immutable) 1.1.0, 1.1.0-rc.1, sha-7a3f912 Mai Desplegar, reproduir, auditar
Mòbil 1.1, 1, latest, estable Sí, amb cada publicació Comoditat, seguir una sèrie

Publicar una versió típica d'Aurora Libros:

docker image tag auroralibros/aurora-api:1.1.0 auroralibros/aurora-api:1.1
docker image tag auroralibros/aurora-api:1.1.0 auroralibros/aurora-api:1
docker image tag auroralibros/aurora-api:1.1.0 auroralibros/aurora-api:latest

Amb això, qui consumeix la imatge tria el seu nivell de risc:

flowchart LR
    subgraph HOY["Publicació d'1.1.0"]
        T1["1.1.0"] --> IMG1["sha256:4e9c…"]
        T2["1.1"] --> IMG1
        T3["1"] --> IMG1
        T4["latest"] --> IMG1
    end

    subgraph MANANA["Publicació d'1.1.1 (pedaç)"]
        U1["1.1.0"] --> IMG1b["sha256:4e9c…<br/>INTACTA"]
        U2["1.1.1"] --> IMG2["sha256:8b3f…"]
        U3["1.1"] --> IMG2
        U4["1"] --> IMG2
        U5["latest"] --> IMG2
    end

    HOY --> MANANA

Fixa't en l'essencial del diagrama: en publicar 1.1.1, l'etiqueta 1.1.0 continua apuntant exactament a la mateixa imatge de sempre. Les mòbils s'han desplaçat. Qui va desplegar 1.1.0 no es veu afectat per res; qui fa servir 1.1 rep el pedaç automàticament.

Altres estratègies

Estratègia Exemple Avantatges Inconvenients
SemVer fix 1.1.0 Traçable, reproduïble, és l'estàndard Cal decidir el número a cada release
SemVer mòbil 1.1, 1 Pedaços automàtics sense tocar el desplegament No saps quina imatge exacta corre
SHA de commit sha-7a3f912 Traçabilitat perfecta al codi; automàtica a CI Il·legible per a humans
Per branca main, develop, feature-cercar Còmoda en desenvolupament Mòbil per definició; mai en producció
Per entorn produccio, staging Simple d'entendre Antipatró: amaga quina versió hi ha desplegada
Per data 2026-08-04, 20260804-1142 Ordre cronològic obvi No diu res del contingut
latest latest Comoditat en provar Mai en producció (apartat 4)

L'estratègia per entorn mereix una advertència especial, perquè sembla raonable i és un parany. Si desplegues auroralibros/aurora-api:produccio, la pregunta "quina versió hi ha en producció?" no té resposta: l'etiqueta s'ha mogut vint vegades i ningú no sap on apunta avui. I un rollback és impossible, perquè la imatge anterior va perdre el nom. El correcte és a l'inrevés: la imatge s'etiqueta per versió, i quina versió va a cada entorn ho decideix el fitxer de desplegament, versionat a Git.

La combinació recomanada

A la pràctica, un pipeline madur publica diverses etiquetes de la mateixa imatge a cada release:

VERSION=1.1.0
COMMIT=$(git rev-parse --short HEAD)
IMATGE=auroralibros/aurora-api

docker build -t $IMATGE:$VERSION \
             -t $IMATGE:sha-$COMMIT \
             -t $IMATGE:1.1 \
             -t $IMATGE:1 \
             -t $IMATGE:latest \
             --build-arg VERSION=$VERSION \
             --build-arg REVISION=$COMMIT \
             --build-arg CREATED="$(date -u +%Y-%m-%dT%H:%M:%SZ)" .

Recorda de la lliçó 02-02 que -t és repetible: una sola build, cinc noms, cap cost addicional. Els --build-arg alimenten les etiquetes OCI de la lliçó 02-04, de manera que la imatge porta a dins la mateixa informació que declara a fora. Aquesta coherència és el que permet respondre "quin codi porta això?" amb un sol docker inspect.

  1. latest i per què no s'hi desplega

latest no té cap significat especial per a Docker. No és "la més recent", no s'actualitza sola i no la calcula ningú: és simplement l'etiqueta que es fa servir per defecte quan no n'escrius cap. Si ningú no publica una etiqueta anomenada latest, no existeix.

Els problemes de desplegar-hi:

  1. No és reproduïble. docker run auroralibros/aurora-api:latest avui i demà poden executar codi diferent. Si demà falla, no pots reproduir l'estat d'avui.
  2. Trenca els rollbacks. "Torna a la versió anterior" no té sentit si l'única referència és latest.
  3. Pot mentir. Si publiques 1.2.0 i després un pedaç 1.1.1 de la branca antiga sense cura, latest pot acabar apuntant a la versió més vella. És una etiqueta manual, no un càlcul.
  4. Provoca desplegaments heterogenis. Cinc rèpliques arrencades en moments diferents amb latest poden estar executant tres versions diferents alhora.
  5. Impedeix auditar. Quin codi hi ha en producció? "Latest." No és una resposta.

La regla d'or: en producció es desplega per etiqueta immutable o per digest. Mai per latest, ni per cap etiqueta mòbil.

Desplegar per digest

El nivell màxim de garantia és referenciar el digest del manifest:

docker pull auroralibros/aurora-api:1.1.0
docker image ls --digests auroralibros/aurora-api --format "{{.Tag}}\t{{.Digest}}"
1.1.0    sha256:c4f81b2e9a7d3061f5b8c2e4a9d7f3b1e6c8a2d4f9b7e3c1a5d8f2b6e4c9a7d3

I es desplega així:

docker pull auroralibros/aurora-api@sha256:c4f81b2e9a7d3061f5b8c2e4a9d7f3b1e6c8a2d4f9b7e3c1a5d8f2b6e4c9a7d3
docker run -d --name aurora-api \
  auroralibros/aurora-api@sha256:c4f81b2e9a7d3061f5b8c2e4a9d7f3b1e6c8a2d4f9b7e3c1a5d8f2b6e4c9a7d3

Comparació de garanties:

Referència Reproduïble? Llegible Ús
:latest No Proves ràpides i res més
:1.1 No (es mou amb els pedaços) Desenvolupament
:1.1.0 per conveni (res no impedeix reescriure-la) Producció normal
@sha256:c4f8… Sí, criptogràficament No Producció crítica, cadena de subministrament auditada

La fila d'1.1.0 té un matís que convé entendre: res a Docker no impedeix que algú torni a publicar 1.1.0 apuntant a una altra imatge. És una convenció d'equip, no una garantia tècnica. El digest sí que ho és: sha256:c4f8… és el hash del manifest, i si el contingut canviés, el hash seria un altre. Per això els sistemes de desplegament seriosos (Kubernetes en entorns regulats, lliçó 06-05) referencien per digest.

Pots obtenir el digest d'una imatge publicada sense descarregar-la:

docker buildx imagetools inspect auroralibros/aurora-api:1.1.0 --format "{{.Manifest.Digest}}"

  1. Publicar a Docker Hub amb docker push

Moment de la veritat. Recorda de l'apartat 10 de la lliçó 02-01 que el repositori triat és auroralibros/aurora-api, públic.

Pas 1: autenticar-se

docker login -u auroralibros
Password:
Login Succeeded

Al camp de contrasenya hi enganxes el token d'accés personal amb permís Read & Write, no la teva contrasenya, per tot l'explicat a la lliçó 02-01.

Pas 2: comprovar que el nom és correcte

docker image ls auroralibros/aurora-api --format "table {{.Tag}}\t{{.ID}}"
TAG      IMAGE ID
1        4e9c7d2a8f31
1.1      4e9c7d2a8f31
1.1.0    4e9c7d2a8f31
latest   4e9c7d2a8f31

Si el teu Docker ID real no és auroralibros, reetiqueta abans:

docker image tag auroralibros/aurora-api:1.1.0 el-teu-docker-id/aurora-api:1.1.0

Pas 3: auditar abans de publicar

Aquest pas no és opcional. Una imatge pública és codi publicat, i un cop pujada no hi ha marxa enrere real: algú pot haver-la descarregat el minut següent.

# a) Hi ha algun secret a les variables d'entorn?
docker image inspect auroralibros/aurora-api:1.1.0 --format '{{range .Config.Env}}{{println .}}{{end}}'
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
NODE_VERSION=22.14.0
YARN_VERSION=1.22.22
NODE_ENV=production
PORT=3000
APP_VERSION=1.1.0

Cap credencial. Correcte.

# b) Hi ha algun --build-arg amb secrets a l'historial?
docker image history auroralibros/aurora-api:1.1.0 --no-trunc --format "{{.CreatedBy}}" | grep -iE "password|secret|token|key" || echo "Sense secrets a l'historial"
Sense secrets a l'historial
# c) S'hi ha colat algun fitxer que no tocava?
docker run --rm --entrypoint ls auroralibros/aurora-api:1.1.0 -la /app
drwxr-xr-x    1 node     node          4096 Aug  4 11:42 .
-rw-r--r--    1 node     node           412 Aug  4 09:15 Dockerfile
drwxr-xr-x  112 node     node          4096 Aug  4 11:42 node_modules
-rw-r--r--    1 node     node         44231 Aug  4 09:15 package-lock.json
-rw-r--r--    1 node     node           412 Aug  4 09:15 package.json
-rw-r--r--    1 node     node          6104 Aug  4 11:20 server.js

Sense .env, sense .git, sense notes locals. El .dockerignore de la lliçó 02-02 ha fet la seva feina. (Hi apareix el Dockerfile si l'has tret de la llista d'exclusions; no és un problema de seguretat, però convé mantenir-lo exclòs pel que s'hi va explicar.)

Pas 4: el push

docker push auroralibros/aurora-api:1.1.0
The push refers to repository [docker.io/auroralibros/aurora-api]
9c1e4a7f2b9d: Pushed
7d3c9f2e8a51: Pushed
2f8e1a9c4b73: Pushed
b8f4e2a91c37: Mounted from library/node
4a1b8c2f9e3d: Mounted from library/node
8f3e1d7c5b9a: Mounted from library/node
e5c2a8f14b76: Mounted from library/node
1.1.0: digest: sha256:c4f81b2e9a7d3061f5b8c2e4a9d7f3b1e6c8a2d4f9b7e3c1a5d8f2b6e4c9a7d3 size: 1785

Aquesta sortida explica una història, i val la pena llegir-la amb calma:

Línia Què significa
Pushed Capa pujada de debò. Són les tres teves: els dos COPY i el RUN npm ci
Mounted from library/node Capa que ja era al registre (pertany a node:22-alpine) i s'ha referenciat, no transferit
1.1.0: digest: sha256:… El digest del manifest, el teu identificador immutable
size: 1785 La mida del manifest en bytes, no la de la imatge

Les quatre línies Mounted from són l'eficiència de les capes compartides de la lliçó 01-05 portada al registre: dels 167 MB de la imatge, només n'has pujat uns 25. La resta ja vivia allà perquè forma part de la imatge oficial de Node. Per això una imatge ben construïda sobre una base comuna es publica en segons.

Ara les etiquetes restants:

docker push auroralibros/aurora-api:1.1
docker push auroralibros/aurora-api:1
docker push auroralibros/aurora-api:latest
The push refers to repository [docker.io/auroralibros/aurora-api]
9c1e4a7f2b9d: Layer already exists
7d3c9f2e8a51: Layer already exists
2f8e1a9c4b73: Layer already exists
b8f4e2a91c37: Layer already exists
...
1.1: digest: sha256:c4f81b2e9a7d3061f5b8c2e4a9d7f3b1e6c8a2d4f9b7e3c1a5d8f2b6e4c9a7d3 size: 1785

Totes Layer already exists, i el mateix digest que 1.1.0. És la confirmació que les etiquetes són punters: no s'han pujat dades, només s'han creat tres noms nous al registre. Les tres publicacions triguen menys d'un segon entre totes tres.

Una drecera que convé conèixer i fer servir amb cura:

docker push --all-tags auroralibros/aurora-api

Puja totes les etiquetes locals d'aquell repositori. Còmode, però perillós: si tens una etiqueta local de proves (experiment, depuracio), acabarà publicada. Millor empènyer explícitament el que vols publicar.

  1. Verificar el que s'ha publicat

No donis mai per bona una publicació sense comprovar-la.

Al web

Entra a https://hub.docker.com/r/auroralibros/aurora-api i ves a la pestanya Tags. Hi hauries de veure 1.1.0, 1.1, 1 i latest, totes amb la mateixa mida comprimida i la mateixa data. És la mateixa pestanya que vas aprendre a llegir a la lliçó 02-01, ara des de l'altre costat.

Amb docker manifest inspect

docker manifest inspect auroralibros/aurora-api:1.1.0
{
   "schemaVersion": 2,
   "mediaType": "application/vnd.oci.image.manifest.v1+json",
   "config": {
      "mediaType": "application/vnd.oci.image.config.v1+json",
      "size": 3421,
      "digest": "sha256:4e9c7d2a8f31b5c9e2f7a1d4c8b3e6f9a2d5c8b1e4f7a3d6c9b2e5f8a1d4c7b0"
   },
   "layers": [
      {
         "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
         "size": 3622890,
         "digest": "sha256:e5c2a8f14b76..."
      },
      {
         "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
         "size": 24718432,
         "digest": "sha256:2f8e1a9c4b73..."
      }
   ]
}

Aquesta comanda consulta el registre remot, no el teu magatzem local: és la prova definitiva que la imatge és allà. El que et diu:

  • mediaType: el format del manifest (OCI, l'estàndard de la lliçó 01-03).
  • config.digest: l'identificador de la configuració de la imatge.
  • layers: cada capa amb la seva mida comprimida i el seu digest. Allà es veu que la capa del npm ci són 24,7 MB, coincidint amb el que reportava docker image history a la lliçó 02-05.

Verifica també que dues etiquetes apunten al mateix:

docker manifest inspect auroralibros/aurora-api:1.1.0 | sha256sum
docker manifest inspect auroralibros/aurora-api:latest | sha256sum
b8e2f4a91c37d5e8a2f6c9b1d4e7a3f8c5b2e9d6a1f4c7b0e3a6d9f2c5b8e1a4  -
b8e2f4a91c37d5e8a2f6c9b1d4e7a3f8c5b2e9d6a1f4c7b0e3a6d9f2c5b8e1a4  -

Manifests idèntics: 1.1.0 i latest són la mateixa imatge.

La prova real: executar-la des de zero

Aquesta és la que tanca el mòdul. Esborra la imatge local i descarrega-la del registre com faria qualsevol persona del món:

docker image rm auroralibros/aurora-api:1.1.0 auroralibros/aurora-api:1.1 \
                auroralibros/aurora-api:1 auroralibros/aurora-api:latest
docker image ls auroralibros/aurora-api
REPOSITORY   TAG   IMAGE ID   CREATED   SIZE

Buit. Ara, des del registre:

docker run -d --name aurora-publicada -p 3000:3000 auroralibros/aurora-api:1.1.0
Unable to find image 'auroralibros/aurora-api:1.1.0' locally
1.1.0: Pulling from auroralibros/aurora-api
e5c2a8f14b76: Pull complete
2f8e1a9c4b73: Pull complete
...
Status: Downloaded newer image for auroralibros/aurora-api:1.1.0
a7f3c9e2b8d1...
curl -s http://localhost:3000/salut
docker ps --filter name=aurora-publicada --format "{{.Names}}: {{.Status}}"
{"servei":"aurora-api","version":"1.0.0","db":"ko","cache":"ko",
 "errorDb":"connect ECONNREFUSED 127.0.0.1:5432","errorCache":"connect ECONNREFUSED 127.0.0.1:6379"}
aurora-publicada: Up 8 seconds (health: starting)

L'API d'Aurora Libros s'està executant des d'una imatge descarregada d'Internet. Aquell docker run funciona ara mateix en qualsevol màquina del planeta amb Docker instal·lat: sense instal·lar Node, sense nvm, sense npm install, sense conèixer el projecte. Compara-ho amb els set intents i l'hora i mitja de la lliçó 01-07.

docker rm -f aurora-publicada

  1. Publicar a GitHub Container Registry

Publicar en un segon registre és habitual: redundància, proximitat al codi, límits de descàrrega diferents. I amb el que saps, costa tres comandes.

Pas 1: crear un token de GitHub

A GitHub: Settings → Developer settings → Personal access tokens → Tokens (classic) → Generate new token, amb aquests permisos:

Permís Per a què
write:packages Publicar imatges
read:packages Descarregar-les
delete:packages Esborrar versions (apartat 9)

Copia'l: no es torna a mostrar.

Pas 2: autenticar-se

echo "ghp_elTeuTokenDExemple123456789" | docker login ghcr.io -u auroralibros --password-stdin
Login Succeeded

--password-stdin pel que s'ha explicat a la lliçó 02-01: res de contrasenyes a l'historial de l'intèrpret d'ordres. I fixa't que ara tens dues sessions obertes alhora, Docker Hub i ghcr.io, sense que cap interfereixi amb l'altra. La destinació la decideix el nom de la imatge, no un estat global.

cat ~/.docker/config.json
{
  "auths": {
    "ghcr.io": {},
    "https://index.docker.io/v1/": {}
  },
  "credsStore": "secretservice"
}

Dues entrades, totes dues buides perquè el credential helper desa les credencials al clauer del sistema.

Pas 3: reetiquetar i publicar

docker pull auroralibros/aurora-api:1.1.0    # Si la vas esborrar a l'apartat 6

docker image tag auroralibros/aurora-api:1.1.0 ghcr.io/auroralibros/aurora-api:1.1.0
docker image tag auroralibros/aurora-api:1.1.0 ghcr.io/auroralibros/aurora-api:latest

docker push ghcr.io/auroralibros/aurora-api:1.1.0
docker push ghcr.io/auroralibros/aurora-api:latest
The push refers to repository [ghcr.io/auroralibros/aurora-api]
9c1e4a7f2b9d: Pushed
7d3c9f2e8a51: Pushed
2f8e1a9c4b73: Pushed
b8f4e2a91c37: Pushed
4a1b8c2f9e3d: Pushed
8f3e1d7c5b9a: Pushed
e5c2a8f14b76: Pushed
1.1.0: digest: sha256:c4f81b2e9a7d3061f5b8c2e4a9d7f3b1e6c8a2d4f9b7e3c1a5d8f2b6e4c9a7d3 size: 1785

Dues observacions importants:

  1. Totes les capes diuen Pushed, cap Mounted from. És lògic: a ghcr.io no hi havia cap còpia prèvia de les capes de node:22-alpine, així que s'han pujat senceres. La primera publicació en un registre nou sempre és la lenta.
  2. El digest és idèntic al de Docker Hub: sha256:c4f81b2e…. La imatge és exactament la mateixa als dos registres, bit a bit. El digest depèn del contingut, no d'on estigui allotjat.

A GitHub, el paquet apareix a https://github.com/users/auroralibros/packages. Un detall: per defecte els paquets de ghcr.io són privats. Per fer-lo públic cal anar a Package settings → Change visibility → Public. I per enllaçar-lo amb el repositori de codi, n'hi ha prou que l'etiqueta OCI org.opencontainers.image.source de la lliçó 02-04 apunti al repositori: GitHub la llegeix i connecta tots dos automàticament. Aquella feina de metadades comença a rendir aquí.

Comparativa dels dos registres per a Aurora Libros:

Aspecte Docker Hub GitHub Container Registry
Repositoris privats al pla gratuït Quota limitada Il·limitats
Límit de descàrregues anònimes Sí (lliçó 02-01) Molt més laxe
Integració amb el codi Manual Automàtica via l'etiqueta OCI source
Descobribilitat pública Màxima Menor
Permisos Per organització Heretats del repositori de GitHub

Per a Aurora Libros, la política raonable: Docker Hub com a registre públic (descobrible, és on la gent busca) i ghcr.io com a registre de treball de l'equip, integrat amb el codi i amb privats sense cost.

  1. Repositoris privats i accés d'equip

Aurora Libros és una empresa: la seva API real no hauria de ser pública. Així es canvia.

Fer privat un repositori a Docker Hub

  1. Entra al repositori → pestanya Settings.
  2. Visibility settingsMake private.
  3. Confirma escrivint el nom del repositori.

L'efecte és immediat:

docker logout
docker pull auroralibros/aurora-api:1.1.0
Error response from daemon: pull access denied for auroralibros/aurora-api,
repository does not exist or may require 'docker login': denied: requested access
to the resource is denied

És el missatge ambigu de l'exercici 3 de la lliçó 02-01, ara amb la causa que faltava en aquella llista: el repositori existeix, però sense sessió no ho pots ni saber. El registre no distingeix entre "no existeix" i "no tens permís" a propòsit, per no filtrar quins repositoris privats té una organització.

docker login -u auroralibros
docker pull auroralibros/aurora-api:1.1.0    # Ara sí

Donar accés a un equip

Un repositori privat sota un compte personal només el veu el seu propietari. Per a un equip cal una organització:

  1. Docker Hub → OrganizationsCreate Organization (per exemple, auroralibros).
  2. A dins, Teams → crear equips per funció:
Equip Permís Qui
desenvolupament Read Desenvolupadors: descarreguen per treballar en local
ci Read & Write El compte de servei del pipeline: construeix i publica
plataforma Admin Responsables: visibilitat, esborrats, configuració
  1. Afegir membres a cada equip.
  2. Al repositori → Permissions → assignar cada equip al seu nivell.

Dos principis que cal respectar:

  • Mínim privilegi. Una desenvolupadora no necessita write. Si ningú més que el pipeline no publica, ningú més que el pipeline no ho ha de poder fer.
  • Comptes de servei per a l'automatització. El pipeline no fa servir mai les credencials personals de ningú: fa servir un compte propi amb un token de permisos limitats. Si aquella persona marxa de l'empresa, el pipeline no cau.

A GitHub Container Registry és més senzill perquè hereta del repositori de codi: qui té accés de lectura al repo té accés al paquet, i s'ajusta a Package settings → Manage Actions access.

  1. Esborrar etiquetes i per què trenca desplegaments

Es pot esborrar una etiqueta publicada. Gairebé mai no s'ha de fer.

A Docker Hub: repositori → pestanya Tags → seleccionar → Delete. Per esborrar el repositori sencer, Settings → Delete repository.

A ghcr.io: pàgina del paquet → Package settings → Manage versions → Delete.

Per què és perillós

Una etiqueta publicada és una dependència d'altres. Pensa en el que passa en esborrar auroralibros/aurora-api:1.1.0:

  1. Els desplegaments existents fallen en reiniciar-se. Els contenidors en marxa continuen vius (tenen la imatge en local), però qualsevol màquina nova, qualsevol rèplica que escali i qualsevol reinici després d'un docker system prune dona:
Error response from daemon: manifest for auroralibros/aurora-api:1.1.0 not found:
manifest unknown: manifest unknown
  1. Els rollbacks esdevenen impossibles. La versió anterior era exactament el que necessitaves per tornar enrere.
  2. Els pipelines es trenquen. Qualsevol Dockerfile que fes FROM auroralibros/aurora-api:1.1.0 deixa de construir.
  3. La traçabilitat desapareix. No es pot reproduir un incident de fa tres mesos.

I una variant encara pitjor: reescriure una etiqueta publicada, és a dir, fer push d'una imatge diferent amb el mateix 1.1.0. Ningú no se n'assabenta, les màquines que ja la tenen continuen amb la vella, les noves descarreguen la nova, i acabes amb un desplegament on 1.1.0 significa dues coses diferents segons quan va arrencar cada rèplica. És una de les incidències més difícils de diagnosticar que existeixen.

Quan sí que cal esborrar

Situació Acció
S'ha publicat un secret per error Esborrar immediatament i, sobretot, rotar la credencial: cal assumir que ja està compromesa
Etiquetes de desenvolupament antigues (pr-142, sha-… de fa mesos) Esborrat rutinari, amb polítiques de retenció
Versió amb una fallada greu de seguretat Millor publicar 1.1.1 corregida i marcar l'anterior com a obsoleta a la descripció, que esborrar-la
Versió estable en ús Mai

L'alternativa correcta a l'esborrat és la deprecació: publicar una versió nova, documentar a la descripció del repositori que l'anterior no s'ha de fer servir i donar un termini. S'avisa, no es trenca.

I una advertència sobre els secrets: esborrar l'etiqueta no esborra el problema. Si vas publicar una contrasenya dins d'una imatge, qualsevol la va poder descarregar durant els minuts que va estar disponible. L'única resposta vàlida és rotar aquella credencial. Per això el pas 3 de l'apartat 5 —auditar abans de publicar— no és burocràcia.

  1. Convencions de nomenclatura per a Aurora Libros

Tanquem amb la decisió escrita, que és el que un equip real acorda i documenta al seu repositori.

Repositoris

Un repositori per artefacte desplegable, mai un per projecte:

Servei Repositori Registre públic Registre d'equip
API del catàleg aurora-api docker.io/auroralibros/aurora-api ghcr.io/auroralibros/aurora-api
Web i proxy aurora-web docker.io/auroralibros/aurora-web ghcr.io/auroralibros/aurora-web
Base de dades (no escau) Es fa servir postgres:16-alpine oficial
Memòria cau (no escau) Es fa servir redis:7-alpine oficial

Les dues últimes files són una decisió deliberada: no es reempaqueten imatges oficials sense motiu. aurora-db farà servir postgres:16-alpine tal qual, amb la seva inicialització muntada des de fora (mòdul 3). Crear una imatge pròpia només per ficar-hi dins un init.sql afegeix una imatge per mantenir a canvi de res.

Etiquetes

Etiqueta Quan es publica Mòbil? Ús permès
1.1.0 A cada release, des d'una etiqueta de Git No Producció
1.1 Amb cada pedaç de la sèrie 1.1 Desenvolupament, entorns de prova
1 Amb cada versió menor de la sèrie 1 Desenvolupament
latest Amb l'última versió estable Proves ràpides. Mai en producció
sha-7a3f912 A cada commit de main, des de CI No Depuració, traçabilitat, rollback fi
1.2.0-rc.1 Candidates a versió No Entorn de preproducció
pr-142 A cada pull request Revisió; s'esborra en tancar la PR

Regles acordades

  1. Producció es desplega per etiqueta immutable (1.1.0) o per digest. Mai per latest, 1.1 ni 1.
  2. Una etiqueta fixa publicada no es reescriu mai. Si hi ha una fallada, es publica una versió nova.
  3. Tota imatge porta les etiquetes OCI de la lliçó 02-04, amb version i revision coincidint amb les etiquetes del registre.
  4. Només el compte de servei de CI publica en producció. Les persones publiquen, com a molt, etiquetes pr-* i sha-*.
  5. Auditar abans de publicar: variables d'entorn, historial i contingut d'/app.
  6. Les etiquetes pr-* caduquen als 30 dies, amb política de retenció automàtica.

Un exemple de la comanda completa de release, que és el que executarà el pipeline de la lliçó 06-02:

#!/bin/bash
# publicar-release.sh — publica una versió d'aurora-api als dos registres
set -euo pipefail

VERSION="${1:?Ús: publicar-release.sh <versio>   ex: 1.1.0}"
COMMIT=$(git rev-parse --short HEAD)
CREATED=$(date -u +%Y-%m-%dT%H:%M:%SZ)
MAJOR="${VERSION%%.*}"
MENOR="${VERSION%.*}"

for REG in "auroralibros" "ghcr.io/auroralibros"; do
  IMG="$REG/aurora-api"
  docker build \
    -t "$IMG:$VERSION" \
    -t "$IMG:$MENOR" \
    -t "$IMG:$MAJOR" \
    -t "$IMG:latest" \
    -t "$IMG:sha-$COMMIT" \
    --build-arg VERSION="$VERSION" \
    --build-arg REVISION="$COMMIT" \
    --build-arg CREATED="$CREATED" \
    --pull \
    ./api

  for T in "$VERSION" "$MENOR" "$MAJOR" "latest" "sha-$COMMIT"; do
    docker push "$IMG:$T"
  done
done

echo "Publicada $VERSION (commit $COMMIT) a Docker Hub i ghcr.io"
docker manifest inspect "auroralibros/aurora-api:$VERSION" --verbose | grep -m1 digest

Fixa't en els detalls: set -euo pipefail avorta davant de qualsevol error, ${VERSION%%.*} i ${VERSION%.*} deriven 1 i 1.1 d'1.1.0 amb expansió de paràmetres de l'intèrpret d'ordres, --pull garanteix la base actualitzada com es va explicar a 02-02, i els --build-arg mantenen coherents les etiquetes OCI internes amb les del registre.

Errors Habituals i Consells

  • Creure que docker image tag copia la imatge. Crea una referència; el disc no creix. Quatre etiquetes de la mateixa imatge ocupen el que una.
  • docker push aurora-api:1.1.0 sense espai de noms. S'expandeix a library/aurora-api i falla amb denied. Davant d'un denied en un push, mira el nom abans que les credencials.
  • Majúscules al nom del repositori. invalid reference format: repository name must be lowercase. Només l'etiqueta admet majúscules.
  • Desplegar amb latest. No és reproduïble, impedeix el rollback, pot apuntar a una versió més antiga i fa impossible auditar què hi ha en producció.
  • Etiquetar per entorn (produccio, staging). Antipatró: l'etiqueta es mou i ningú no sap quina versió corre. Etiqueta per versió i decideix al desplegament.
  • Reescriure una etiqueta fixa ja publicada. Provoca desplegaments on 1.1.0 significa coses diferents segons quan va arrencar cada rèplica. Publica una versió nova.
  • Esborrar una etiqueta publicada. Trenca reinicis, escalats i rollbacks de tothom qui la faci servir. Depreca en lloc d'esborrar.
  • Publicar sense auditar. Un secret publicat s'ha de donar per compromès: esborrar l'etiqueta no n'hi ha prou, cal rotar la credencial.
  • docker push --all-tags a la lleugera. Publica també les teves etiquetes de proves locals.
  • Fer servir la contrasenya en lloc d'un token. Un token es revoca sense canviar la contrasenya, té permisos limitats i en pots tenir un per màquina.
  • Consell: verifica sempre amb docker manifest inspect. Consulta el registre remot, no la teva memòria cau local: és l'única prova real que la publicació va funcionar.
  • Consell: esborra la imatge local i descarrega-la del registre abans de donar per bona una release. És l'única manera de comprovar que el que has publicat és autosuficient.
  • Consell: fes que les etiquetes OCI internes coincideixin amb les del registre. Que org.opencontainers.image.version digui 1.1.0 i l'etiqueta sigui 1.1.0 t'estalviarà més d'una investigació.

Exercicis

Exercici 1: demostra que les etiquetes són punters

  1. Anota el resultat de docker system df (línia d'Images).
  2. Crea cinc etiquetes noves per a auroralibros/aurora-api:1.1.0: 1.1, 1, latest, estable i sha-abc1234.
  3. Torna a executar docker system df. Ha crescut l'espai? Per què?
  4. Comprova que les sis referències comparteixen IMAGE ID.
  5. Esborra les etiquetes estable i sha-abc1234. Quin missatge dona cada esborrat? I si esborressis les sis?
  6. Reconstrueix la imatge després de modificar server.js, etiquetant-la de nou com a 1.1.0. Què passa amb 1.1, 1 i latest? Explica el resultat en termes de punters.

Exercici 2: publica i verifica el cicle complet

Amb el teu compte real de Docker Hub (substitueix auroralibros pel teu Docker ID):

  1. Audita la imatge abans de publicar: variables d'entorn, historial a la recerca de secrets i contingut d'/app.
  2. Publica 1.1.0 i latest, i analitza la sortida del primer push: quantes capes diuen Pushed i quantes Mounted from? Per què?
  3. Analitza la sortida del segon push. Per què és instantani?
  4. Verifica amb docker manifest inspect que totes dues etiquetes tenen el mateix digest.
  5. Esborra totes les còpies locals i executa l'API descarregant-la del registre. Comprova /salut i l'estat del healthcheck.
  6. Obtén el digest i executa la imatge referenciant-la per @sha256:… en lloc de per etiqueta.

Exercici 3: dissenya l'estratègia d'etiquetatge d'Aurora Libros

L'equip d'Aurora Libros et planteja quatre situacions. Per a cadascuna, indica quines etiquetes publicaries, quina desplegaries en producció i per què:

a) Release estable de la versió 1.2.0, amb una funcionalitat nova (/llibres/cercar) des del commit 9c4e1a7 de la branca main.

b) Correcció urgent d'una fallada de seguretat a la 1.2.0, que cal desplegar en producció avui mateix.

c) Una pull request número 87 que afegeix paginació i cal provar a l'entorn de revisió.

d) Canvi incompatible: /llibres passa a retornar {dades: [...], total: n} en lloc d'un array. Trenca el web actual.

A més, respon:

e) Un company proposa docker push auroralibros/aurora-api:produccio a cada desplegament. Dona tres arguments tècnics concrets per rebutjar-ho i una alternativa.

Solucions

Solució a l'exercici 1

# 1
docker system df --format "table {{.Type}}\t{{.TotalCount}}\t{{.Size}}" | head -2
TYPE      TOTAL     SIZE
Images    9         842.3MB
# 2
for T in 1.1 1 latest estable sha-abc1234; do
  docker image tag auroralibros/aurora-api:1.1.0 auroralibros/aurora-api:$T
done

# 3
docker system df --format "table {{.Type}}\t{{.TotalCount}}\t{{.Size}}" | head -2
TYPE      TOTAL     SIZE
Images    9         842.3MB

Ni el recompte ni la mida han canviat. El recompte continua sent 9 perquè docker system df compta imatges, no referències, i les sis etiquetes són una sola imatge. docker image tag escriu una entrada en un índex de noms; no toca ni un byte de les capes.

# 4
docker image ls auroralibros/aurora-api --format "table {{.Tag}}\t{{.ID}}"
TAG           IMAGE ID
1             4e9c7d2a8f31
1.1           4e9c7d2a8f31
1.1.0         4e9c7d2a8f31
estable       4e9c7d2a8f31
latest        4e9c7d2a8f31
sha-abc1234   4e9c7d2a8f31
# 5
docker image rm auroralibros/aurora-api:estable auroralibros/aurora-api:sha-abc1234
Untagged: auroralibros/aurora-api:estable
Untagged: auroralibros/aurora-api:sha-abc1234

Només Untagged, sense Deleted: queden quatre referències apuntant a la imatge, així que les dades continuen sent-hi. Si esborressis les sis, l'última mostraria Untagged i una llista de Deleted: sha256:…, una per capa exclusiva. El Deleted apareix únicament quan cau l'última referència, tal com vas veure a la lliçó 02-05.

# 6
cd ~/aurora-libros/api
echo "// canvi per a l'exercici" >> server.js
docker build -q -t auroralibros/aurora-api:1.1.0 .
docker image ls auroralibros/aurora-api --format "table {{.Tag}}\t{{.ID}}"
TAG      IMAGE ID
1.1.0    b3f7e2a91c48
1        4e9c7d2a8f31
1.1      4e9c7d2a8f31
latest   4e9c7d2a8f31

Només 1.1.0 s'ha mogut a la imatge nova. Les altres tres continuen apuntant a la vella, que ja no és dangling perquè conserva noms. Això demostra que etiquetar és un acte explícit: Docker no propaga res. Si vols que 1.1, 1 i latest segueixin la versió nova, cal reetiquetar-les a mà (o generar totes les etiquetes al mateix docker build amb diversos -t, com a l'script de l'apartat 10).

I compte amb el que acabes de fer sense voler: has reescrit el contingut de l'etiqueta 1.1.0. En local és inofensiu; publicada, seria exactament l'antipatró de l'apartat 9.

Solució a l'exercici 2

# 1. Auditoria prèvia
docker image inspect auroralibros/aurora-api:1.1.0 --format '{{range .Config.Env}}{{println .}}{{end}}'
docker image history auroralibros/aurora-api:1.1.0 --no-trunc --format "{{.CreatedBy}}" \
  | grep -iE "password|secret|token|api[_-]?key" || echo "OK: sense secrets"
docker run --rm --entrypoint ls auroralibros/aurora-api:1.1.0 -la /app
OK: sense secrets
# 2
docker login -u auroralibros
docker push auroralibros/aurora-api:1.1.0
9c1e4a7f2b9d: Pushed
7d3c9f2e8a51: Pushed
2f8e1a9c4b73: Pushed
b8f4e2a91c37: Mounted from library/node
4a1b8c2f9e3d: Mounted from library/node
8f3e1d7c5b9a: Mounted from library/node
e5c2a8f14b76: Mounted from library/node
1.1.0: digest: sha256:c4f81b2e9a7d... size: 1785

Tres Pushed i quatre Mounted from. Les tres pujades són les capes que genera el teu Dockerfile: COPY package*.json, RUN npm ci i COPY . .. Les quatre muntades pertanyen a node:22-alpine, que ja era a Docker Hub: el registre les referencia en lloc de rebre-les. De 167 MB d'imatge se'n transfereixen uns 25. És el mateix estalvi de les capes compartides de la lliçó 01-05, ara a la xarxa.

# 3
docker push auroralibros/aurora-api:latest
9c1e4a7f2b9d: Layer already exists
...
latest: digest: sha256:c4f81b2e9a7d... size: 1785

Instantani perquè no es transfereix res: totes les capes ja són al registre i el manifest és idèntic. L'única cosa que passa és que es crea un nom nou apuntant al mateix digest. Publicar deu etiquetes de la mateixa imatge costa pràcticament el mateix que publicar-ne una.

# 4
docker manifest inspect auroralibros/aurora-api:1.1.0 | sha256sum
docker manifest inspect auroralibros/aurora-api:latest | sha256sum
b8e2f4a91c37d5e8a2f6c9b1d4e7a3f8c5b2e9d6a1f4c7b0e3a6d9f2c5b8e1a4  -
b8e2f4a91c37d5e8a2f6c9b1d4e7a3f8c5b2e9d6a1f4c7b0e3a6d9f2c5b8e1a4  -

Idèntics.

# 5
docker image rm -f $(docker image ls -q auroralibros/aurora-api)
docker image ls auroralibros/aurora-api
docker run -d --name des-de-registre -p 3000:3000 auroralibros/aurora-api:1.1.0
sleep 45
curl -s http://localhost:3000/salut
docker ps --filter name=des-de-registre --format "{{.Names}}: {{.Status}}"
(llistat buit)
Unable to find image 'auroralibros/aurora-api:1.1.0' locally
1.1.0: Pulling from auroralibros/aurora-api
...
{"servei":"aurora-api","version":"1.0.0","db":"ko","cache":"ko",...}
des-de-registre: Up 45 seconds (unhealthy)

Funciona des de zero, i el healthcheck de la lliçó 02-04 fa la seva feina: unhealthy perquè no hi ha PostgreSQL ni Redis, exactament l'esperat fins al mòdul 3.

# 6. Per digest
DIGEST=$(docker image ls --digests auroralibros/aurora-api --format "{{.Digest}}" | head -1)
echo "$DIGEST"
docker rm -f des-de-registre
docker run -d --name per-digest -p 3000:3000 "auroralibros/aurora-api@$DIGEST"
docker ps --filter name=per-digest --format "{{.Image}}"
sha256:c4f81b2e9a7d3061f5b8c2e4a9d7f3b1e6c8a2d4f9b7e3c1a5d8f2b6e4c9a7d3
auroralibros/aurora-api@sha256:c4f81b2e9a7d3061f5b8c2e4a9d7f3b1e6c8a2d4f9b7e3c1a5d8f2b6e4c9a7d3

Fixa't en la columna IMAGE de docker ps: mostra el digest, no una etiqueta. Aquell contenidor està lligat criptogràficament a un contingut concret, i cap reescriptura d'etiquetes al registre no pot canviar el que executa.

docker rm -f per-digest

Solució a l'exercici 3

(a) Release estable 1.2.0 des de 9c4e1a7:

docker build -t auroralibros/aurora-api:1.2.0 \
             -t auroralibros/aurora-api:1.2 \
             -t auroralibros/aurora-api:1 \
             -t auroralibros/aurora-api:latest \
             -t auroralibros/aurora-api:sha-9c4e1a7 \
             --build-arg VERSION=1.2.0 --build-arg REVISION=9c4e1a7 \
             --pull ./api

És una funcionalitat compatible (afegeix un endpoint sense canviar els existents), així que puja el número MENOR. En producció es desplega 1.2.0, l'etiqueta immutable. Les mòbils 1.2, 1 i latest es publiquen per comoditat de qui desenvolupa, i sha-9c4e1a7 dona traçabilitat exacta al commit.

(b) Correcció urgent de seguretat:

docker build -t auroralibros/aurora-api:1.2.1 \
             -t auroralibros/aurora-api:1.2 \
             -t auroralibros/aurora-api:1 \
             -t auroralibros/aurora-api:latest \
             -t auroralibros/aurora-api:sha-4b8f2c1 \
             --build-arg VERSION=1.2.1 --build-arg REVISION=4b8f2c1 \
             --pull --no-cache ./api

Puja el PEDAÇ: és una correcció compatible. Es desplega 1.2.1. Tres decisions importants:

  • No es toca 1.2.0. Continua existint i continua apuntant a la imatge antiga. Això permet un rollback immediat si 1.2.1 surt malament, i és tota la diferència entre un incident controlat i un que s'agreuja.
  • --no-cache, perquè es tracta d'un pedaç de seguretat i no vols reutilitzar capes que puguin contenir la versió vulnerable d'una dependència.
  • --pull, perquè la base node:22-alpine porti també els seus pedaços.

Si la vulnerabilitat fos a la mateixa 1.2.0, la resposta correcta no és esborrar aquella etiqueta (trencaria tothom qui la fes servir, inclosos els qui encara no han pogut actualitzar), sinó documentar-la com a obsoleta a la descripció del repositori i comunicar l'actualització.

(c) Pull request 87:

docker build -t auroralibros/aurora-api:pr-87 \
             -t auroralibros/aurora-api:sha-e1d7a92 \
             --build-arg VERSION=1.2.1-pr87 --build-arg REVISION=e1d7a92 ./api

Etiqueta efímera pr-87, mòbil (se sobreescriu amb cada push a la branca de la PR), i una sha-* immutable per poder reproduir exactament el que s'ha provat. No es publiquen latest, 1.2 ni cap mòbil de la sèrie estable: contaminarien el canal que consumeixen altres. En tancar la PR, pr-87 s'esborra; és un dels pocs esborrats d'etiqueta legítims, perquè ningú no la fa servir en producció per definició.

(d) Canvi incompatible a /llibres:

docker build -t auroralibros/aurora-api:2.0.0-rc.1 \
             -t auroralibros/aurora-api:sha-a72f9c3 ./api

Puja el número MAJOR, perquè trenca els consumidors existents (el web actual espera un array). I es publica primer com a candidata 2.0.0-rc.1, no com a estable.

A més, latest no es mou encara. Aquest és el matís que més equips obliden: si latest saltés a la 2.0.0, qualsevol que estigués fent servir latest en un entorn de proves veuria com se li trenca el web sense haver canviat res. La seqüència correcta és: publicar la RC, actualitzar aurora-web per consumir el format nou, provar-los tots dos junts, i només aleshores publicar 2.0.0 i moure latest i 2. L'etiqueta 1 continua apuntant a la sèrie 1 per a qui necessiti temps per migrar.

(e) Tres arguments contra :produccio:

  1. No és reproduïble ni auditable. La pregunta "quin codi hi ha en producció?" deixa de tenir resposta: l'etiqueta s'ha mogut cinquanta vegades i no queda registre d'on apunta avui ni d'on apuntava durant l'incident del dimarts passat.
  2. Impossibilita el rollback. En moure :produccio a la versió nova, l'anterior perd la seva única referència. Tornar enrere exigeix saber l'IMAGE ID o el digest antic, i sense un registre escrit d'això, no hi ha marxa enrere.
  3. Provoca desplegaments heterogenis. Amb imagePullPolicy: Always o després d'un reinici, unes rèpliques descarreguen la versió nova i altres conserven la vella: la mateixa etiqueta executant dos codis alhora, amb errors intermitents impossibles de reproduir.

Un quart argument, si cal: acobla la imatge a l'entorn, trencant el principi de "construir una vegada, desplegar a tot arreu". El mateix artefacte ha de servir per a proves i per a producció; el que canvia és la configuració injectada, no la imatge.

L'alternativa: etiquetar per versió (1.2.1) i desar a Git, dins del repositori de desplegament, quina versió correspon a cada entorn:

# desplegament/produccio.yaml
imatge: auroralibros/aurora-api:1.2.1

# desplegament/staging.yaml
imatge: auroralibros/aurora-api:1.3.0-rc.2

Així, "què hi ha en producció" es respon amb un git log d'un fitxer, el rollback és revertir un commit, i l'historial complet de desplegaments queda auditat. És exactament el plantejament que es desenvoluparà a la lliçó 06-07, sobre estratègies de desplegament i rollback.

Conclusió

Has tancat el cicle. docker image tag no copia res: crea referències, i quatre etiquetes de la mateixa imatge ocupen exactament el que una, com va confirmar docker system df sense moure's ni un byte. El nom complet registre/usuari/repositori:etiqueta és el que decideix la destinació d'un push —no existeix cap opció --registry—, i per això un denied gairebé sempre és un nom sense espai de noms abans que un problema de credencials. Has distingit les etiquetes fixes de les mòbils, has vist per què etiquetar per entorn és un antipatró que esborra la traçabilitat, i t'endús la regla que més disgustos evita: en producció es desplega per etiqueta immutable o per digest, mai per latest, amb @sha256:… com a única garantia criptogràfica real.

I has publicat. auroralibros/aurora-api:1.1.0 viu a Docker Hub i a GitHub Container Registry, amb el mateix digest a tots dos perquè la imatge és idèntica bit a bit. Saps llegir la sortida d'un push —Pushed per a les teves tres capes, Mounted from library/node per a les quatre que el registre ja tenia, i dels 167 MB només en van viatjar uns 25—, verificar-la contra el registre remot amb docker manifest inspect, fer privat un repositori i repartir permisos per equips amb el principi de mínim privilegi. I saps per què esborrar una etiqueta publicada trenca reinicis, escalats i rollbacks aliens, i per què davant d'un secret filtrat l'única cosa que serveix és rotar la credencial.

Fes balanç del que ha canviat en aquest mòdul. Vas començar amb un repositori de codi, quinze passos manuals d'onboarding i una API que només arrencava en màquines amb Node 22, nvm, PostgreSQL i Redis instal·lats a mà. Acabes amb una imatge publicada que s'executa amb una sola comanda en qualsevol màquina del món amb Docker: sense instal·lar Node, sense npm install, sense conèixer el projecte. Pel camí has entès el context de construcció i l'has retallat de 23,41 MB a 47,83 kB amb un .dockerignore, has domat la memòria cau de capes fins a passar de 46,7 a 1,4 segons per build, has escrit un Dockerfile línia a línia justificant cada decisió, l'has professionalitzat amb usuari sense privilegis, metadades OCI i un healthcheck real contra /salut, i has après a mantenir el teu magatzem d'imatges sense destruir el que importa. Els passos 1 a 5 d'aquella llista de quinze han desaparegut.

Però la teva imatge continua retornant ECONNREFUSED a /llibres, i aquesta fallada fa tres lliçons que t'espera. Dins del contenidor, localhost és el contenidor mateix: no hi ha cap PostgreSQL ni cap Redis allà dins, i per això el healthcheck marca unhealthy amb tota la raó. Al mòdul 3, Contenidors Docker, aquests contenidors deixen de ser demostracions aïllades i comencen a funcionar de debò: dominaràs les opcions de docker run i el cicle de vida complet, aprendràs a inspeccionar i depurar un contenidor per dins, crearàs una xarxa pròpia on aurora-api trobi aurora-db cridant-la pel seu nom, donaràs a PostgreSQL un volum perquè el catàleg de vuit llibres sobrevisqui a la destrucció del contenidor, i posaràs límits de memòria i polítiques de reinici als quatre serveis. En acabar-lo, curl http://localhost:3000/llibres retornarà per fi El jardín de senderos que se bifurcan, Rayuela i els altres sis títols, servits des d'una base de dades real en un contenidor real. La plataforma d'Aurora Libros començarà a estar viva.

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