Salta el contingut

Pas 3 - Contenidors en segon pla

Els contenidors del pas 1 feien una tasca i s'aturaven. Un servei, en canvi (un servidor web, una base de dades), ha d'estar funcionant contínuament. En aquest pas executarem un servidor NGINX en segon pla (mode detached o dimoni) i aprendrem a gestionar-lo.

Objectius del pas

  • Veure la diferència entre executar en primer pla i en segon pla (-d).
  • Donar nom als contenidors i gestionar-ne el cicle de vida: stop, start, restart, rm.
  • Consultar logs i executar ordres dins d'un contenidor en marxa (logs, exec).
  • Comprovar que els canvis fets dins d'un contenidor es perden quan s'elimina.

3.1 Primer pla: el terminal queda ocupat

docker run nginx:1.27-alpine

Aquesta vegada el contenidor no s'atura: NGINX és un servidor i el seu procés principal es queda escoltant peticions. El terminal queda "segrestat" mostrant els logs del servidor. Si premeu ++ctrl+c++, el procés rep el senyal, acaba, i el contenidor s'atura.

3.2 Segon pla: -d

docker run -d --name web nginx:1.27-alpine
b7e2f4c1a9d83e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f
  • -d (detached): el contenidor s'executa en segon pla i Docker ens retorna el terminal, mostrant només l'ID del contenidor.
  • --name web: li donem un nom per poder-nos-hi referir fàcilment. Els noms són únics: si torneu a executar la comanda, Docker es queixarà que el nom web ja està en ús.
docker ps
CONTAINER ID   IMAGE               COMMAND                  CREATED          STATUS          PORTS     NAMES
b7e2f4c1a9d8   nginx:1.27-alpine   "/docker-entrypoint.…"   10 seconds ago   Up 9 seconds    80/tcp    web

Ara l'estat és Up. La columna PORTS diu 80/tcp: NGINX escolta al port 80 dins del contenidor, però encara no hi podem accedir des de fora. Ho resoldrem al Pas 4.

3.3 Mirar què passa dins

Logs: tot el que el procés principal escriu per la sortida estàndard queda registrat.

docker logs web            # tots els logs fins ara
docker logs -f web         # segueix els logs en temps real (Ctrl+C per sortir)
docker logs --tail 20 web  # només les últimes 20 línies

Executar ordres dins d'un contenidor que ja està funcionant:

docker exec web ls /usr/share/nginx/html
docker exec web cat /etc/os-release
docker exec -it web sh     # obre un shell interactiu (alpine no té bash)

Dins del shell, proveu ps. Veureu que el procés amb PID 1 és nginx: master process: és el procés principal. Mentre ell visqui, el contenidor viurà. Sortiu amb exit: ara el contenidor no s'atura, perquè el sh que tanqueu no és el procés principal, només un procés afegit amb exec.

Consum de recursos i detalls:

docker stats web           # CPU, memòria i xarxa en temps real
docker top web             # processos del contenidor vistos des de l'amfitrió
docker inspect web         # tota la configuració en JSON (IP, muntatges, estat...)

docker run o docker exec?

docker run crea un contenidor nou a partir d'una imatge. docker exec executa una ordre dins d'un contenidor que ja existeix i està en marxa. Un error típic és fer docker run -it nginx sh per "entrar al servidor" i acabar dins d'un contenidor nou i buit, diferent del que volíeu inspeccionar.

3.4 El cicle de vida d'un contenidor

docker stop web       # atura (envia SIGTERM i, després de 10 s, SIGKILL)
docker ps -a          # estat: Exited
docker start web      # torna a arrencar EL MATEIX contenidor
docker restart web    # stop + start
docker pause web      # congela els processos (sense aturar-los)
docker unpause web
docker rm web         # error: no es pot eliminar un contenidor en marxa
docker rm -f web      # atura i elimina
stateDiagram-v2
    [*] --> Created: docker create
    Created --> Running: docker start
    [*] --> Running: docker run
    Running --> Paused: docker pause
    Paused --> Running: docker unpause
    Running --> Exited: docker stop / el proces acaba
    Exited --> Running: docker start
    Exited --> [*]: docker rm
    Running --> [*]: docker rm -f

docker run = docker create + docker start

docker create prepara el contenidor sense arrencar-lo (estat Created). És útil per configurar-lo abans d'engegar-lo, però a la pràctica gairebé sempre fareu servir docker run.

Polítiques de reinici

Què passa si l'amfitrió es reinicia? Per defecte, els contenidors queden aturats. Amb --restart podem indicar què ha de fer Docker:

Política Comportament
no (per defecte) No es reinicia mai automàticament
on-failure[:N] Es reinicia si el procés acaba amb error (fins a N intents)
always Es reinicia sempre, també en arrencar el dimoni de Docker
unless-stopped Com always, excepte si l'heu aturat vosaltres manualment
docker run -d --name web --restart unless-stopped nginx:1.27-alpine

3.5 Els contenidors són efímers

Aquest és un dels experiments més importants del bloc. Modifiquem la pàgina d'inici del servidor des de dins del contenidor:

docker exec web sh -c 'echo "<h1>Pagina modificada</h1>" > /usr/share/nginx/html/index.html'
docker exec web cat /usr/share/nginx/html/index.html

Ara aturem i tornem a arrencar el mateix contenidor:

docker stop web
docker start web
docker exec web cat /usr/share/nginx/html/index.html   # el canvi continua aquí

El canvi es manté, perquè és a la capa d'escriptura del contenidor, i aquesta capa existeix mentre existeixi el contenidor. Però ara l'eliminem i en creem un de nou:

docker rm -f web
docker run -d --name web nginx:1.27-alpine
docker exec web cat /usr/share/nginx/html/index.html   # torna a ser la pàgina original

El canvi s'ha perdut. El contenidor nou parteix de la imatge, que no ha canviat mai, amb una capa d'escriptura nova i buida.

flowchart LR
    I[(Imatge nginx)] --> A[Contenidor web 1<br/>index.html modificat]
    A -->|docker rm| X((eliminat))
    I --> B[Contenidor web 2<br/>index.html original]
    classDef img fill:#2563EB,stroke:#1E40AF,color:#FFFFFF
    classDef cont fill:#16A34A,stroke:#166534,color:#FFFFFF
    classDef del fill:#64748B,stroke:#475569,color:#FFFFFF
    class I img
    class A,B cont
    class X del

Aquesta és la raó de ser de dos passos posteriors:

  • Si volem que un contingut propi formi part de la imatge, cal construir una imatge personalitzada (Pas 8).
  • Si volem que unes dades sobrevisquin al contenidor (una base de dades, fitxers pujats...), cal guardar-les en un volum (Pas 6).

Miniactivitat - Cicle de vida

  1. Arrenqueu dos contenidors NGINX en segon pla anomenats web1 i web2. Pot tenir cadascun el seu servidor al port 80 intern sense conflicte? Per què?
  2. Obriu un shell dins de web1 i trobeu quin és el PID 1.
  3. Executeu docker run -d alpine:3.20. Per què apareix a docker ps -a com a Exited si l'hem arrencat amb -d? Com podríeu mantenir-lo en marxa? (pista: sleep infinity).
  4. Repetiu l'experiment de la secció 3.5 i expliqueu amb les vostres paraules per què el canvi sobreviu a stop/start però no a rm.

3.6 Neteja

docker rm -f web web1 web2
docker container prune

Resum del pas

Comanda Què fa
docker run -d --name NOM IMATGE Contenidor en segon pla amb nom
docker logs [-f] NOM Logs del procés principal
docker exec [-it] NOM ordre Executa una ordre dins d'un contenidor en marxa
docker stop / start / restart NOM Atura / arrenca / reinicia el mateix contenidor
docker rm [-f] NOM Elimina el contenidor (i la seva capa d'escriptura)
docker stats, docker top, docker inspect Recursos, processos i configuració
--restart unless-stopped Reinicia automàticament el contenidor

Següent pas: tenim un servidor web funcionant... però no hi podem accedir. Al Pas 4 el publicarem.