Aller au contenu
Sylvain Joffraud l4w
Retour

`extra_hosts` override le DNS Docker en silence

Setup classique pour mixer dev local et conteneurs Docker. Mon docker-compose.yml lance des services satellites (Postgres, un autre service tiers), mais le backend tourne directement sur l’hôte en dev (npm run dev, hot reload). Pour que les autres conteneurs joignent le backend, je mappe son nom vers le host-gateway :

services:
  satellite:
    image: vendor/satellite:latest
    extra_hosts:
      - "backend:host-gateway"

En dev ça marche : depuis le conteneur satellite, wget http://backend:3000/healthz tape l’hôte (où npm run dev écoute sur :3000). En prod, je rajoute un service backend au compose, je le déploie sur Coolify/n’importe quel PaaS docker-compose-friendly. Les conteneurs sont sur le même réseau. Et là silencieusement :

Symptôme

docker exec satellite getent hosts backend
# → 172.17.0.1   backend  backend
docker exec satellite wget http://backend:3000/healthz
# → wget: can't connect to remote host (172.17.0.1): Connection refused

backend résout vers 172.17.0.1 (host-gateway), pas vers le conteneur backend qui est pourtant sur le même réseau Docker. Le wget échoue parce que rien n’écoute sur 172.17.0.1:3000 côté hôte en prod.

Le service satellite continue à fonctionner pour le reste. Aucun log d’erreur dans le service ni dans Docker. Le forward / le call HTTP qui devrait atteindre le backend tombe juste dans le vide.

Cause

extra_hosts en docker-compose prend la priorité sur le DNS Docker du réseau. Tant que cette ligne est là, le conteneur résout backend selon extra_hosts (= host-gateway), JAMAIS via le DNS Docker interne qui pointerait vers le conteneur du service backend.

Cette priorité est documentée mais peu mise en avant. Le piège tombe au moment où l’archi passe de « dev local hors Docker » → « tout containerisé en prod », parce que le extra_hosts qui était utile en dev devient une bombe silencieuse en prod.

Solution

Utiliser un alias dédié au dev, ne JAMAIS reprendre le nom d’un service qui existera en prod. Convention propre :

services:
  satellite:
    image: vendor/satellite:latest
    extra_hosts:
      - "host.docker.internal:host-gateway"   # alias générique standard
    environment:
      # En dev (depuis l'hôte) : BACKEND_URL=http://host.docker.internal:3000
      # En prod (compose Docker) : BACKEND_URL=http://backend:3000
      BACKEND_URL: ${BACKEND_URL:-http://host.docker.internal:3000}

Et la config statique du service (par exemple traccar.xml, forward.url…) doit utiliser l’alias host.docker.internal en dev, le nom du service en prod. Si la config est statique sans interpolation possible, choisir la valeur prod (http://backend:3000) et gérer le cas dev autrement (par exemple générer une variante locale du fichier).

Lessons learned

#docker #compose #devops #troubleshooting #networking #dns #host-gateway

Questions fréquentes

Pourquoi mon conteneur résout un nom de service vers 172.17.0.1 au lieu du conteneur Docker ?
Parce qu’une entrée extra_hosts mappe ce nom vers host-gateway, et extra_hosts prend la priorité sur le DNS Docker du réseau. Vérifiez avec docker exec <conteneur> getent hosts <service> : si la réponse est l’IP du host-gateway (172.17.0.1) et non une IP du réseau Docker (172.20.0.X), c’est extra_hosts qui détourne la résolution.
Mon wget vers un service Docker renvoie Connection refused alors que les conteneurs sont sur le même réseau, pourquoi ?
Le nom du service résout vers l’IP du host-gateway au lieu du conteneur cible, car un extra_hosts réutilise ce nom de service. Le call tombe sur l’hôte où rien n’écoute sur ce port en prod, d’où le « Connection refused », sans aucun log d’erreur côté service ni Docker. C’est un échec silencieux typique de l’override extra_hosts.
Comment garder un alias host-gateway en dev sans casser la résolution DNS en prod containerisée ?
Ne réutilisez jamais le nom d’un service du compose dans extra_hosts ; utilisez l’alias générique standard host.docker.internal:host-gateway. Pilotez ensuite la cible via une variable, par exemple BACKEND_URL=http://host.docker.internal:3000 en dev et http://backend:3000 en prod.
À quoi sert host.docker.internal et faut-il l’ajouter sur Linux ?
C’est l’alias canonique pour joindre « l’hôte depuis le conteneur », default sur Windows et Mac. Sur Linux, il faut l’ajouter explicitement via extra_hosts avec host.docker.internal:host-gateway pour que les conteneurs utilisent le même nom, sans jamais piétiner un nom de service réel du réseau Docker.

Partager cet article :

Article précédent
Tracker IoT 4G : INTERNET FAILED en intérieur, bande B20
Article suivant
Traccar : forcer un protocole sur un port précis