Perché due container sullo stesso host non si vedono di default
Avvia Postgres e la tua API con un semplice docker run e l’API non riesce a raggiungere il database, anche se entrambi i container girano sulla stessa macchina.
| |
Entrambi finiscono sulla bridge di default di Docker, quella su cui va a finire ogni container Docker se non specifichi altro. Ricevono un indirizzo IP. Quello che non ricevono è un modo per trovarsi a vicenda per nome: l’API dovrebbe quindi avere l’IP interno del database scritto a mano, un indirizzo che cambia ogni volta che il container riparte.
Una rete Docker risolve il problema. È uno switch virtuale a cui i container si collegano, con un proprio intervallo di IP e un proprio DNS. Creandone una, i nomi dei container diventano hostname e decidi tu chi può vedere chi.
Quale driver di rete usare: bridge, host o overlay
Docker include diversi driver di rete predefiniti. Tre di questi coprono quasi tutto ciò che incontrerai su un singolo host o su uno swarm:
| Driver | Ambito | I container hanno un IP proprio | DNS per nome | Uso tipico |
|---|---|---|---|---|
| bridge (default) | singolo host | sì | no (solo IP) | quello che ottieni senza il flag --network |
| bridge (user-defined) | singolo host | sì | sì | la scelta di default per sviluppo locale e deployment su singolo host |
| host | singolo host | no, condivide lo stack di rete dell’host | n/a | servizi ad alto throughput, strumenti che devono vedere la rete reale dell’host |
| overlay | più host (swarm) | sì | sì | servizi distribuiti su più host Docker |
C’è anche none, che dà al container solo un’interfaccia di loopback e nient’altro. Utile per un job batch che non deve avere alcun accesso di rete.
Ogni docker run finisce sulla bridge di default. Funziona, ma instrada solo per IP: il vecchio flag --link serviva a coprire l’assenza dei nomi, e Docker lo considera legacy. Una bridge user-defined fa lo stesso lavoro con il DNS già incluso. Ed è quella che vuoi.
Come creare una rete Docker e collegarci i container
| |
Questa è una bridge privata con una propria subnet, isolata dalla bridge di default e da ogni altra rete user-defined che crei. Collega i container all’avvio:
| |
Dentro api, connettersi a db:5432 funziona. Nessuna ricerca di IP, nessun giro di variabili d’ambiente: il DNS integrato di Docker risolve il nome del container db con il suo indirizzo attuale su app-net, e aggiorna la mappatura quando il container riparte con un IP nuovo. Docker Compose si appoggia allo stesso meccanismo. docker compose up crea una rete per lo stack, e ogni servizio raggiunge gli altri per nome del servizio.
Puoi anche collegare un container già in esecuzione, senza riavviarlo:
| |
Un container può stare su più reti contemporaneamente. È così che dai a un servizio una rete esposta pubblicamente e una privata condivisa solo con i suoi container backend.
Perché pubblicare una porta non è il modo in cui due container comunicano
-p 8080:8080 mappa la porta 8080 dell’host su quella del container, così puoi raggiungere l’API dal browser su localhost:8080. Non dice nulla sul fatto che api possa raggiungere db. Il traffico tra container sulla stessa rete user-defined resta interno, porte pubblicate o no.
Pubblica solo le porte che qualcosa fuori dall’host Docker deve raggiungere. Un database che parla solo con l’API non ha bisogno di -p 5432:5432: lascialo non pubblicato e né l’host né internet potranno connettersi direttamente. api continua comunque a raggiungerlo, perché condividono app-net.
Quando usare –network host, e quando no
| |
Con --network host il container salta il proprio namespace di rete e si lega direttamente alle interfacce dell’host. Niente NAT, niente bridge, niente mappatura di porte: -p e -P vengono ignorati, e Docker stampa un warning se li passi comunque. Questo elimina un piccolo overhead per pacchetto e lascia al container la vista reale delle interfacce di rete dell’host: utile per gli strumenti di monitoraggio di rete e per i software che hanno bisogno di un range di porte ampio e contiguo.
Toglie anche l’isolamento che dà una bridge. Il container può legarsi a qualunque porta libera dell’host, e vede tutto ciò che c’è sulla rete dell’host. Su Linux funziona senza limitazioni. Docker Desktop per Mac e Windows richiede di attivarlo esplicitamente (disponibile da Docker Desktop 4.34), perché la rete della VM di Docker non è quella dell’host. I container Windows non lo supportano affatto. Per la maggior parte dei servizi, una bridge user-defined con porte pubblicate fa lo stesso lavoro con meno esposizione.
Quando serve una rete overlay al posto di una bridge
Le reti bridge e host si fermano al confine di un singolo host Docker. Esegui Docker Swarm su più macchine e un servizio sull’host A ha bisogno di una rete overlay per raggiungere un servizio sull’host B:
| |
Un’overlay incapsula il traffico dei container e lo instrada tra i daemon Docker di ogni nodo dello swarm: i nomi dei container su macchine fisiche o virtuali diverse si risolvono esattamente come su una bridge. Il flag --attachable permette anche ai container standalone di collegarcisi; senza, possono farlo solo i servizi swarm. Se non usi swarm o un altro orchestratore multi-host, salta questo driver. Una rete bridge su singolo host copre gli stack Compose e i setup locali.
Come ispezionare e risolvere i problemi di una rete Docker
| |
Ogni rete presente sull’host, incluse bridge, host e none che Docker crea sempre.
| |
La subnet, il gateway e ogni container collegato in quel momento con il suo IP su quella rete. È il primo posto dove guardare quando un container non riesce a raggiungerne un altro. Leggi la sezione Containers: se uno dei due manca, non è su questa rete, e docker network connect lo mette lì.
Per verificare la risoluzione DNS dall’interno di un container in esecuzione:
| |
Un IP di ritorno significa che la risoluzione dei nomi funziona e il problema è nell’applicazione: porta sbagliata, o il servizio non è ancora in ascolto. Nessuna risposta significa che i due container non sono sulla stessa rete. Conferma con docker network inspect, oppure chiedi a ciascun container a cosa è collegato:
| |
Una rete per ogni stack, con tutti i container sopra
La bridge di default è quella che hai gratis. Non è quella su cui dovresti costruire. Esegui docker network create <app>-net per ogni stack applicativo, collegaci tutti i container di cui quello stack ha bisogno, e riserva --network host ai rari servizi che hanno davvero bisogno dello stack di rete dell’host.
Quando gestisci a mano più di un paio di container, smetti di farlo a mano. Docker Compose crea la rete e ci collega i container al posto tuo da un unico file YAML. I volumi Docker coprono l’altra metà del problema di sopravvivere a un riavvio: i dati che quei container scrivono.