Les xarxes Docker permeten crear i gestionar xarxes virtuals que habiliten la comunicació entre contenidors. Aquesta funcionalitat és essencial quan construïm aplicacions multi-contenidor, on diferents serveis (servidor web, aplicació, base de dades) necessiten comunicar-se entre ells de manera segura i aïllada.
En aquest article explorarem els tipus de xarxes disponibles, com crear-les i gestionar-les i com connectar contenidors per formar una aplicació completa.
Xarxes per defecte #
Quan instal·lam Docker, es creen automàticament tres xarxes:
docker network lsLa sortida de la comanda serà:
NETWORK ID NAME DRIVER SCOPE
b7673e576242 bridge bridge local
4f8526c27950 host host local
f5a661f700d9 none null localCada xarxa té un propòsit diferent:
| Controlador | Descripció | Cas d’ús |
|---|---|---|
bridge |
Xarxa privada interna. Els contenidors poden comunicar-se entre ells | Aplicacions multi-contenidor en un sol host |
host |
El contenidor comparteix la xarxa de l’amfitrió directament | Aplicacions que necessiten el màxim rendiment de xarxa |
none |
El contenidor no té accés a cap xarxa | Contenidors que processen dades sense necessitat de xarxa |
La xarxa bridge #
Quan executam un contenidor sense especificar cap xarxa, Docker el connecta a la xarxa bridge per defecte:
docker run --name nginx-test --detach nginx:1.29-alpine
docker inspect nginx-test --format '{{.NetworkSettings.IPAddress}}'El contenidor rep una IP de la xarxa bridge (per exemple, 172.17.0.2). Tanmateix, la xarxa bridge per defecte té una limitació important: no proporciona resolució DNS automàtica pel nom del contenidor.
docker run --rm alpine ping -c 2 nginx-testAquesta comanda fallarà amb “bad address ’nginx-test’” perquè la xarxa bridge per defecte no resol noms de contenidor.
Netejem abans de continuar:
docker rm --force nginx-testXarxes d’usuari #
Per superar les limitacions de la xarxa bridge per defecte, podem crear les nostres pròpies xarxes. Les xarxes definides per l’usuari proporcionen:
- Resolució DNS automàtica, de forma que els contenidors es poden trobar pel seu nom.
- Millor aïllament, car només els contenidors de la mateixa xarxa poden comunicar-se.
- Connexió i desconnexió en calent, car podem afegir o treure contenidors d’una xarxa sense aturar-los.
Creació #
Per a crear una nova xarxa usarem la comanda docker network create, passant el nom de la xarxa com a paràmetre:
docker network create webapp-networkPer defecte, Docker crea una xarxa de tipus bridge amb una subxarxa automàtica. Podem verificar-ho:
docker network inspect webapp-network \
--format '{{range .IPAM.Config}}{{.Subnet}}{{end}}'Si necessitam especificar la subxarxa usarem el paràmetre --subnet i, si escau, el paràmetre --gateway:
docker network create \
--driver bridge \
--subnet 172.28.0.0/16 \
--gateway 172.28.0.1 \
custom-networkGestió #
De manera anàloga als volums, l’ordre docker network ofereix un conjunt de comandes per gestionar les xarxes.
| Comanda | Descripció |
|---|---|
docker network create <nom> |
Crea una xarxa nova |
docker network ls |
Llista totes les xarxes |
docker network inspect <nom> |
Mostra informació detallada |
docker network rm <nom> |
Elimina una xarxa (ha d’estar buida) |
docker network prune |
Elimina totes les xarxes no utilitzades |
docker network connect <xarxa> <contenidor> |
Connecta un contenidor a una xarxa |
docker network disconnect <xarxa> <contenidor> |
Desconnecta un contenidor d’una xarxa |
DNS intern #
Vegem com funciona la resolució DNS amb un exemple pràctic. Crearem una xarxa i hi connectarem dos contenidors:
docker network create test-dns
docker run --name server-a \
--network test-dns \
--detach \
alpine sleep 3600
docker run --name server-b \
--network test-dns \
--detach \
alpine sleep 3600Ara, des de server-a, podem fer ping a server-b pel seu nom:
docker exec server-a ping -c 3 server-bLa sortida serà:
PING server-b (172.20.0.3): 56 data bytes
64 bytes from 172.20.0.3: seq=0 ttl=64 time=0.089 ms
64 bytes from 172.20.0.3: seq=1 ttl=64 time=0.085 ms
64 bytes from 172.20.0.3: seq=2 ttl=64 time=0.087 ms
...Docker gestiona automàticament la resolució DNS dins de la xarxa. Això és fonamental per a aplicacions on els serveis es referencien pel nom, e.g., una aplicació que es connecta a postgres o redis.
Netejem els contenidors creats i la xarxa abans de continuar:
docker rm --force server-a server-b
docker network rm test-dnsExemple pràctic #
Construirem una aplicació web amb tres components:
- NGINX: Servidor web que actua com a reverse proxy.
- Python: Aplicació Python sobre HTTP.
- PostgreSQL: Base de dades.
flowchart LR
subgraph Internet
U[Usuari]
end
subgraph Docker Host
subgraph webapp-net
N[NGINX
:80]
A[App Python
:8000]
D[(PostgreSQL
:5432)]
end
end
U -->|:8080| N
N -->|proxy_pass| A
A -->|database_host| D
Xarxa i volum #
Abans d’executar contenidors necessitam crear les nostres xarxes i els nostres volums. En aquest cas, amb un de cada ens basta:
docker network create webapp-net
docker volume create webapp-pgdataBase de dades #
El primer contenidor que necessitam crear és el de la base de dades:
docker run --name webapp-db \
--network webapp-net \
--volume webapp-pgdata:/var/lib/postgresql \
--env POSTGRES_PASSWORD=webapp123 \
--env POSTGRES_USER=webapp \
--env POSTGRES_DB=webapp \
--detach \
postgres:18-alpineFixem-nos que no publicam el port 5432, és a dir, la base de dades només és accessible des de la xarxa interna webapp-net.
Aplicació #
Per a aquest exemple, usarem una imatge de demostració. En un cas real, construiríem la nostra pròpia imatge amb el nostre propi Dockerfile.
Començarem creant un subdirectori de desenvolupament al nostre directori de projectes:
mkdir --parents ~/Projects/webapp-demoLlavors, a dins, crearem un fitxer de Python, anomenat app.py, amb un servidor HTTP que respongui amb un missatge:
from http.server import HTTPServer, BaseHTTPRequestHandler
import json
import os
import socket
class Handler(BaseHTTPRequestHandler):
def check_database(self, host: str, port: int = 5432) -> bool:
"""Comprova si el port de PostgreSQL és accessible."""
try:
with socket.create_connection((host, port), timeout=5):
return True
except (socket.timeout, ConnectionRefusedError):
return False
def do_GET(self):
self.send_response(200)
self.send_header("Content-type", "application/json")
self.end_headers()
dbhost: str = os.environ.get("DATABASE_HOST")
response = {
"message": "Hola des de Python!",
"hostname": socket.gethostname(),
"database_host": dbhost,
"database_reachable": self.check_database(dbhost)
}
self.wfile.write(json.dumps(response).encode())
if __name__ == "__main__":
server = HTTPServer(('0.0.0.0', 8000), Handler)
print("Servidor escoltant al port 8000...")
server.serve_forever()Executam l’aplicació:
docker run --name webapp-app \
--network webapp-net \
--volume ~/Projects/webapp-demo:/app:ro \
--workdir /app \
--env DATABASE_HOST=webapp-db \
--detach \
python:3.13-slim \
python app.pyL’aplicació es connecta al port de la base de dades usant el nom del contenidor webapp-db com a host. Docker resol aquest nom a la IP correcta dins de la xarxa.
Proxy invers #
Per al nostre proxy invers crearem un fitxer de configuració nginx/default.conf dins el directori del projecte:
mkdir --parents ~/Projects/webapp-demo/nginxUna configuració bàsica d’NGINX per aquest exemple seria:
upstream python {
server webapp-app:8000;
}
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://python;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /health {
access_log off;
return 200 'OK';
add_header Content-Type text/plain;
}
}Ja podem executar el contenidor amb NGINX:
docker run --name webapp-nginx \
--network webapp-net \
--volume ~/Projects/webapp-demo/nginx:/etc/nginx/conf.d:ro \
--publish 8080:80 \
--detach \
nginx:1.29-alpineVerificació #
Començam la verificació comprovant que tots els contenidors estan a la mateixa xarxa:
docker network inspect webapp-net \
--format '{{range .Containers}}{{.Name}} {{end}}'Hauria de mostrar:
webapp-db webapp-app webapp-nginxI continuant comprovant la comunicació entre els contenidors executant una petició HTTP:
curl --silent http://localhost:8080/ | jqEl resultat serà:
{
"message": "Hola des de Python!",
"hostname": "abc123def456",
"database_host": "webapp-db",
"database_reachable": true
}Neteja #
Per a netejar tots els contenidors, el volum i la xarxa podem usar les següents comandes:
docker rm --force webapp-nginx webapp-app webapp-db
docker network rm webapp-net
docker volume rm webapp-pgdata
rm -rf ~/Projects/webapp-demoEl següent apartat fa ús d’aquests contenidors per a demostrar com inspeccionar i depurar les xarxes de Docker. Si vols executar les comandes, espera una mica a executar les comandes de neteja.
Inspecció i depuració #
Podem veure les xarxes d’un contenidor amb la següent comanda:
docker inspect webapp-app \
--format '{{json .NetworkSettings.Networks}}' | jqPodem veure els contenidors d’una xarxa amb la següent comanda:
docker network inspect webapp-netPodem comprovar la connectivitat des de dins amb la següent comanda:
docker exec -it webapp-db sh -c "ping -c 2 webapp-app"Podem verificar ports oberts des de dins d’un contenidor amb la següent comanda:
docker exec -it webapp-db bash \
-c "(echo > /dev/tcp/webapp-nginx/80) && \
echo 'obert' || echo 'tancat'"Connectar i desconnectar contenidors #
Podem connectar un contenidor existent a una xarxa addicional sense aturar-lo:
docker network connect webapp-net existing-containerAixò és útil, per exemple, per a:
- Afegir un contenidor de monitorització a una xarxa existent.
- Connectar temporalment un contenidor de depuració.
- Migrar contenidors entre xarxes.
Per desconnectar:
docker network disconnect webapp-net existing-containerLa xarxa host #
El driver host elimina l’aïllament de xarxa entre el contenidor i l’amfitrió. El contenidor usa directament la interfície de xarxa de l’amfitrió:
docker run --rm --network host nginx:1.29-alpineAmb --network host:
- El contenidor no obté una IP pròpia.
- El port 80 de NGINX està directament accessible a l’amfitrió (sense necessitat de
--publish). - No hi ha traducció de ports (NAT).
Casos d’ús:
- Aplicacions que necessiten el màxim rendiment de xarxa.
- Contenidors que necessiten veure tot el tràfic de xarxa de l’amfitrió.
- Situacions on la traducció de ports és problemàtica.
host només funciona a Linux. A MacOS i Windows, Docker s’executa dins d’una màquina virtual, per la qual cosa --network host connecta el contenidor a la xarxa de la VM, no a la de l’amfitrió real.
La xarxa none #
El driver none desconnecta completament el contenidor de qualsevol xarxa:
docker run --rm --network none alpine ip addrEl contenidor només tendrà la interfície de loopback (lo). Això és útil, per exemple, per a:
- Processar dades sensibles sense risc d’exfiltració per xarxa.
- Executar tasques que no necessiten connectivitat.
- Màxim aïllament per seguretat.
Exercicis pràctics #
Es proposen dos exercicis pràctics per facilitar l’aprenentatge progressiu.
Exercici 1 #
Xarxa amb múltiples contenidors
- Crea una xarxa anomenada
exercici-net. - Executa un contenidor MariaDB anomenat
exercici-dbconnectat a la xarxa, amb una base de dadestestdb, usuaritestuseri contrasenyatestpass. - Executa un contenidor Adminer (
adminer:latest) anomenatexercici-adminerconnectat a la mateixa xarxa, publicant el port 8080. - Accedeix a Adminer des del navegador i connecta’t a MariaDB usant el nom del contenidor com a servidor.
- Crea una taula i insereix algunes dades per verificar la connexió.
- Usa
docker network inspectper veure els dos contenidors connectats. - Neteja tots els recursos.
Pista: A Adminer, el camp “Server” ha de ser el nom del contenidor de MariaDB.
Exercici 2 #
Aïllament de xarxes
- Crea dues xarxes:
frontend-netibackend-net. - Executa un contenidor PostgreSQL anomenat
dbconnectat només abackend-net. - Executa un contenidor Alpine anomenat
appconnectat a ambdues xarxes (frontend-netibackend-net). - Executa un contenidor Alpine anomenat
webconnectat només afrontend-net. - Verifica que:
apppot fer ping adb(ambdós abackend-net)apppot fer ping aweb(ambdós afrontend-net)webno pot fer ping adb(xarxes diferents)
- Neteja tots els recursos.
Pista: Pots connectar un contenidor a una segona xarxa amb
docker network connect.