Introducció a Docker
Què és Docker i per què ha revolucionat el desenvolupament de programari
Docker Cheat Sheet
Pots consultar una guia ràpida en aquest link
Quan comenceu a treballar amb aplicacions web modernes, us trobareu amb un problema clàssic que ha existit des dels inicis de la informàtica: "al meu ordinador funciona, però al servidor no". Aquest problema apareix constantment i és frustrant: desenvolupeu una aplicació al vostre portàtil, tot funciona perfectament, però quan l'intenteu desplegar al servidor de producció, alguna cosa falla. Potser és perquè al servidor hi ha una versió diferent de Python, o falta una llibreria, o la configuració del sistema operatiu és diferent.
Docker resol aquest problema d'una manera elegant: permet empaquetar l'aplicació juntament amb totes les seves dependències i configuració dins d'un "contenidor". Aquest contenidor és com una capsa autònoma que conté tot el que l'aplicació necessita per funcionar. El mateix contenidor que executeu al vostre ordinador es pot executar idènticament al servidor, al núvol, o a l'ordinador d'un company. Això garanteix que l'aplicació es comportarà exactament igual a tot arreu.
Però Docker és molt més que això. Ha canviat completament la manera com desenvolupem, desplegem i gestionem aplicacions. Abans de Docker, si volíeu provar una aplicació nova, havíeu d'instal·lar un munt de programari, configurar coses, i possiblement trencar alguna part del vostre sistema. Amb Docker, podeu executar pràcticament qualsevol aplicació amb una sola comanda, sense tocar la configuració del vostre sistema. Quan acabeu, l'elimineu i el vostre sistema queda exactament com estava.
Docker és una plataforma de codi obert creada el 2013 per l'empresa dotCloud (ara Docker, Inc.). Des de llavors s'ha convertit en un estàndard de la indústria, utilitzat per empreses de tots els tamanys, des de startups fins a corporacions multinacionals. Segons les estadístiques, milions de desenvolupadors utilitzen Docker diàriament, i hi ha milers de milions de contenidors executant-se arreu del món.
Versions actuals d'octubre 2025
És important que sapigueu amb quines versions estareu treballant. Al moment d'escriure aquest document, a finals d'octubre de 2025, la versió més recent de Docker Engine és la v28. Docker Engine és el motor principal que executa els contenidors. Aquesta versió va introduir millores significatives en la gestió de xarxes, seguretat reforçada, i noves funcionalitats com muntar imatges directament dins de contenidors.
Docker Desktop, que és l'aplicació gràfica que inclou Docker Engine i altres eines útils, està a la versió 4.45 o superior. Des d'agost de 2025, Docker ha canviat la seva estratègia de llançaments i ara publiquen versions noves cada dues setmanes, amb l'objectiu d'arribar a llançaments setmanals abans de finals del 2025. Això vol dir que tindreu accés a noves funcionalitats i correccions de seguretat molt més ràpidament que abans.
Docker Compose, que és l'eina per gestionar aplicacions multi-contenidor, va per la versió 2.39 o superior. I Dockerfile, el format per definir com construir imatges, està a la versió 1.14 de sintaxi.
Podeu trobar tota la informació oficial sobre versions a https://docs.docker.com/engine/release-notes/28/ per Docker Engine, i a https://docs.docker.com/desktop/release-notes/ per Docker Desktop. La pàgina https://endoflife.date/docker-engine també documenta el suport de les diferents versions.
Entenent els conceptes bàsics: contenidors, imatges i el que els diferencia
Abans d'entrar en detalls tècnics, és fonamental que entengueu tres conceptes que són el cor de Docker: contenidors, imatges, i la diferència entre ells. És un dels punts que més confusió genera quan comenceu, però un cop ho tingueu clar, tot el resta té molt més sentit.

Què és exactament un contenidor?
Un contenidor és com una petita màquina virtual lleugera que executa la vostra aplicació. Però atenció, no és realment una màquina virtual, tot i que s'assembla. La diferència és important: una màquina virtual emula tot un ordinador complet amb el seu propi sistema operatiu, kernel, etc. Un contenidor, en canvi, comparteix el kernel del sistema operatiu amfitrió però està aïllat de la resta del sistema.
Penseu en un contenidor com un apartament d'un edifici. Tots els apartaments comparteixen l'estructura bàsica de l'edifici (l'equivalent al kernel del sistema operatiu), però cada apartament té les seves pròpies habitacions, mobles, i inquilins que no interfereixen amb els altres apartaments. Els contenidors funcionen així: comparteixen el sistema operatiu base per eficiència, però cada contenidor té el seu propi sistema de fitxers, processos, i xarxa.
Aquesta arquitectura fa que els contenidors siguin extremadament lleugers. Mentre que una màquina virtual pot ocupar gigabytes i trigar minuts a arrencar, un contenidor pot ocupar megabytes i arrencar en segons, o fins i tot mil·lisegons. Això és el que els fa tan atractius: podeu tenir desenes o fins i tot centenars de contenidors executant-se al mateix servidor on només podríeu tenir unes poques màquines virtuals.
Un contenidor encapsula tot el que l'aplicació necessita: el codi de l'aplicació, les llibreries de les que depèn, les eines del sistema que utilitza, i la configuració necessària. Per exemple, si teniu una aplicació web en Python que usa Flask i necessita PostgreSQL, el contenidor contindria Python, Flask, i tot el codi de la vostra aplicació. La base de dades PostgreSQL probablement estaria en un altre contenidor separat, però parlarem d'això més endavant.
Què és una imatge Docker?
Si un contenidor és l'aplicació en execució, una imatge és la plantilla o el motlle a partir del qual es creen els contenidors. És com la diferència entre una recepta de cuina i el plat cuinat. La recepta (la imatge) descriu tots els ingredients i passos necessaris, mentre que el plat cuinat (el contenidor) és el resultat d'executar aquesta recepta.
Una imatge és un fitxer de només lectura que conté tot el necessari per executar l'aplicació: el sistema operatiu base (normalment una versió minimalista de Linux), l'aplicació, les dependències, i la configuració. Les imatges s'organitzen en capes, com si fossin les capes d'una ceba. Cada capa afegeix alguna cosa: la primera capa podria ser el sistema operatiu base, la segona instal·la Python, la tercera instal·la les llibreries necessàries, la quarta copia el vostre codi, etc.
Aquest sistema de capes és molt eficient. Si dues imatges comparteixen les primeres capes (per exemple, totes dues usen Ubuntu com a base), Docker només emmagatzema aquestes capes una vegada i les reutilitza. Això estalvia molt espai i fa que descarregar noves imatges sigui més ràpid.
Les imatges no canvien mai. Un cop creada, una imatge és immutable. Si voleu canviar alguna cosa, heu de crear una nova imatge. Aquesta immutabilitat és una característica molt important perquè garanteix que el que funciona avui continuarà funcionant demà exactament de la mateixa manera.
La relació entre imatges i contenidors
La relació entre imatges i contenidors és com la que hi ha entre un programa instal·lat i un procés en execució. Quan feu doble clic a una aplicació del vostre ordinador, el sistema operatiu carrega el programa des del disc i crea un procés en execució. De la mateixa manera, quan arrenqueu un contenidor, Docker carrega una imatge i crea un contenidor en execució basat en aquesta imatge.
Una diferència important és que podeu crear múltiples contenidors a partir de la mateixa imatge. Per exemple, si teniu una imatge d'un servidor web NGINX, podeu crear 10 contenidors diferents tots basats en aquesta mateixa imatge. Cadascun d'aquests contenidors serà independent i tindrà el seu propi sistema de fitxers (tot i que inicialment seran idèntics), el seu propi espai de xarxa, i els seus propis processos.
Quan un contenidor fa canvis al sistema de fitxers (per exemple, escriu logs o crea fitxers temporals), aquests canvis només existeixen mentre el contenidor està en execució. Si atureu i elimineu el contenidor, tots aquests canvis es perden. La imatge original es manté intacta. Això és el que anomenem "efímer": els contenidors estan dissenyats per ser temporals. Si voleu que les dades persisteixin, heu d'utilitzar volums, que són un mecanisme especial per emmagatzemar dades fora del contenidor, però això ho veurem més endavant.
Comparant Docker amb altres tecnologies: màquines virtuals vs contenidors
Molts alumnes em pregunten: "Si Docker és com una màquina virtual, per què no simplement utilitzem VMware o VirtualBox?" És una bona pregunta, i entendre les diferències us ajudarà a comprendre per què Docker ha tingut tant d'èxit.
Com funcionen les màquines virtuals
Una màquina virtual és, literalment, un ordinador simulat dins d'un altre ordinador. Quan creeu una màquina virtual, esteu creant un sistema operatiu complet amb el seu propi kernel, els seus propis drivers, i tots els seus serveis. Aquest sistema operatiu "guest" (convidat) s'executa damunt d'un hypervisor, que és el programari que s'encarrega de l'emulació (com VMware, VirtualBox, o Hyper-V).
L'avantatge d'aquest enfocament és que teniu aïllament complet: cada màquina virtual és totalment independent i no pot afectar altres màquines virtuals ni el sistema amfitrió. També podeu executar diferents sistemes operatius: podeu tenir Linux, Windows, i altres sistemes funcionant simultàniament al mateix servidor físic.
Però té desavantatges importants. Cada màquina virtual necessita els seus propis recursos: si voleu executar 10 màquines virtuals, necessiteu suficient RAM per 10 sistemes operatius complets, suficient espai de disc per 10 sistemes complets, etc. Això és molt ineficient. A més, arrencar una màquina virtual és lent: pot trigar diversos minuts fins que el sistema operatiu està completament carregat i llest per funcionar.
Com funcionen els contenidors Docker
Els contenidors adopten un enfocament completament diferent. En lloc d'emular un ordinador complet, utilitzen funcionalitats del kernel de Linux (namespaces i cgroups) per crear entorns aïllats. Tots els contenidors comparteixen el mateix kernel del sistema operatiu amfitrió, però cada contenidor té el seu propi espai aïllat de processos, xarxa, sistema de fitxers, etc.
Això fa que els contenidors siguin extremadament lleugers. Mentre que una màquina virtual pot ocupar diversos gigabytes i utilitzar gigabytes de RAM, un contenidor pot ocupar només desenes de megabytes i utilitzar molt poca memòria. Un contenidor pot arrencar en menys d'un segon, mentre que una màquina virtual triga minuts.
La contrapartida és que tots els contenidors han de compartir el mateix tipus de kernel. En un sistema Linux, només podeu executar contenidors Linux. No podeu executar contenidors Windows natívament en Linux (tot i que Docker Desktop ho soluciona utilitzant una màquina virtual internament quan cal).
Quan utilitzar cada tecnologia
Les màquines virtuals encara tenen el seu lloc. Són ideals quan necessiteu aïllament fort per seguretat, quan heu d'executar diferents sistemes operatius, o quan treballeu amb aplicacions legacy que necessiten un entorn específic de sistema operatiu. Moltes organitzacions utilitzen màquines virtuals per aïllar diferents projectes o clients.
Els contenidors són millors quan voleu eficiència, velocitat, i la capacitat de desplegar aplicacions ràpidament. Són ideals per microserveis, on podeu tenir desenes o centenars de petits serveis diferents, cadascun en el seu propi contenidor. També són perfectes per entorns de desenvolupament: podeu tenir un contenidor amb una versió específica de Node.js per un projecte, i un altre contenidor amb una versió diferent per un altre projecte, sense que entrin en conflicte.
De fet, moltes empreses utilitzen ambdues tecnologies juntes: tenen màquines virtuals per aïllar grans unitats de treball, i dins de cada màquina virtual executen múltiples contenidors per les aplicacions individuals. És el millor dels dos móns.
Instal·lant Docker al vostre sistema
Ara que entenem què és Docker i per què és útil, anem a instal·lar-lo. Teniu dues opcions principals: Docker Engine (la versió de línia de comandes) o Docker Desktop (que inclou una interfície gràfica i algunes eines addicionals).
Docker Desktop: la manera més senzilla de començar
Per a alumnes i desenvolupadors, Docker Desktop és normalment la millor opció per començar. Inclou tot el que necessiteu: Docker Engine, Docker Compose, una interfície gràfica per gestionar contenidors, i algunes eines útils. A més, si esteu en Windows o Mac, Docker Desktop s'encarrega automàticament de configurar la màquina virtual necessària per executar contenidors Linux.
Per instal·lar Docker Desktop, aneu a https://www.docker.com/products/docker-desktop/ i descarregueu la versió per al vostre sistema operatiu. La instal·lació és senzilla, com qualsevol altra aplicació. A Windows, també podeu instal·lar-lo des de la Microsoft Store, que proporciona actualitzacions automàtiques.
Un cop instal·lat, Docker Desktop us demanarà que configureu alguns detalls. Si esteu en Windows, us preguntarà si voleu utilitzar WSL 2 (Windows Subsystem for Linux 2), que és l'opció recomanada perquè ofereix millor rendiment. A Mac, us preguntarà si voleu utilitzar Apple Virtualization Framework (per a Macs amb Apple Silicon) o Rosetta (per compatibilitat amb arquitectures diferents).
Després d'instal·lar, obriu un terminal i executeu:
Hauríeu de veure alguna cosa com "Docker version 28.0.0, build xxxxxxx" (el número exacte pot variar). Això confirma que Docker està instal·lat i funcionant correctament.
Docker Engine a Linux: instal·lació per a servidors
Si esteu treballant en un servidor Linux o preferiu la versió de línia de comandes, podeu instal·lar Docker Engine directament. Les instruccions específiques varien segons la distribució de Linux, però en general el procés és similar.
Per a Ubuntu o Debian, primer elimineu qualsevol versió antiga que pugui estar instal·lada:
Després afegiu el repositori oficial de Docker:
sudo apt-get update
sudo apt-get install ca-certificates curl gnupg lsb-release
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
I finalment, instal·leu Docker:
sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
Per verificar que tot funciona, executeu:
Aquesta comanda descarregarà una petita imatge de prova, crearà un contenidor, i mostrarà un missatge de benvinguda. Si veieu el missatge, vol dir que Docker està funcionant correctament.
Per defecte, necessitareu permisos de superusuari (sudo) per executar comandes Docker. Si voleu poder executar Docker sense sudo, afegiu el vostre usuari al grup docker:
Després haureu de tancar la sessió i tornar a entrar perquè els canvis tinguin efecte.
Podeu trobar instruccions detallades per a altres distribucions Linux a https://docs.docker.com/engine/install/
Verificant la instal·lació
Independentment de com l'hagueu instal·lat, és una bona idea verificar que tot funciona correctament. A més de docker --version, també podeu executar:
Això us mostrarà molta informació sobre la vostra instal·lació de Docker: quants contenidors teniu, quantes imatges, quin sistema d'emmagatzematge està utilitzant, etc. Si veieu aquesta informació sense errors, vol dir que Docker està configurat correctament i llest per utilitzar.
Els vostres primers passos amb Docker: creant i executant contenidors
Ara que teniu Docker instal·lat, anem a fer la nostra primera experiència pràctica. Començarem amb exemples senzills per entendre el workflow bàsic abans de passar a coses més complexes.
El primer contenidor: Hello World
Docker inclou una imatge de prova anomenada "hello-world" que és perfecta per verificar que tot funciona. Executeu aquesta comanda:
Veureu diverses línies de sortida. Anem a analitzar què ha passat exactament quan heu executat aquesta comanda, perquè entendre aquest procés és fonamental:
Primer, Docker ha buscat la imatge "hello-world" al vostre ordinador local. Com que és la primera vegada que l'executeu, no l'ha trobat. Docker us ha dit "Unable to find image 'hello-world:latest' locally", que vol dir que no la teniu descarregada.
Aleshores Docker automàticament s'ha connectat a Docker Hub, que és el registre públic d'imatges Docker (com si fos un GitHub per a imatges Docker). Ha trobat la imatge hello-world i l'ha descarregat. Veureu línies com "Pulling from library/hello-world" i "Status: Downloaded newer image".
Un cop descarregada la imatge, Docker ha creat un contenidor nou basant-se en aquesta imatge i l'ha executat. El contenidor ha mostrat un missatge explicant el que ha passat, i després ha finalitzat. Tot el procés ha durat només uns segons.
Si executeu la mateixa comanda una segona vegada, veureu que ara és molt més ràpid. Això és perquè la imatge ja està descarregada al vostre sistema, així que Docker només ha de crear i executar el contenidor.
Executant un servidor web NGINX
Ara provem alguna cosa més interessant: executar un servidor web complet. NGINX és un servidor web molt popular, i hi ha una imatge oficial a Docker Hub. Executeu:
Aquesta comanda és més complexa que l'anterior, així que anem a analitzar cada part:
El paràmetre -d vol dir "detached", és a dir, executar el contenidor en segon pla. Sense aquest paràmetre, el contenidor ocuparia el terminal i veuríeu tots els logs en directe. Amb -d, Docker executa el contenidor i us retorna el control del terminal.
El paràmetre -p 8080:80 és molt important: està fent un "port mapping" o mapatge de ports. Vol dir "connecta el port 8080 del meu ordinador amb el port 80 del contenidor". Els contenidors tenen la seva pròpia xarxa aïllada, així que per defecte no podeu accedir als serveis que hi ha dins. Amb aquest mapatge, quan accediu a localhost:8080 al vostre ordinador, Docker redirigeix la connexió al port 80 dins del contenidor, on NGINX està escoltant.
El paràmetre --name meu-nginx assigna un nom al contenidor. Si no poseu un nom, Docker en generarà un de forma aleatòria (coses com "hungry_einstein" o "silly_tesla"). És millor posar-li un nom descriptiu perquè després pugueu referir-vos al contenidor fàcilment.
Finalment, nginx:1.27-alpine especifica quina imatge volem utilitzar. La primera part (nginx) és el nom de la imatge, i la part després dels dos punts (1.27-alpine) és l'etiqueta o tag que especifica la versió. L'etiqueta "alpine" indica que aquesta és una versió basada en Alpine Linux, que és una distribució molt lleugera. Si no especifiqueu etiqueta, Docker utilitzarà "latest", però com hem comentat abans, és millor especificar versions concretes.
Després d'executar aquesta comanda, veureu un identificador llarg (el hash del contenidor). Això vol dir que el contenidor s'ha creat i està en execució. Ara podeu obrir el navegador i anar a http://localhost:8080. Hauríeu de veure la pàgina de benvinguda d'NGINX!
Gestionant el cicle de vida dels contenidors
Ara que teniu un contenidor en execució, anem a aprendre les comandes bàsiques per gestionar-lo. Per veure tots els contenidors que estan en execució actualment, executeu:
Això us mostrarà una llista amb informació sobre cada contenidor: el seu ID, la imatge de la qual prové, quant de temps fa que està en execució, i quins ports estan exposats. Hauríeu de veure el vostre contenidor "meu-nginx" a la llista.
Si voleu veure TOTS els contenidors, incloent els que s'han aturat, afegiu el paràmetre -a:
Per aturar un contenidor que està en execució, utilitzeu:
Això envia un senyal SIGTERM al procés principal del contenidor, donant-li temps per tancar-se correctament. Si el contenidor no s'atura després d'uns 10 segons, Docker enviarà un SIGKILL per forçar-lo a tancar.
Si executeu docker ps ara, veureu que el contenidor ja no apareix, perquè s'ha aturat. Però si executeu docker ps -a, encara el veureu a la llista, amb l'estat "Exited".
Per tornar a arrencar un contenidor aturat:
Fixeu-vos que docker start és diferent de docker run. docker run crea un NOUS contenidor a partir d'una imatge. docker start arrenca un contenidor existent que s'havia aturat. Si executeu docker run nginx diverses vegades, creareu múltiples contenidors diferents. Si executeu docker start sobre el mateix contenidor, només esteu arrencant-lo novament.
Per eliminar completament un contenidor:
Això eliminarà el contenidor permanentment. Però atenció: primer heu d'aturar el contenidor amb docker stop, o bé utilitzar el paràmetre -f per forçar l'eliminació:
Creant les vostres pròpies imatges amb Dockerfile
Fins ara hem utilitzat imatges creades per altres persones, però el veritable poder de Docker apareix quan creeu les vostres pròpies imatges personalitzades per les vostres aplicacions. Això es fa amb un fitxer especial anomenat Dockerfile.
Què és un Dockerfile i com funciona
Un Dockerfile és un fitxer de text que conté una sèrie d'instruccions que defineixen com construir una imatge Docker. És com una recepta: descriu pas a pas què cal fer per crear la imatge. Docker llegeix aquest fitxer i executa cada instrucció en ordre, creant una nova capa a la imatge per cada instrucció.
Anem a crear un exemple pràctic per entendre com funciona. Imagineu que teniu una aplicació web senzilla en Python que utilitza Flask. Creeu un directori nou per aquest projecte:
Primer, crearem una aplicació Python molt senzilla. Creeu un fitxer anomenat app.py amb aquest contingut:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return '<h1>Hola des del meu contenidor Docker!</h1>'
@app.route('/salut/<nom>')
def salut(nom):
return f'<h1>Hola, {nom}!</h1>'
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
Aquesta és una aplicació Flask molt bàsica que té dues rutes: una pàgina principal que mostra un salut, i una ruta dinàmica que saluda per nom. El paràmetre host='0.0.0.0' és important perquè fa que Flask escolti en totes les interfícies de xarxa, no només en localhost, que és necessari per poder accedir des de fora del contenidor.
També necessitarem especificar les dependències. Creeu un fitxer anomenat requirements.txt:
Ara ve la part interessant: crearem el Dockerfile. Creeu un fitxer anomenat Dockerfile (sense extensió) amb aquest contingut:
# Especifica la imatge base
FROM python:3.12-slim
# Estableix el directori de treball dins del contenidor
WORKDIR /app
# Copia el fitxer de requirements
COPY requirements.txt .
# Instal·la les dependències
RUN pip install --no-cache-dir -r requirements.txt
# Copia tot el codi de l'aplicació
COPY . .
# Exposa el port que utilitzarà l'aplicació
EXPOSE 5000
# Defineix la comanda que s'executarà quan el contenidor arrenqui
CMD ["python", "app.py"]
Anem a analitzar cada instrucció en detall perquè entengueu què fa cadascuna:
La instrucció FROM és sempre la primera, i especifica la imatge base sobre la qual construirem la nostra. En aquest cas, estem utilitzant python:3.12-slim, que és una imatge oficial de Python versió 3.12 en una variant "slim" (lleugera). Aquesta imatge ja té Python instal·lat i configurat, així que no hem de fer-ho nosaltres.
WORKDIR /app crea un directori anomenat /app dins del contenidor i el fa el directori de treball actual. Totes les instruccions posteriors s'executaran en aquest directori. És com fer un mkdir /app seguit d'un cd /app.
COPY requirements.txt . copia el fitxer requirements.txt des del vostre ordinador (el directori on teniu el Dockerfile) cap al directori de treball del contenidor (que és /app). El punt . al final significa "el directori actual", és a dir, /app.
RUN pip install --no-cache-dir -r requirements.txt executa una comanda dins del contenidor durant la construcció de la imatge. En aquest cas, instal·la les dependències Python especificades a requirements.txt. L'opció --no-cache-dir evita que pip guardi fitxers cache, fent que la imatge sigui més petita.
És interessant notar que primer copiem només requirements.txt i l'instal·lem abans de copiar la resta del codi. Això és una optimització: si només canvieu el codi de l'aplicació però no les dependències, Docker pot reutilitzar la capa on s'instal·len les dependències de construccions anteriors, fent que reconstruir la imatge sigui molt més ràpid.
COPY . . copia tots els fitxers del directori actual del vostre ordinador al directori de treball del contenidor. Això inclou app.py i qualsevol altre fitxer que tingueu.
EXPOSE 5000 documenta que el contenidor escoltarà en el port 5000. Atenció: aquesta instrucció NO publica el port automàticament, només el documenta. Encara haureu d'utilitzar -p quan executeu el contenidor si voleu accedir-hi des de fora.
CMD ["python", "app.py"] especifica la comanda que s'executarà quan arrenqueu un contenidor basat en aquesta imatge. En aquest cas, executarà Python amb el fitxer app.py.
Construint la imatge
Ara que teniu el Dockerfile preparat, anem a construir la imatge. Des del directori on teniu tots els fitxers, executeu:
El paràmetre -t la-meva-app:1.0 assigna un nom i una etiqueta a la imatge. El punt . al final indica a Docker que el context de construcció és el directori actual, és a dir, que els fitxers que pot copiar són els del directori actual.
Veureu com Docker executa cada instrucció del Dockerfile, mostrant el progrés. Cada pas crea una nova capa a la imatge. Al final, hauríeu de veure un missatge dient que la construcció ha estat exitosa.
Podeu veure totes les imatges que teniu al vostre sistema amb:
Hauríeu de veure la vostra imatge "la-meva-app" amb l'etiqueta "1.0" a la llista.
Executant la vostra aplicació containeritzada
Ara que teniu la imatge construïda, podeu crear contenidors a partir d'ella:
Si obriu el navegador i aneu a http://localhost:5000, hauríeu de veure el missatge "Hola des del meu contenidor Docker!". També podeu provar http://localhost:5000/salut/Maria i veureu un salut personalitzat.
El més impressionant d'això és que no heu hagut d'instal·lar Python al vostre sistema, ni Flask, ni cap dependència. Tot està encapsulat dins del contenidor. Si copieu aquesta imatge a un altre ordinador, funcionarà exactament igual. Això és el poder de Docker.
Gestionant dades: volums i persistència
Un dels aspectes més importants (i sovint més confusos) de Docker és com gestionar les dades. Recordeu que els contenidors són efímers per disseny: quan elimineu un contenidor, tot el que hi havia dins desapareix. Però moltes aplicacions necessiten emmagatzemar dades de forma persistent: bases de dades, fitxers pujats pels usuaris, logs, etc. Aquí és on entren els volums.
Per què necessitem volums
Imagineu que executeu una base de dades MySQL dins d'un contenidor. Els usuaris afegeixen dades, i tot funciona perfectament. Però després decidiu actualitzar MySQL a una versió més nova. Per fer-ho, elimineu el contenidor vell i en creeu un de nou amb la nova versió. De cop i volta, totes les dades han desaparegut! Això no és acceptable.
La solució són els volums. Un volum és una manera d'emmagatzemar dades fora del sistema de fitxers del contenidor, en una ubicació gestionada per Docker al sistema amfitrió. Quan elimineu el contenidor, el volum i les seves dades es mantenen. Quan creeu un nou contenidor, podeu muntar el mateix volum i les dades estaran allà.
Tipus de muntatges: volums, bind mounts i tmpfs
Docker ofereix tres maneres principals de muntar dades en un contenidor, i és important entendre les diferències:
Els volums són l'opció recomanada per Docker. Són completament gestionats per Docker i s'emmagatzemen en una part específica del sistema de fitxers de l'amfitrió (normalment dins de /var/lib/docker/volumes/ en Linux). No heu de preocupar-vos de rutes absolutes o permisos complicats. Docker s'encarrega de tot. Els volums també funcionen millor amb Docker Desktop en Windows i Mac.
Els bind mounts munten un directori o fitxer específic del vostre sistema amfitrió dins del contenidor. Això és útil durant el desenvolupament perquè podeu editar els fitxers al vostre ordinador amb el vostre editor favorit, i els canvis es reflecteixen immediatament dins del contenidor. La desavantatge és que sou responsables de gestionar els permisos i les rutes.
Els tmpfs mounts són volums que s'emmagatzemen a la memòria RAM, no al disc. Són útils per dades temporals que no necessiten persistir i que voleu que siguin molt ràpides.
Treballant amb volums
Anem a veure exemples pràctics. Primer, crearem un volum:
Això crea un volum anomenat "dades-mysql". Podeu veure tots els vostres volums amb:
Per obtenir informació detallada sobre un volum:
Això us mostrarà on s'emmagatzema físicament el volum al vostre sistema.
Ara anem a utilitzar aquest volum amb una base de dades MySQL:
docker run -d \
--name mysql-db \
-e MYSQL_ROOT_PASSWORD=contrasenya_segura \
-v dades-mysql:/var/lib/mysql \
mysql:8.4
Analitzem aquesta comanda. El paràmetre -e estableix una variable d'entorn dins del contenidor. MySQL necessita que li especifiqueu la contrasenya de root mitjançant aquesta variable. El paràmetre -v dades-mysql:/var/lib/mysql és el més important: munta el volum "dades-mysql" al directori /var/lib/mysql dins del contenidor, que és on MySQL emmagatzema totes les seves dades.
Ara, si elimineu aquest contenidor i en creeu un de nou muntant el mateix volum, totes les dades estaran allà. Això és essencial per a bases de dades i qualsevol altre servei que necessiti persistència.
Utilitzant bind mounts per desenvolupament
Durant el desenvolupament, sovint és útil utilitzar bind mounts perquè podeu editar el codi al vostre ordinador i veure els canvis immediatament sense haver de reconstruir la imatge. Per exemple, amb la nostra aplicació Flask d'abans:
El $(pwd) (o ${PWD} en PowerShell) s'expandeix al directori actual. Això munta el vostre directori de treball dins de /app al contenidor. Ara, si editeu app.py al vostre ordinador, els canvis es veuran dins del contenidor. Depenent de com estigui configurada l'aplicació, pot ser que hagueu de reiniciar el contenidor per veure els canvis, però no cal reconstruir la imatge.
Netejant volums
Els volums ocupen espai al disc, així que de tant en tant haureu de netejar els que ja no utilitzeu. Per eliminar un volum:
Atenció: només podeu eliminar un volum si cap contenidor l'està utilitzant actualment. Si heu d'eliminar contenidors i els seus volums alhora:
L'opció -v elimina els volums anònims associats amb el contenidor.
Per eliminar tots els volums que no estan sent utilitzats per cap contenidor:
Aquesta comanda us demanarà confirmació abans d'eliminar res, per seguretat.
Docker Compose: gestionant aplicacions multi-contenidor
Fins ara hem treballat amb contenidors individuals, però les aplicacions reals sovint estan compostes de múltiples serveis que treballen junts. Per exemple, una aplicació web típica podria tenir un contenidor per l'aplicació, un altre per la base de dades, i potser un altre per un sistema de caching com Redis. Gestionar tots aquests contenidors manualment, amb totes les seves opcions i dependències, es fa ràpidament complicat. Aquí és on Docker Compose resulta invaluable.
Què és Docker Compose i per què és tan útil
Docker Compose és una eina per definir i executar aplicacions Docker multi-contenidor. En lloc d'haver de recordar comandes llargues amb dotzenes de paràmetres per cada contenidor, definiu tot en un fitxer YAML anomenat docker-compose.yml. Després, amb una sola comanda, podeu arrencar tots els serveis alhora, i Docker Compose s'encarrega de crear els volums, les xarxes, i els contenidors necessaris.
Però Docker Compose és més que això: també gestiona les dependències entre serveis. Si l'aplicació web necessita que la base de dades estigui funcionant primer, podeu especificar-ho, i Docker Compose arrencarà els serveis en l'ordre correcte. També facilita veure els logs de tots els serveis alhora, aturar-los tots amb una comanda, i reconstruir-los quan feu canvis.
Creant el primer docker-compose.yml
Anem a crear un exemple pràctic que combini una aplicació web amb una base de dades. Crearem una aplicació senzilla que emmagatzema i mostra comentaris. Primer, necessitem una aplicació. Aquí teniu un exemple molt bàsic amb Flask i PostgreSQL.
Creeu un directori nou per aquest projecte:
Creeu l'aplicació app.py:
from flask import Flask, request, render_template_string
import psycopg2
import os
app = Flask(__name__)
def get_db_connection():
conn = psycopg2.connect(
host=os.environ.get('DB_HOST', 'db'),
database=os.environ.get('DB_NAME', 'comentaris'),
user=os.environ.get('DB_USER', 'usuari'),
password=os.environ.get('DB_PASSWORD', 'contrasenya')
)
return conn
@app.route('/', methods=['GET', 'POST'])
def index():
if request.method == 'POST':
comentari = request.form['comentari']
conn = get_db_connection()
cur = conn.cursor()
cur.execute('INSERT INTO comentaris (text) VALUES (%s)', (comentari,))
conn.commit()
cur.close()
conn.close()
conn = get_db_connection()
cur = conn.cursor()
cur.execute('SELECT * FROM comentaris ORDER BY id DESC')
comentaris = cur.fetchall()
cur.close()
conn.close()
return render_template_string('''
<h1>Aplicació de Comentaris</h1>
<form method="post">
<textarea name="comentari" rows="3" cols="50"></textarea><br>
<input type="submit" value="Afegir comentari">
</form>
<h2>Comentaris:</h2>
<ul>
{% for comentari in comentaris %}
<li>{{ comentari[1] }}</li>
{% endfor %}
</ul>
''', comentaris=comentaris)
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
Creeu el fitxer requirements.txt:
Creeu un script per inicialitzar la base de dades, init.sql:
CREATE TABLE IF NOT EXISTS comentaris (
id SERIAL PRIMARY KEY,
text TEXT NOT NULL,
creat_a TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Creeu el Dockerfile per l'aplicació:
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["python", "app.py"]
I ara, la part més important, el fitxer docker-compose.yml:
services:
web:
build: .
ports:
- "5000:5000"
environment:
- DB_HOST=db
- DB_NAME=comentaris
- DB_USER=usuari
- DB_PASSWORD=contrasenya_segura
depends_on:
- db
volumes:
- .:/app
db:
image: postgres:17-alpine
environment:
- POSTGRES_DB=comentaris
- POSTGRES_USER=usuari
- POSTGRES_PASSWORD=contrasenya_segura
volumes:
- dades-postgres:/var/lib/postgresql/data
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
volumes:
dades-postgres:
Aquest fitxer defineix una aplicació completa amb dos serveis: "web" i "db". Anem a analitzar cada secció detingudament perquè és important entendre com funciona.
La secció services defineix cada contenidor que forma part de l'aplicació. Tenim dos serveis: "web" i "db".
Per al servei "web", en lloc d'especificar una imatge existent, utilitzem build: ., que li diu a Docker Compose que construeixi la imatge a partir del Dockerfile que hi ha al directori actual. La secció ports mapeja el port 5000 del nostre ordinador al port 5000 del contenidor.
La secció environment defineix variables d'entorn que estarà disponibles dins del contenidor. Fixeu-vos que DB_HOST=db: això funciona perquè Docker Compose automàticament crea una xarxa on els serveis poden comunicar-se entre ells usant els seus noms com a hostnames. Així, des del contenidor "web", podem accedir a la base de dades simplement referint-nos a "db".
El paràmetre depends_on indica que el servei "web" depèn del servei "db". Docker Compose s'assegurarà que la base de dades s'arrenqui abans que l'aplicació web. Això no garanteix que la base de dades estigui LLESTA (només que el contenidor s'hagi iniciat), però és un primer pas.
El volum .:/app munta el directori actual dins de /app al contenidor, permetent-nos desenvolupar i veure els canvis sense reconstruir.
Per al servei "db", utilitzem la imatge oficial de PostgreSQL. Les variables d'entorn configuren el nom de la base de dades, l'usuari i la contrasenya. El primer volum dades-postgres:/var/lib/postgresql/data assegura que les dades de la base de dades persisteixin entre reinicios. El segon volum munta el nostre script SQL d'inicialització a una ubicació especial: la imatge de PostgreSQL automàticament executa qualsevol script .sql que trobi a /docker-entrypoint-initdb.d/ quan crea la base de dades per primera vegada.
Finalment, la secció volumes al final del fitxer declara els volums anomenats que utilitzem. Docker Compose crearà automàticament el volum "dades-postgres" si no existeix.
Executant l'aplicació amb Docker Compose
Amb tot això configurat, executar l'aplicació és extremadament senzill:
Aquesta comanda fa TOTES aquestes coses: 1. Crea la xarxa necessària per que els serveis es puguin comunicar 2. Crea el volum "dades-postgres" si no existeix 3. Construeix la imatge per al servei "web" si no està construïda 4. Descarrega la imatge de PostgreSQL si no la teniu 5. Arrenca el contenidor de la base de dades 6. Espera que la base de dades estigui llesta 7. Arrenca el contenidor de l'aplicació web 8. Us mostra els logs de tots els serveis en temps real
Veureu els logs de tots dos serveis barrejats a la pantalla, cadascun amb un prefix de color que indica de quin servei provenen. Podeu veure com PostgreSQL s'inicialitza, executa el script SQL per crear la taula, i després l'aplicació Flask s'arrenca i es connecta a la base de dades.
Obriu el navegador i aneu a http://localhost:5000. Hauríeu de veure l'aplicació funcionant! Podeu afegir comentaris i es guardaran a la base de dades PostgreSQL que està funcionant en un contenidor separat.
Si voleu executar-ho en segon pla (detached mode), utilitzeu:
Per veure els logs després:
O per seguir els logs en temps real:
Per aturar tots els serveis:
Això atura i elimina tots els contenidors i la xarxa, però manté els volums (i per tant les dades de la base de dades). Si també voleu eliminar els volums:
Per reconstruir les imatges si heu fet canvis:
O per reconstruir i arrencar en una sola comanda:
La documentació completa de Docker Compose està a https://docs.docker.com/compose/
Xarxes a Docker: comunicació entre contenidors
Un aspecte fonamental de Docker que sovint es passa per alt és com funciona la xarxa. Quan executeu múltiples contenidors que necessiten comunicar-se entre ells, Docker proporciona diverses opcions de xarxa que us permeten controlar com es comuniquen.
Com funcionen les xarxes Docker per defecte
Quan instal·leu Docker, crea automàticament tres xarxes: bridge, host, i none. Podeu veure-les executant:
La xarxa "bridge" és la xarxa per defecte. Quan executeu un contenidor sense especificar una xarxa, Docker el connecta automàticament a aquesta xarxa bridge. Els contenidors en aquesta xarxa poden comunicar-se entre ells usant les seves adreces IP, però NO poden comunicar-se usant els seus noms. Això és important: si teniu dos contenidors a la xarxa bridge per defecte, no podeu fer ping nom-contenidor, heu d'usar l'adreça IP.
La xarxa "host" elimina l'aïllament de xarxa entre el contenidor i el sistema amfitrió. Si un contenidor està connectat a la xarxa host, utilitzarà directament la xarxa de l'amfitrió, sense cap mapatge de ports. Això pot ser útil per aplicacions que necessiten molt rendiment de xarxa, però elimina part de l'aïllament que proporcionen els contenidors.
La xarxa "none" desactiva completament la xarxa per al contenidor. És útil per contenidors que no necessiten connectivitat de xarxa en absolut.
Creant xarxes personalitzades
La pràctica recomanada és crear xarxes personalitzades per les vostres aplicacions. Els contenidors en xarxes personalitzades poden comunicar-se entre ells usant els seus noms com a hostnames, cosa que fa la configuració molt més senzilla. A més, proporcionen millor aïllament: només els contenidors que explícitament connecteu a una xarxa poden comunicar-se amb els altres contenidors d'aquesta xarxa.
Per crear una xarxa:
Ara podeu executar contenidors connectats a aquesta xarxa:
docker run -d --name contenidor1 --network la-meva-xarxa nginx
docker run -d --name contenidor2 --network la-meva-xarxa nginx
Des de contenidor1, podríeu fer ping contenidor2 i funcionaria! Docker proporciona resolució DNS automàtica dins de xarxes personalitzades.
Si teniu un contenidor que ja està en execució i voleu connectar-lo a una xarxa addicional:
Un contenidor pot estar connectat a múltiples xarxes simultàniament.
Exemple pràctic: aplicació web amb base de dades
Anem a veure un exemple pràctic de com utilitzar xarxes. Crearem una xarxa per una aplicació web i la seva base de dades:
docker network create xarxa-app
docker run -d \
--name mysql-backend \
--network xarxa-app \
-e MYSQL_ROOT_PASSWORD=contrasenya \
-e MYSQL_DATABASE=app_db \
mysql:8.4
docker run -d \
--name web-frontend \
--network xarxa-app \
-p 8080:80 \
nginx
Ara el contenidor "web-frontend" pot accedir a "mysql-backend" simplement usant el nom "mysql-backend" com a hostname. En el fitxer de configuració de l'aplicació, podríeu posar:
I funcionaria perfectament. No cal especificar adreces IP que poden canviar, no cal configurar cap mapatge de ports (els contenidors dins de la mateixa xarxa poden parlar entre ells directament pels ports interns).
Docker Compose fa tot això automàticament: crea una xarxa per defecte per cada projecte i hi connecta tots els serveis, permetent que es comuniquin per nom.
Seguretat i aïllament
Les xarxes també proporcionen seguretat. Si teniu múltiples aplicacions diferents al mateix servidor, podeu posar cada aplicació en la seva pròpia xarxa. Els contenidors d'una aplicació no podran accedir als contenidors d'altres aplicacions, proporcionant aïllament.
Per exemple, podríeu tenir: - Xarxa "app1" amb el frontend, backend i base de dades de l'Aplicació 1 - Xarxa "app2" amb el frontend, backend i base de dades de l'Aplicació 2
Els contenidors de l'Aplicació 1 no podran accedir als de l'Aplicació 2, i viceversa.
Bones pràctiques per treballar amb Docker
A mesura que utilitzeu Docker més i més, us adonareu que hi ha maneres millors i pitjors de fer les coses. Aquí us explico les pràctiques recomanades que us estalviaran problts i farant que les vostres aplicacions siguin més robustes i mantenibles.
Mantenir les imatges petites
Les imatges grans són problemàtiques: triguen més a descarregar, ocupen més espai al disc, i sovint contenen programari innecessari que podria tenir vulnerabilitats de seguretat. Hi ha diverses tècniques per mantenir les imatges petites.
Primer, utilitzeu imatges base lleugeres quan sigui possible. Les variants "alpine" de les imatges oficials són molt més petites que les variants estàndard. Per exemple, python:3.12-alpine és molt més petita que python:3.12. L'única desavantatge és que Alpine Linux usa musl libc en lloc de glibc, cosa que de vegades pot causar incompatibilitats amb certs paquets, però per la majoria d'aplicacions funciona perfectament.
Segon, minimitzeu el nombre de capes. Cada instrucció RUN al Dockerfile crea una nova capa. En lloc de tenir múltiples instruccions RUN, combineu-les quan tingui sentit:
# Menys òptim - crea 3 capes
RUN apt-get update
RUN apt-get install -y curl
RUN apt-get install -y vim
# Millor - crea només 1 capa
RUN apt-get update && \
apt-get install -y curl vim && \
rm -rf /var/lib/apt/lists/*
Fixeu-vos que també eliminem els fitxers cache d'apt al final per fer la capa més petita.
Tercer, utilitzeu .dockerignore. Aquest fitxer funciona com .gitignore: especifica quins fitxers i directoris NO s'haurien de copiar a la imatge. Per exemple, no necessiteu copiar el directori .git, fitxers temporals, o documentació:
Això fa que el context de construcció sigui més petit i ràpid de processar.
Multi-stage builds: el secret de les imatges professionals
Els multi-stage builds són una tècnica avançada però extremadament útil. La idea és utilitzar múltiples instruccions FROM en un mateix Dockerfile, cadascuna definint una "stage" o etapa de construcció. Això us permet tenir una etapa per compilar o construir l'aplicació (amb totes les eines de desenvolupament), i després una etapa final molt més lleugera que només conté l'aplicació compilada.
Per exemple, per una aplicació Go:
# Etapa 1: Compilació
FROM golang:1.23-alpine AS builder
WORKDIR /build
COPY . .
RUN go build -o app .
# Etapa 2: Imatge final
FROM alpine:3.20
WORKDIR /app
COPY --from=builder /build/app .
CMD ["./app"]
La imatge final no conté el compilador de Go ni els fitxers font, només el binari compilat. Això pot reduir la mida de la imatge de centenars de megabytes a pocs megabytes.
No executeu contenidors com a root
Per defecte, els processos dins dels contenidors s'executen com a usuari root, cosa que és un risc de seguretat. Si algú compromet el vostre contenidor, tindria permisos de root dins del contenidor. Tot i que Docker proporciona aïllament, és millor seguir el principi del mínim privilegi.
Podeu crear un usuari no privilegiat al Dockerfile:
FROM python:3.12-slim
# Crear un usuari no-root
RUN useradd -m -u 1000 appuser
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
# Canviar a l'usuari no-root
USER appuser
CMD ["python", "app.py"]
Utilitzar variables d'entorn per la configuració
Mai codifiqueu en dur coses com contrasenyes, URLs d'APIs, o qualsevol configuració que pugui canviar entre entorns (desenvolupament, staging, producció). Utilitzeu variables d'entorn:
import os
DB_HOST = os.environ.get('DB_HOST', 'localhost')
DB_USER = os.environ.get('DB_USER', 'user')
DB_PASSWORD = os.environ.get('DB_PASSWORD')
Això fa que la mateixa imatge es pugui utilitzar en diferents entorns simplement passant diferents variables d'entorn.
Especificar versions explícites
Ja ho hem dit abans, però és tan important que val la pena repetir-ho: sempre especifiqueu versions concretes de les imatges base i les dependències. No utilitzeu FROM python:latest o RUN pip install flask. Utilitzeu versions específiques:
I al requirements.txt:
Això garanteix que la imatge es construirà de la mateixa manera avui i d'aquí a sis mesos.
Health checks per aplicacions crítiques
Docker permet definir health checks al Dockerfile per monitorar si l'aplicació està funcionant correctament:
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:5000/ || exit 1
Això fa que Docker comprovi cada 30 segons si l'aplicació respon correctament. Si falla 3 vegades seguides, Docker marcarà el contenidor com "unhealthy". Això és molt útil quan s'utilitza amb orquestradors com Kubernetes.
Podeu trobar més bones pràctiques a la documentació oficial: https://docs.docker.com/develop/develop-images/dockerfile_best-practices/
Registres d'imatges: Docker Hub i alternatives
Fins ara hem treballat només amb imatges locals o imatges públiques de Docker Hub. Però en un entorn professional, sovint necessitareu compartir les vostres imatges amb altres membres de l'equip o desplegar-les a servidors de producció. Aquí és on entren els registres d'imatges.
Docker Hub: el registre públic per excel·lència
Docker Hub és el registre públic més gran d'imatges Docker. És com GitHub però per imatges Docker. Conté milers d'imatges oficials i milions d'imatges creades per la comunitat. Quan executeu docker pull nginx, Docker automàticament el busca a Docker Hub.
Docker Hub ofereix comptes gratuïts que us permeten tenir un repositori privat i repositoris públics il·limitats. Això és perfecte per aprenentatge i projectes personals. Per a ús professional, Docker Hub ofereix plans de pagament amb més repositoris privats i funcionalitats addicionals.
Pujant imatges a Docker Hub
Per pujar la vostra pròpia imatge a Docker Hub, primer necessiteu crear un compte a https://hub.docker.com. Un cop tingueu el compte, heu de fer login des de la línia de comandes:
Això us demanarà el vostre nom d'usuari i contrasenya. Un cop autenticats, podeu pujar imatges. Però primer, les imatges necessiten tenir el format correcte de nom: usuari/nom-imatge:etiqueta.
Si la vostra imatge es diu la-meva-app:1.0 i el vostre usuari és joanserra, heu de retaggejar la imatge:
Ara podeu pujar-la:
Veureu com Docker puja cada capa de la imatge. Si algunes capes ja existeixen a Docker Hub (perquè es comparteixen amb altres imatges), no cal tornar-les a pujar, fent que el procés sigui molt més ràpid.
Un cop pujada, qualsevol persona (si és pública) o qualsevol membre del vostre equip (si és privada) pot descarregar i utilitzar la imatge:
Registres privats: quan necessiteu més control
Per a empreses i projectes que necessiten més control, privacitat, o que volen allotjar les imatges als seus propis servidors, hi ha alternatives a Docker Hub. Podeu muntar el vostre propi registre privat utilitzant Docker Registry, que és una aplicació de codi obert proporcionada per Docker:
Això arrenca un registre local al port 5000. Ara podeu pujar imatges a aquest registre:
docker tag la-meva-app:1.0 localhost:5000/la-meva-app:1.0
docker push localhost:5000/la-meva-app:1.0
Hi ha també alternatives comercials com Amazon ECR (Elastic Container Registry), Google Container Registry, Azure Container Registry, o GitLab Container Registry, que ofereixen integració amb les seves respectives plataformes de núvol.
Depuració i solució de problemes
Inevitablement, us trobareu amb situacions on alguna cosa no funciona com esperàveu. Docker proporciona diverses eines per depurar i entendre què està passant.
Consultant logs
La primera eina de depuració són els logs. Per veure els logs d'un contenidor:
Això mostra tot el que l'aplicació ha escrit a stdout i stderr. L'opció -f segueix els logs en temps real:
Si només voleu veure les últimes línies:
Entrant dins d'un contenidor
De vegades necessiteu "entrar" dins d'un contenidor per inspeccionar-lo. Utilitzeu docker exec per executar comandes dins d'un contenidor en execució:
Això us dona una shell interactiva dins del contenidor. L'opció -it combina -i (interactiu) i -t (terminal). Si la imatge està basada en Alpine, probablement haureu d'utilitzar /bin/sh en lloc de /bin/bash perquè Alpine no inclou bash per defecte.
Un cop dins, podeu executar qualsevol comanda: ls, ps, cat, etc. Això és molt útil per verificar que els fitxers estan on haurien d'estar, comprovar variables d'entorn, o executar comandes de depuració.
Per sortir del contenidor, simplement escriviu exit.
Inspeccionant contenidors i imatges
La comanda docker inspect us dona informació detallada sobre gairebé qualsevol cosa de Docker:
Això retorna un JSON enorme amb tota la informació sobre el contenidor: la seva configuració, xarxa, volums, variables d'entorn, etc. És massa informació per llegir d'un cop, però podeu filtrar-la:
També podeu usar el format de plantilla per extreure informació específica:
Problemes comuns i les seves solucions
Un problema molt comú és "No puc connectar-me al meu contenidor". Normalment això passa perquè heu oblidat mapegar els ports. Recordeu que un contenidor té la seva pròpia xarxa aïllada. Si l'aplicació escolta al port 80 dins del contenidor, però no heu fet -p 8080:80, no hi podreu accedir des de fora.
Un altre problema freqüent és "El meu contenidor es reinicia constantment". Això normalment vol dir que el procés principal del contenidor està fallant. Consulteu els logs amb docker logs per veure què està passant. Potser falta una variable d'entorn, o el proces s'està executant i acabant immediatament perquè no té res a fer.
"El meu volum està buit o no té les dades que esperava" sol passar per problemes de permisos o rutes incorrectes. Utilitzeu docker inspect per verificar on està muntat exactament el volum, i comproveu que la ruta dins del contenidor coincideix amb la que esperàveu.
Finalment, "Docker diu que el port ja està en ús". Això vol dir que ja teniu alguna cosa (potser un altre contenidor, o una aplicació del vostre sistema) utilitzant el port que intenteu mapegar. Podeu utilitzar un port diferent, o aturar l'altra aplicació/contenidor que està utilitzant el port.
Exemples pràctics complets
Ara que heu après tots els conceptes, anem a posar-ho tot junt amb alguns exemples del món real que podeu utilitzar com a base per els vostres propis projectes.
Exemple 1: WordPress amb MySQL
WordPress és una aplicació perfecta per aprendre Docker Compose perquè necessita una base de dades i té una configuració relativament simple. Creeu un directori nou i dins un fitxer docker-compose.yml:
services:
db:
image: mysql:8.4
volumes:
- db_data:/var/lib/mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: contrasenya_root
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress_password
wordpress:
depends_on:
- db
image: wordpress:6.7
ports:
- "8000:80"
restart: always
environment:
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress_password
WORDPRESS_DB_NAME: wordpress
volumes:
- wordpress_data:/var/www/html
volumes:
db_data:
wordpress_data:
Executeu docker-compose up -d i després aneu a http://localhost:8000. Veureu l'instal·lador de WordPress! Totes les dades es guarden als volums, així que podeu aturar i arrencar els contenidors sense perdre res.
Exemple 2: Aplicació Node.js amb MongoDB i Redis
Aquest és un exemple més complex que mostra com funciona una aplicació web moderna amb diverses dependències:
Creeu app.js:
const express = require('express');
const mongoose = require('mongoose');
const redis = require('redis');
const app = express();
const PORT = process.env.PORT || 3000;
// Connexió a MongoDB
mongoose.connect(process.env.MONGODB_URI || 'mongodb://mongo:27017/myapp', {
useNewUrlParser: true,
useUnifiedTopology: true
});
// Connexió a Redis
const redisClient = redis.createClient({
url: process.env.REDIS_URL || 'redis://redis:6379'
});
redisClient.connect();
app.get('/', async (req, res) => {
const visits = await redisClient.incr('visits');
res.json({
message: 'Aplicació funcionant!',
visits: visits,
database: mongoose.connection.readyState === 1 ? 'connectada' : 'desconnectada'
});
});
app.listen(PORT, () => {
console.log(`Servidor escoltant al port ${PORT}`);
});
Creeu package.json:
{
"name": "app-exemple",
"version": "1.0.0",
"dependencies": {
"express": "^4.21.2",
"mongoose": "^8.9.5",
"redis": "^4.7.0"
}
}
Creeu Dockerfile:
FROM node:22-alpine
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["node", "app.js"]
I finalment docker-compose.yml:
services:
app:
build: .
ports:
- "3000:3000"
environment:
- MONGODB_URI=mongodb://mongo:27017/myapp
- REDIS_URL=redis://redis:6379
depends_on:
- mongo
- redis
mongo:
image: mongo:8.0
volumes:
- mongo_data:/data/db
redis:
image: redis:7.4-alpine
volumes:
- redis_data:/data
volumes:
mongo_data:
redis_data:
Aquest exemple mostra com una aplicació pot utilitzar múltiples serveis: MongoDB per dades persistents, i Redis per caching i comptadors. Executeu docker-compose up i accediu a http://localhost:3000. Cada vegada que recarregueu la pàgina, el comptador de visites augmentarà.
Recursos per continuar aprenent i aprofundir
Docker és una tecnologia molt àmplia, i aquest document només ha cobert els fonaments. Hi ha molt més a aprendre: orquestració amb Kubernetes, pipelines CI/CD, seguretat avançada, optimització de rendiment, i molt més.
Documentació oficial
La documentació oficial de Docker a https://docs.docker.com és excepcional. Està ben organitzada, té molts exemples, i es manté actualitzada. Especialment recomanables són:
- La guia de referència de Dockerfile: https://docs.docker.com/engine/reference/builder/
- Les bones pràctiques per escriure Dockerfiles: https://docs.docker.com/develop/develop-images/dockerfile_best-practices/
- La documentació de Docker Compose: https://docs.docker.com/compose/
Comunitat i suport
Docker té una comunitat molt activa. El fòrum oficial a https://forums.docker.com és un bon lloc per fer preguntes. També hi ha comunitats actives a Stack Overflow amb l'etiqueta docker i a Reddit a r/docker.
Eines complementàries
Hi ha moltes eines que complementen Docker i fan el desenvolupament més productiu. Portainer (https://www.portainer.io/) és una interfície web per gestionar Docker de forma visual. Dive (https://github.com/wagoodman/dive) és una eina per inspeccionar les capes de les imatges i veure com es construeixen. Hadolint (https://github.com/hadolint/hadolint) és un linter per Dockerfiles que us ajuda a seguir les bones pràctiques.
El següent pas: orquestració
Un cop domineu Docker, el següent pas natural és aprendre sobre orquestració de contenidors. Kubernetes és l'orquestrador més popular i és essencialment l'estàndard de la indústria. Si voleu treballar amb Docker en entorns de producció amb desenes o centenars de contenidors, haureu d'aprendre Kubernetes.
Referències i fonts d'informació
Tota la informació d'aquest document ha estat extreta i verificada de les següents fonts oficials, actualitzades fins a octubre de 2025:
Documentació oficial de Docker: - Pàgina principal de documentació: https://docs.docker.com - Notes de llançament de Docker Desktop: https://docs.docker.com/desktop/release-notes/ - Referència de Dockerfile: https://docs.docker.com/engine/reference/builder/ - Documentació de Docker Compose: https://docs.docker.com/compose/ - Bones pràctiques per Dockerfiles: https://docs.docker.com/develop/develop-images/dockerfile_best-practices/
Informació sobre versions i suport: - Estado de versions de Docker Engine: https://endoflife.date/docker-engine - Anunci de la nova cadència de llançaments: https://www.docker.com/blog/docker-desktop-updates-every-two-weeks/