Modificare la risoluzione DNS di un container Docker per usare domini di sviluppo definiti sull'host

Quando si lavora in sviluppo locale è comune definire domini come api.test o frontend.test nel file /etc/hosts della macchina host. Con Docker però nasce un problema: il container non vede automaticamente le voci presenti nel /etc/hosts dell'host e quindi non riesce a risolvere quei domini.

In questo articolo vediamo in dettaglio:

  • come Docker gestisce DNS e /etc/hosts nei container;
  • come far risolvere domini .test definiti sull'host;
  • pro e contro delle varie soluzioni;
  • alcuni esempi pratici con docker run e Docker Compose.

1. Come funziona la risoluzione DNS nei container Docker

Ogni container Docker è isolato dal sistema host. Questo significa che:

  • il container ha il proprio file /etc/hosts, generato da Docker al momento dell'avvio;
  • il container non legge il /etc/hosts dell'host;
  • il container utilizza un resolver DNS configurato da Docker (tipicamente l'internal DNS di Docker o quello specificato nel daemon o nel comando docker run).

Se quindi sulla macchina host hai qualcosa come:

# /etc/hosts dell'host
127.0.0.1   api.test
127.0.0.1   frontend.test

il container non vedrà queste voci a meno che tu non le aggiunga esplicitamente al suo /etc/hosts o non configuri un DNS che le conosca.

2. Obiettivo: risolvere domini .test definiti nell'host

L'obiettivo tipico è questo: dall'interno del container vuoi poter raggiungere, ad esempio, api.test, che sull'host punta a un certo IP (spesso 127.0.0.1 o un altro indirizzo locale).

Hai essenzialmente tre strade principali:

  1. passare esplicitamente le voci hosts al container (--add-host o extra_hosts);
  2. usare la parola chiave speciale host-gateway per far puntare il dominio all'IP dell'host visto dal container;
  3. configurare un DNS locale (es. dnsmasq, Pi-hole, Bind) e dire ai container di usare quel DNS.

3. Soluzione 1: usare --add-host / extra_hosts

Il modo più diretto e semplice per far sì che un container risolva un dominio .test è aggiungerlo al suo /etc/hosts al momento dell'avvio.

3.1. Esempio con docker run

Supponiamo che sull'host tu abbia:

# /etc/hosts dell'host
127.0.0.1   api.test

Puoi avviare il container così:

docker run --add-host api.test:127.0.0.1 myimage

Nel container verrà generato un /etc/hosts che, tra le altre cose, conterrà una riga simile a:

127.0.0.1   api.test

In questo modo, qualunque processo dentro il container che provi a risolvere api.test userà quell'indirizzo.

3.2. Esempio con Docker Compose

Con Docker Compose, l'equivalente è la direttiva extra_hosts:

services:
  app:
    image: myimage
    extra_hosts:
      - "api.test:127.0.0.1"

Anche in questo caso, Docker aggiungerà la voce al /etc/hosts del container.

3.3. Pro e contro della soluzione --add-host

  • Pro:
    • semplice da configurare;
    • non richiede un DNS esterno;
    • funziona ovunque, anche senza privilegi particolari sull'host.
  • Contro:
    • devi specificare esplicitamente l'IP (se cambia, va aggiornato dappertutto);
    • se hai molti domini .test, la configurazione può diventare verbosa.

4. Soluzione 2: usare host-gateway per puntare all'host

Docker fornisce una parola chiave speciale, host-gateway, che rappresenta l'indirizzo IP dell'host visto dal container.

Invece di scrivere un IP fisso, puoi fare:

docker run --add-host api.test:host-gateway myimage

Oppure in Docker Compose:

services:
  app:
    image: myimage
    extra_hosts:
      - "api.test:host-gateway"

In questo modo, api.test punterà all'IP dell'host della rete Docker, indipendentemente dal valore numerico concreto.

4.1. Quando usare host-gateway

Questa modalità è molto comoda quando:

  • il servizio che ti interessa gira sull'host, non in un altro container;
  • non vuoi dover scoprire e mantenere aggiornato l'IP dell'host;
  • vuoi una configurazione più portabile tra ambienti diversi.

5. Soluzione 3: usare un DNS locale per i domini .test

Se stai usando molti domini .test o vuoi una soluzione più vicina a un ambiente “reale”, puoi configurare un DNS locale che risolva i tuoi domini di sviluppo e far sì che sia i container sia l'host lo usino.

Esempio tipico: installi e configuri dnsmasq su una macchina della tua LAN (o sulla stessa macchina host) per far sì che tutti i domini *.test puntino a un certo IP.

5.1. Configurare il DNS nei container

Una volta che hai un server DNS (ad esempio 192.168.1.10) che sa risolvere api.test, puoi dire ai container di usare quel DNS con:

docker run --dns=192.168.1.10 myimage

Oppure, a livello di Docker daemon, modificando /etc/docker/daemon.json sull'host:

{
  "dns": ["192.168.1.10"]
}

Dopo il riavvio del servizio Docker, tutti i container useranno quel DNS, a meno che non venga specificato diversamente al momento del run.

5.2. Pro e contro del DNS locale

  • Pro:
    • gestione centralizzata dei domini;
    • l'host e i container condividono la stessa visione del DNS;
    • molto scalabile quando i domini diventano numerosi.
  • Contro:
    • richiede la configurazione e manutenzione di un DNS server;
    • è leggermente più complesso rispetto a un semplice --add-host.

6. Perché non si può “ereditare” /etc/hosts dell'host

Una domanda frequente è: “Posso dire a Docker di usare direttamente il /etc/hosts dell'host?”.

La risposta, ad oggi, è: no, non in modo supportato e automatico. Il motivo è legato al modello di isolamento dei container:

  • ogni container ha un suo namespace di rete e un suo filesystem;
  • Docker genera il /etc/hosts del container in base ai parametri del run, alla rete, ai link tra container, ecc.;
  • agganciare il /etc/hosts dell'host così com'è romperebbe vari assunti di isolamento e potrebbe creare inconsistenze.

In teoria potresti montare manualmente il file di hosts dell'host nel container, ma è una pratica sconsigliata perché:

  • può cambiare il file sull'host in modi non voluti dal container (e viceversa, se montato in lettura/scrittura);
  • può creare effetti collaterali difficili da diagnosticare.

7. Pattern consigliato per domini .test in sviluppo

Un pattern pratico e relativamente semplice da mantenere è:

  1. Sull'host, far girare i servizi che devono rispondere a .test (o esporli da altri container).
  2. Nel file /etc/hosts dell'host, definire i domini .test per uso del browser e di altri tool locali.
  3. Per i container che devono raggiungere tali domini:
    • usare --add-host dominio.test:host-gateway o extra_hosts con host-gateway;
    • oppure, se usi già un DNS locale, configurare correttamente il DNS con --dns o daemon.json.

Esempio completo con Docker Compose che usa host-gateway:

version: "3.9"

services:
  app:
    image: myimage
    extra_hosts:
      - "api.test:host-gateway"
    environment:
      - API_BASE_URL=http://api.test:8080

In questo scenario:

  • l'applicazione nel container può contattare http://api.test:8080;
  • api.test viene risolto all'IP dell'host visto dal container;
  • sull'host puoi avere una voce in /etc/hosts che punta lo stesso dominio a 127.0.0.1, così il browser dell'host vede lo stesso indirizzo simbolico.

8. Riepilogo

  • I container Docker non leggono automaticamente il /etc/hosts dell'host.
  • Per far risolvere domini .test definiti sull'host hai tre soluzioni principali:
    • passare le voci hosts esplicitamente con --add-host o extra_hosts;
    • usare host-gateway per evitare di specificare un IP fisso e puntare all'host;
    • configurare un DNS locale e dire ai container di usare quel resolver.
  • La soluzione più rapida nella maggior parte dei casi è usare --add-host o extra_hosts con host-gateway, così non devi gestire manualmente gli indirizzi IP.

Con questa configurazione, i tuoi container potranno risolvere senza problemi i domini .test che usi sulla macchina host, mantenendo al tempo stesso l'isolamento e la coerenza dell'ambiente Docker.

Torna su