Arriba el moment de la pràctica seriosa. En aquesta lliçó no llegiràs teoria: teclejaràs, t'equivocaràs i veuràs resultats al navegador. Començaràs amb el contenidor més simple possible —un que imprimeix alguna cosa i mor— per entendre el cicle més curt que existeix. Després aixecaràs un servidor web real en segon pla, en publicaràs el port i el visitaràs des del navegador, aprenent pel camí què significa exactament -p 8080:80, que és probablement l'opció que escriuràs més vegades a la teva carrera. Recorreràs el cicle complet d'aquest contenidor: veure'l, llegir-ne els registres, aturar-lo, arrencar-lo i esborrar-lo. Després entraràs dins d'un contenidor amb un shell interactiu per tocar el seu sistema de fitxers aïllat amb les teves pròpies mans. I acabaràs servint la primera pàgina web d'Aurora Libros des d'un contenidor, muntant-hi a dins un fitxer de la teva màquina.

Contingut

  1. Abans de començar: comprovacions
  2. El teu primer contenidor: en primer pla
  3. Què passa quan el procés acaba
  4. Un contenidor de servei en segon pla
  5. El mapatge de ports explicat
  6. El cicle complet: logs, stop, start i rm
  7. Un contenidor interactiu: entrar a dins
  8. Servint la pàgina d'Aurora Libros amb -v
  9. Neteja final

  1. Abans de començar: comprovacions

Assegura't que tot està en ordre:

docker version
docker ps

El primer ha de mostrar els blocs Client i Server. El segon, una taula (probablement buida) sense errors. Si alguna cosa falla, torna a la lliçó 01-02.

Comprova també que el port que farem servir està lliure:

ss -tln | grep 8080 || echo "Port 8080 lliure"

ss -tln llista els ports TCP en escolta (-t TCP, -l en escolta, -n sense resoldre noms). Si no hi ha coincidències, grep falla i el || executa l'echo. Si alguna cosa estigués escoltant al 8080, fes servir el 8081 a totes les comandes de la lliçó.

  1. El teu primer contenidor: en primer pla

Comencem pel mínim:

docker run alpine:3.20 echo "Hola des d'Aurora Libros"
Hola des d'Aurora Libros

Sembla trivial, però han passat moltes coses. Desglossament de la línia:

Part Significat
docker run Crea un contenidor nou i l'arrenca
alpine:3.20 La imatge a partir de la qual crear-lo
echo "Hola des d'Aurora Libros" La comanda que s'ha d'executar dins del contenidor, substituint la comanda per defecte de la imatge

I la seqüència interna, que ja coneixes de la lliçó 01-03: el client va cridar el daemon, el daemon va comprovar que tenia alpine:3.20 en local, va crear un contenidor apilant una capa d'escriptura sobre les capes de la imatge, va demanar a containerd que arrenqués el procés echo, aquest va escriure a la seva sortida estàndard, i aquesta sortida es va transmetre pel socket fins al teu terminal.

Com que no has fet servir -d, el contenidor s'executa en primer pla: el teu terminal queda connectat a la seva sortida i no en recupera el control fins que el procés acaba.

Provem alguna cosa una mica més llarga:

docker run alpine:3.20 sh -c "echo 'Cataleg:'; echo '- Rayuela'; echo '- El jardín de senderos que se bifurcan'"
Cataleg:
- Rayuela
- El jardín de senderos que se bifurcan

Aquí la comanda és sh -c "...": es llança un shell dins del contenidor i se li passa una cadena amb diverses ordres. És el patró habitual quan necessites encadenar comandes, perquè docker run només accepta un executable amb els seus arguments, no una línia de shell amb ; o &&.

I alguna cosa que duri una estona, perquè vegis el primer pla de debò:

docker run alpine:3.20 sh -c "for i in 1 2 3 4 5; do echo \"Processant la comanda \$i\"; sleep 1; done"
Processant la comanda 1
Processant la comanda 2
Processant la comanda 3
Processant la comanda 4
Processant la comanda 5

Les línies surten d'una en una, amb un segon de pausa. El teu terminal està bloquejat mentrestant: això és primer pla. Si prems Ctrl+C durant l'execució, envies un senyal d'interrupció al procés i el contenidor acaba abans d'hora.

  1. Què passa quan el procés acaba

Aquesta és la regla més important de tota la lliçó:

Un contenidor viu exactament el que viu el seu procés principal. Quan aquest procés acaba, el contenidor passa a estat Exited. No hi ha res més per "apagar".

Comprova-ho:

docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES

Buit: cap dels tres contenidors anteriors continua viu.

docker ps -a --format "table {{.Names}}\t{{.Command}}\t{{.Status}}"
NAMES               COMMAND                  STATUS
happy_bardeen       "sh -c 'for i in 1 2…"   Exited (0) 10 seconds ago
festive_curie       "sh -c 'echo Cataleg…"   Exited (0) 1 minute ago
vibrant_torvalds    "echo 'Hola des d'Au…"   Exited (0) 2 minutes ago

Tots tres hi són, aturats, amb codi de sortida 0 (van acabar bé). Ocupen una mica de disc cadascun.

Aquest comportament explica un ensopec clàssic de principiants:

docker run alpine:3.20
docker ps

La segona comanda no mostra res, i la reacció típica és "el contenidor no arrenca". Sí que va arrencar: la imatge alpine té com a comanda per defecte /bin/sh, un shell que, en no tenir terminal interactiu ni entrada, no troba res a fer i acaba immediatament. El contenidor va fer la seva feina i va morir en mil·lisegons.

Perquè un contenidor continuï viu li cal un procés que no acabi: un servidor web esperant peticions, una base de dades, o un shell amb un terminal interactiu connectat. Això és el que veurem ara.

  1. Un contenidor de servei en segon pla

Aixequem un servidor web Nginx, que és exactament això: un procés que es queda escoltant indefinidament.

docker run -d --name aurora-web-demo -p 8080:80 nginx:alpine
Unable to find image 'nginx:alpine' locally
alpine: Pulling from library/nginx
f18232174bc9: Already exists
2c8d4f7e1a09: Pull complete
...
Status: Downloaded newer image for nginx:alpine
7c3e9a1f5b2d84e0a6c7d9f3b1e5a7c9d2f4b6e8a0c2d4f6b8e0a2c4d6f8b0e2

Analitzem-ho opció per opció, perquè cadascuna resol un problema concret:

Opció Nom llarg Què fa
-d --detach Executa el contenidor en segon pla i et retorna el control del terminal immediatament, imprimint-ne l'ID
--name aurora-web-demo Li dona un nom llegible en lloc d'un d'aleatori. T'hi podràs referir per aquest nom a totes les comandes
-p 8080:80 --publish Publica el port 80 del contenidor al port 8080 de la teva màquina
nginx:alpine La imatge. No hi indiquem comanda, així que es fa servir la de la imatge: arrencar Nginx

Aquella llarga cadena hexadecimal de l'última línia és l'ID complet del contenidor. Com que has fet servir --name, no et caldrà.

Ara sí:

docker ps
CONTAINER ID   IMAGE          COMMAND                  STATUS         PORTS                                     NAMES
7c3e9a1f5b2d   nginx:alpine   "/docker-entrypoint.…"   Up 8 seconds   0.0.0.0:8080->80/tcp, [::]:8080->80/tcp   aurora-web-demo

Up 8 seconds: està viu, i continuarà viu perquè Nginx no acaba mai per si sol. Fixa't que docker ps sense -a ja el mostra, a diferència dels contenidors anteriors.

  1. El mapatge de ports explicat

La columna PORTS mereix un apartat propi perquè és on més gent s'encalla.

Un contenidor té la seva pròpia pila de xarxa: la seva pròpia interfície, la seva pròpia adreça IP interna i els seus propis ports. Nginx escolta al port 80 d'aquesta xarxa interna, que des de la teva màquina no és accessible directament. L'opció -p crea un pont.

flowchart LR
    NAV["El teu navegador<br/>http://localhost:8080"]
    subgraph HOST["La teva màquina (amfitrió)"]
        P8080["Port 8080<br/>(reservat per Docker)"]
        subgraph NET["Xarxa bridge de Docker · 172.17.0.0/16"]
            subgraph CNT["Contenidor aurora-web-demo · 172.17.0.2"]
                P80["Port 80<br/>Nginx escoltant"]
            end
        end
    end
    NAV --> P8080
    P8080 -->|"-p 8080:80<br/>reenviament"| P80

La sintaxi es llegeix sempre de fora cap a dins:

-p PORT_DE_L_AMFITRIO:PORT_DEL_CONTENIDOR
  • El primer és el de la teva màquina: el que escrius al navegador. El tries tu i ha d'estar lliure.
  • El segon és el del contenidor: aquell on escolta l'aplicació. No el tries tu, el determina l'aplicació (Nginx fa servir el 80, PostgreSQL el 5432, Redis el 6379, i l'API d'Aurora Libros farà servir el 3000).

Variants útils:

Sintaxi Efecte
-p 8080:80 Port 8080 de totes les interfícies de l'amfitrió → 80 del contenidor
-p 127.0.0.1:8080:80 Només accessible des de la teva pròpia màquina, no des de la xarxa local. Més segur
-p 80:80 Port 80 de l'amfitrió (a Linux pot requerir privilegis)
-p 8080:80 -p 8443:443 Diversos ports publicats alhora
-P Publica tots els ports declarats per la imatge en ports aleatoris alts

Sense -p, el contenidor funciona perfectament però és inabastable des de fora. És el correcte per a serveis interns: al mòdul 4, aurora-db i aurora-cache no publicaran ports, perquè només han de ser accessibles des d'aurora-api, no des d'Internet.

Comprovació amb curl i amb el navegador

curl http://localhost:8080
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
...
</html>

Només les capçaleres, per veure l'estat HTTP:

curl -I http://localhost:8080
HTTP/1.1 200 OK
Server: nginx/1.27.4
Content-Type: text/html
Content-Length: 615

-I demana només les capçaleres (mètode HEAD). El 200 OK confirma que el servidor respon.

Obre ara http://localhost:8080 al teu navegador: hi veuràs la pàgina de benvinguda de Nginx. Acabes de servir un web des d'un contenidor sense haver instal·lat Nginx al teu sistema. Si executes nginx -v al teu terminal, el més probable és que la comanda no existeixi: tot és dins del contenidor.

  1. El cicle complet: logs, stop, start i rm

Veure els registres

docker logs aurora-web-demo
/docker-entrypoint.sh: Configuration complete; ready for start up
2026/08/04 10:15:03 [notice] 1#1: nginx/1.27.4
2026/08/04 10:15:03 [notice] 1#1: start worker processes
172.17.0.1 - - [04/Aug/2026:10:16:12 +0000] "GET / HTTP/1.1" 200 615 "-" "curl/8.5.0"
172.17.0.1 - - [04/Aug/2026:10:16:45 +0000] "GET / HTTP/1.1" 200 615 "-" "Mozilla/5.0 ..."

Aquí hi ha les teves dues visites: la de curl i la del navegador, identificades pel seu user agent. La IP 172.17.0.1 és la de la teva màquina vista des de la xarxa interna de Docker.

Opcions molt útils:

docker logs -f aurora-web-demo        # seguir en directe (Ctrl+C per sortir)
docker logs --tail 20 aurora-web-demo # només les 20 últimes línies
docker logs -t aurora-web-demo        # amb marca de temps de Docker
docker logs --since 5m aurora-web-demo # només els últims 5 minuts

Prova docker logs -f aurora-web-demo, torna a carregar el navegador i veuràs aparèixer la línia en temps real. Ctrl+C talla el seguiment sense afectar el contenidor (un dubte molt habitual: no, no l'atures).

Aturar el contenidor

docker stop aurora-web-demo
aurora-web-demo

Retorna el nom del que ha aturat. Verifica-ho:

docker ps
docker ps -a --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
NAMES              STATUS                     PORTS
aurora-web-demo    Exited (0) 5 seconds ago

Dues coses: docker ps ja no el mostra, i a docker ps -a la columna PORTS està buida (el mapatge del port 8080 s'ha alliberat). Confirma-ho:

curl http://localhost:8080
curl: (7) Failed to connect to localhost port 8080 after 0 ms: Connection refused

Ningú no escolta. El contenidor continua existint, però no està en execució.

Arrencar-lo de nou

docker start aurora-web-demo
curl -I http://localhost:8080
aurora-web-demo
HTTP/1.1 200 OK

Torna a funcionar, amb la mateixa configuració. I aquí hi ha un detall conceptual important: no has hagut de repetir -p 8080:80. La configuració es va fixar en crear el contenidor i en forma part. docker start només el reprèn.

Corol·lari: no pots canviar el mapatge de ports d'un contenidor existent. Si necessites un altre port, cal esborrar-lo i crear-ne un de nou. És una manifestació de la filosofia de contenidors rebutjables.

docker logs aurora-web-demo

Hi veuràs els registres de la primera arrencada i els de la segona, un darrere l'altre: els registres es conserven mentre el contenidor existeixi.

Esborrar-lo

docker stop aurora-web-demo
docker rm aurora-web-demo
aurora-web-demo
aurora-web-demo

Si intentes docker rm sense aturar-lo primer:

Error response from daemon: cannot remove container "aurora-web-demo": container is running:
stop the container before removing or force remove

Ho pots forçar amb docker rm -f aurora-web-demo, que atura i esborra en un sol pas. És còmode en desenvolupament, però mata el procés sense donar-li temps de tancar-se ordenadament.

Verifica-ho:

docker ps -a
docker images

El contenidor ha desaparegut de la llista; la imatge nginx:alpine hi continua sent. Un cop més: esborrar un contenidor no esborra la seva imatge.

  1. Un contenidor interactiu: entrar a dins

Fins ara executaves comandes soltes. Ara obriràs un shell dins d'un contenidor i t'hi mouràs.

docker run -it --rm alpine:3.20 sh

Tres opcions noves:

Opció Nom llarg Què fa
-i --interactive Manté oberta l'entrada estàndard, perquè puguis escriure
-t --tty Assigna un terminal virtual: prompt, colors, format
--rm Esborra el contenidor automàticament en sortir

-i i -t gairebé sempre van junts (-it): amb -i sense -t podries escriure però sense prompt ni format; amb -t sense -i veuries el prompt però no podries escriure.

El teu prompt canvia:

/ #

Ets dins del contenidor. Explora:

hostname
4a7f2b9e1c03

El nom d'amfitrió és l'ID curt del contenidor, no el de la teva màquina.

ls /
bin    dev    etc    home   lib    media  mnt    opt    proc   root
run    sbin   srv    sys    tmp    usr    var

Un sistema de fitxers Linux complet... que no és el teu. Comprova-ho:

cat /etc/os-release
NAME="Alpine Linux"
PRETTY_NAME="Alpine Linux v3.20"

Alpine, encara que la teva màquina sigui Ubuntu, Windows o macOS.

ls /home
ls /root

Buits: els teus documents no són aquí. L'aïllament del sistema de fitxers és real.

ps aux
PID   USER     TIME  COMMAND
    1 root      0:00 sh
    7 root      0:00 ps aux

Això és revelador: només hi ha dos processos, i el shell té el PID 1. A la teva màquina hi ha centenars de processos i el PID 1 és systemd. El contenidor té el seu propi espai de noms de processos i no veu els de l'amfitrió (el mecanisme exacte, a la lliçó 05-07).

I tanmateix:

uname -r
6.8.0-52-generic

El nucli és el del teu amfitrió. Espai d'usuari d'Alpine, nucli d'Ubuntu. Aquesta frase resumeix què és un contenidor.

Instal·lar alguna cosa a dins

Alpine fa servir apk com a gestor de paquets:

curl --version
sh: curl: not found

No està instal·lat. Instal·lem-lo:

apk add --no-cache curl
fetch https://dl-cdn.alpinelinux.org/alpine/v3.20/main/x86_64/APKINDEX.tar.gz
(1/8) Installing ca-certificates (20240705-r0)
...
(8/8) Installing curl (8.12.1-r0)
Executing busybox-1.36.1-r29.trigger
OK: 13 MiB in 23 packages
  • apk add instal·la paquets (l'equivalent d'apt install).
  • --no-cache evita desar l'índex de paquets al disc; és l'opció estàndard en contenidors perquè en redueix la mida.
curl --version
curl 8.12.1 (x86_64-alpine-linux-musl) libcurl/8.12.1 ...

Funciona. I una cosa important: has instal·lat curl al contenidor, no a la teva màquina. El teu sistema no s'ha tocat.

Crea també un fitxer per a la comprovació següent:

echo "Aurora Libros S.L." > /prova.txt
cat /prova.txt

Ara surt:

exit

Tornes al teu prompt normal.

La màgia de --rm

docker ps -a --filter "ancestor=alpine:3.20"

No hi apareix. Gràcies a --rm, el contenidor es va eliminar automàticament en acabar el seu procés. Amb ell se'n van anar la seva capa d'escriptura, el curl que hi vas instal·lar i el fitxer /prova.txt.

Confirma-ho obrint-ne un altre:

docker run -it --rm alpine:3.20 sh
curl --version
cat /prova.txt
exit
sh: curl: not found
cat: can't open '/prova.txt': No such file or directory

És la lliçó 01-05 en carn i ossos: cada contenidor neix net de la imatge immutable. Els canvis viuen a la seva capa d'escriptura i moren amb ell.

--rm és una opció excel·lent per a proves i comandes puntuals: t'estalvia l'acumulació de contenidors aturats. No la facis servir en serveis que vulguis poder reprendre.

  1. Servint la pàgina d'Aurora Libros amb -v

Nginx servint la seva pàgina per defecte està bé per provar, però volem el nostre contingut. Crearem la primera pàgina d'Aurora Libros.

Crear la pàgina

mkdir -p ~/aurora-libros/web
cd ~/aurora-libros/web

Crea index.html amb aquest contingut:

<!DOCTYPE html>
<html lang="ca">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Aurora Libros · Llibreria en línia</title>
  <style>
    body { font-family: system-ui, sans-serif; max-width: 42rem; margin: 3rem auto; padding: 0 1rem; color: #222; }
    h1 { color: #6b3fa0; }
    li { margin-bottom: .6rem; }
    footer { margin-top: 2rem; font-size: .85rem; color: #666; }
  </style>
</head>
<body>
  <h1>Aurora Libros</h1>
  <p>La teva llibreria en línia. Catàleg destacat:</p>
  <ul>
    <li><strong>El jardín de senderos que se bifurcan</strong> — Jorge Luis Borges — 14,50 €</li>
    <li><strong>Rayuela</strong> — Julio Cortázar — 19,90 €</li>
    <li><strong>Cien años de soledad</strong> — Gabriel García Márquez — 17,95 €</li>
  </ul>
  <footer>Aurora Libros S.L. · Servit des d'un contenidor Docker</footer>
</body>
</html>

Muntar-la al contenidor

docker run -d --name aurora-web-demo -p 8080:80 \
  -v ~/aurora-libros/web/index.html:/usr/share/nginx/html/index.html:ro \
  nginx:alpine

L'opció nova és -v (--volume), i la seva sintaxi es llegeix igual que la de -p, de fora cap a dins:

-v RUTA_A_LA_TEVA_MAQUINA:RUTA_DINS_DEL_CONTENIDOR[:opcions]
Part Significat
~/aurora-libros/web/index.html El fitxer de la teva màquina. Ha de ser una ruta absoluta (~ l'expandeix el shell)
/usr/share/nginx/html/index.html On apareixerà dins del contenidor. Aquesta ruta és on la imatge de Nginx busca els seus fitxers
:ro read-only: el contenidor el pot llegir però no modificar. Bona pràctica quan no hi ha d'escriure

Comprova-ho:

curl http://localhost:8080
<!DOCTYPE html>
<html lang="ca">
...
  <h1>Aurora Libros</h1>
...

I al navegador, a http://localhost:8080, hi veuràs la pàgina d'Aurora Libros amb el seu catàleg. El fitxer continua estant al teu disc; el contenidor simplement el veu muntat al seu sistema de fitxers.

L'avantatge: edició en directe

Edita index.html al teu editor i afegeix-hi un llibre:

    <li><strong>La sombra del viento</strong> — Carlos Ruiz Zafón — 21,00 €</li>

Desa i torna a carregar el navegador. El canvi apareix a l'instant, sense reconstruir res ni reiniciar el contenidor. El muntatge és una finestra en directe entre el teu disc i el contenidor.

Aquest patró —muntar el codi font dins del contenidor per desenvolupar sense reconstruir— és la base del flux de treball de desenvolupament local que veuràs a la lliçó 04-07.

Nota important. Aquest ús de -v és purament introductori: és un bind mount, que munta una ruta de la teva màquina. N'hi ha un altre tipus, els volums gestionats per Docker, que és el que necessitarà aurora-db per no perdre el catàleg. Les diferències, quan fer servir cadascun i les implicacions de permisos i rendiment són el contingut complet de la lliçó 03-06.

  1. Neteja final

Deixem el sistema com estava:

docker stop aurora-web-demo
docker rm aurora-web-demo
docker container prune -f
  • Les dues primeres aturen i esborren el contenidor de Nginx.
  • container prune -f elimina qualsevol altre contenidor aturat que hagis deixat pel camí.

Comprova-ho:

docker ps -a
docker images
docker system df

Sense contenidors; les imatges (alpine:3.20, nginx:alpine, hello-world) intactes, a punt per reutilitzar-se sense tornar-les a descarregar. El teu fitxer ~/aurora-libros/web/index.html també continua al seu lloc: el necessitaràs a les properes lliçons.

Errors Habituals i Consells

  • Bind for 0.0.0.0:8080 failed: port is already allocated. El port de l'amfitrió ja està ocupat, gairebé sempre per un altre contenidor teu. Busca'l amb docker ps i atura'l, o publica en un altre port (-p 8081:80).
  • Invertir l'ordre de -p. -p 80:8080 amb Nginx no funciona: estaries reenviant el port 80 de l'amfitrió al 8080 del contenidor, on no escolta ningú. Recorda: amfitrió:contenidor, de fora cap a dins.
  • docker run -d amb una comanda que acaba. docker run -d alpine echo hola arrenca i mor a l'instant. -d no manté viu res: només desconnecta el teu terminal. Per continuar viu cal un procés que no acabi.
  • Rutes relatives a -v. -v ./web:/usr/share/nginx/html pot fallar segons la versió i el shell. Fes servir sempre rutes absolutes: $(pwd)/web o ~/....
  • Muntar un directori sobre un que ja té contingut. El muntatge oculta el que hi hagués en aquesta ruta dins de la imatge. Si muntes un directori buit sobre /usr/share/nginx/html, Nginx retornarà 403 o 404 perquè ja no veu el seu index.html.
  • Confondre Ctrl+C amb aturar el contenidor. A docker logs -f o docker attach, Ctrl+C només talla la teva connexió. En un contenidor en primer pla, sí que interromp el procés i l'acaba.
  • Consell: fes servir --rm per a tot el que sigui rebutjable. Proves, comandes puntuals, shells exploratoris. T'estalviarà neteges.
  • Consell: posa sempre nom amb --name. aurora-web-demo és infinitament més manejable que stupefied_swanson, i fa que els filtres per nom funcionin.
  • Consell: si un contenidor mor tot just arrencar, docker logs primer. Els registres es conserven encara que estigui aturat, i allà hi ha gairebé sempre l'explicació.

Exercicis

Exercici 1: dos aparadors alhora

Aixeca dos contenidors de Nginx simultàniament a partir de la mateixa imatge: un anomenat aurora-web-ca al port 8080 i un altre aurora-web-en al 8081, cadascun servint un index.html diferent (un en català i l'altre en anglès). Comprova amb curl que cada port retorna la seva pàgina. Després respon: quantes còpies de la imatge nginx:alpine hi ha al teu disc?, quant ocupa la capa d'escriptura de cada contenidor (docker ps -as)? En acabar, esborra'ls tots dos amb una única comanda.

Exercici 2: el detectiu de registres

Executa aquest contenidor, que està mal configurat expressament:

docker run -d --name aurora-fallada -p 8080:80 \
  -v /tmp/carpeta-que-no-faig-servir:/usr/share/nginx/html:ro \
  nginx:alpine

(Crea abans /tmp/carpeta-que-no-faig-servir buida amb mkdir -p /tmp/carpeta-que-no-faig-servir.)

Visita http://localhost:8080 i observa l'error. Després: (a) està el contenidor en execució?, (b) quin codi HTTP retorna i per què?, (c) fes servir docker logs per trobar el missatge d'error exacte de Nginx, i (d) explica en termes de muntatges què ha passat i com ho arreglaries.

Exercici 3: exploració interactiva comparada

Obre un contenidor interactiu d'alpine:3.20 i un altre de nginx:alpine (en dos terminals, tots dos amb --rm). En cadascun, esbrina: (a) el sistema operatiu de l'espai d'usuari, (b) la versió del nucli, (c) el PID 1 i quants processos hi ha, i (d) si existeix el directori /usr/share/nginx/html i què conté. Explica les diferències i les coincidències entre els dos contenidors, i què et diu cadascuna sobre la naturalesa dels contenidors.

Solucions

Solució a l'exercici 1

mkdir -p ~/aurora-libros/web-ca ~/aurora-libros/web-en
echo '<h1>Aurora Libros</h1><p>Benvingut a la nostra llibreria.</p>' > ~/aurora-libros/web-ca/index.html
echo '<h1>Aurora Books</h1><p>Welcome to our bookstore.</p>' > ~/aurora-libros/web-en/index.html

docker run -d --name aurora-web-ca -p 8080:80 \
  -v ~/aurora-libros/web-ca:/usr/share/nginx/html:ro nginx:alpine

docker run -d --name aurora-web-en -p 8081:80 \
  -v ~/aurora-libros/web-en:/usr/share/nginx/html:ro nginx:alpine

curl -s http://localhost:8080
curl -s http://localhost:8081
<h1>Aurora Libros</h1><p>Benvingut a la nostra llibreria.</p>
<h1>Aurora Books</h1><p>Welcome to our bookstore.</p>

Aquí es munten directoris en lloc de fitxers solts, que és el més habitual amb Nginx. Els dos contenidors fan servir ports diferents de l'amfitrió (8080 i 8081) però tots dos el 80 a dins: no hi ha conflicte, perquè cada contenidor té la seva pròpia pila de xarxa i el seu propi port 80.

docker images nginx
docker ps -as --format "table {{.Names}}\t{{.Size}}"
REPOSITORY   TAG      IMAGE ID       SIZE
nginx        alpine   3f8a4339aadd   52.5MB
NAMES            SIZE
aurora-web-en    1.09kB (virtual 52.5MB)
aurora-web-ca    1.09kB (virtual 52.5MB)

Respostes: hi ha una sola còpia de la imatge al disc, compartida pels dos contenidors. Cada capa d'escriptura ocupa al voltant d'1 kB. El "virtual 52.5MB" inclou les capes compartides de la imatge, que no es compten dues vegades. Aixecar el segon contenidor va costar pràcticament zero disc: és la densitat de la lliçó 01-01, mesurada.

docker rm -f aurora-web-ca aurora-web-en

rm -f atura i esborra en un sol pas, i accepta diversos noms.

Solució a l'exercici 2

(a) Sí, el contenidor està en execució:

docker ps --filter "name=aurora-fallada"
CONTAINER ID   IMAGE          STATUS         PORTS                  NAMES
b2c8f4a1e7d9   nginx:alpine   Up 20 seconds  0.0.0.0:8080->80/tcp   aurora-fallada

Nginx va arrencar correctament. El problema no és l'arrencada, sinó el contingut que serveix.

(b) Retorna 403 Forbidden:

curl -I http://localhost:8080
HTTP/1.1 403 Forbidden
Server: nginx/1.27.4

És 403 i no 404 perquè el directori arrel existeix (l'has muntat) però està buit: no hi ha cap index.html per servir i Nginx no permet llistar directoris per defecte.

(c) Els registres:

docker logs aurora-fallada
2026/08/04 11:02:41 [error] 30#30: *1 directory index of "/usr/share/nginx/html/" is forbidden,
client: 172.17.0.1, server: localhost, request: "GET / HTTP/1.1", host: "localhost:8080"

El missatge és explícit: directory index ... is forbidden.

(d) Què ha passat: el muntatge oculta el contingut original d'aquesta ruta a la imatge. La imatge nginx:alpine porta el seu propi index.html a /usr/share/nginx/html, però en muntar-hi a sobre un directori buit de la teva màquina, aquell contingut deixa de ser visible (no s'esborra: continua a la capa de la imatge, simplement queda tapat). Nginx troba un directori buit i respon 403.

Arranjaments possibles: posar un index.html a /tmp/carpeta-que-no-faig-servir, muntar un directori que sí que tingui contingut, o no muntar res si volies la pàgina per defecte.

echo '<h1>Aurora Libros</h1>' > /tmp/carpeta-que-no-faig-servir/index.html
curl -s http://localhost:8080
docker rm -f aurora-fallada

Fixa't que no cal reiniciar el contenidor: el muntatge és en directe, així que tan bon punt el fitxer existeix al teu disc, Nginx el serveix.

Solució a l'exercici 3

# Terminal 1
docker run -it --rm alpine:3.20 sh

# Terminal 2
docker run -it --rm nginx:alpine sh

(La imatge de Nginx també porta un shell, així que s'hi pot entrar igual.)

En cadascun:

cat /etc/os-release | head -2
uname -r
ps aux
ls /usr/share/nginx/html

Resultats i la seva lectura:

Comprovació alpine:3.20 nginx:alpine Què significa
(a) SO de l'espai d'usuari Alpine Linux v3.20 Alpine Linux (versió de la imatge base de Nginx) Totes dues es construeixen sobre Alpine: comparteixen capes base, com vas veure a 01-05
(b) Nucli El del teu amfitrió (p. ex. 6.8.0-52-generic) El mateix Confirma que tots dos contenidors comparteixen el nucli de l'amfitrió. No hi ha SO convidat
(c) PID 1 i nre. de processos sh com a PID 1, 2 processos sh com a PID 1, 2 processos Cada contenidor té el seu propi espai de noms de processos, aïllat. I compte: aquí Nginx no s'està executant, perquè en passar sh vas substituir la comanda per defecte de la imatge
(d) /usr/share/nginx/html No existeix (ls: ... No such file or directory) Existeix, amb index.html i 50x.html El sistema de fitxers l'aporta la imatge: són diferents perquè les imatges porten contingut diferent

Les conclusions importants: els contenidors comparteixen el nucli (b) però tenen sistemes de fitxers i espais de processos aïllats (c, d); dues imatges poden compartir la seva base i diferenciar-se només en les capes superiors (a); i passar una comanda a docker run substitueix la comanda per defecte de la imatge, per la qual cosa entrar amb sh a la imatge de Nginx et dona el sistema de fitxers de Nginx sense Nginx executant-se.

Conclusió

Ja has creat, gestionat i destruït contenidors amb les teves pròpies mans. T'endús la regla que governa tota la resta: un contenidor viu el que viu el seu procés principal; si aquest procés acaba, el contenidor passa a Exited, i per això docker run alpine "no fa res" mentre que docker run nginx:alpine es queda viu indefinidament.

Saps llançar contenidors en primer pla i en segon pla amb -d, posar-los nom amb --name, i sobretot publicar ports amb -p amfitrió:contenidor, llegint la sintaxi sempre de fora cap a dins; has comprovat que el mapatge es fixa en crear el contenidor i que docker start el reprèn amb la mateixa configuració. Has recorregut el cicle complet —logs, stop, start, rm—, has entrat dins d'un contenidor amb -it per veure amb els teus ulls l'aïllament del sistema de fitxers i dels processos al costat del nucli compartit, i has confirmat amb --rm que tot el que escrius a dins mor amb el contenidor. I has servit la primera pàgina d'Aurora Libros muntant un fitxer del teu disc amb -v, amb edició en directe inclosa.

Tens les tres peces del trencaclosques —imatges, contenidors i comandes— i una pàgina web funcionant. El que encara no tens és el problema real que justifica tot això. A l'última lliçó del mòdul, El Projecte del Curs: la Plataforma Aurora Libros, coneixeràs l'empresa, la seva arquitectura objectiu i el codi complet de la seva API, i intentaràs arrencar-la sense Docker per viure en primera persona el calvari que la resta del curs resoldrà.

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