Configurer la résolution DNS dans Docker

Dans l'article précédent sur l'aiguillage DNS nous avons demandé la résolution DNS au serveur dnsmasq écoutant sur 127.0.0.1.

Problème, les conteneurs Docker ont basculé sur le serveur DNS 8.8.8.8 au lieu d'utiliser la résolution DNS du réseau interne. Les conteneurs envoient leurs requêtes DNS à Google. Pourquoi ??

Lorsque le moteur Docker cherche un serveur DNS pour ses conteneurs, impossible d'utiliser l'adresse 127.0.0.1 puisque les conteneurs sont dans un réseau isolé. L'adresse 127.0.0.1 est ignorée. Le moteur Docker ne trouvant pas de serveur résolvable, il bascule sur Google DNS.

Outre passer par Google, cette configuration empêche vos conteneurs de résoudre les domaines privés comme par exemple une instance interne de Warehouse.

La solution s'impose : il faut exposer dnsmasq sur une IP routable. Routable ne veut pas dire publique. Il suffit juste qu'elle ne commence pas par 127..

Partager dnsmasq avec les conteneurs Docker

Première étape, monter une interface réseau virtuelle et lui associer une IP routable. Pour le besoin, j'ai pris 192.168.7.1. Je vais présenter comment configurer ça dans Debian, on peut aussi le faire avec systemd-networkd, mais c'est un poil plus compliqué.

D'abord, créer le fichier /etc/network/interfaces.d/dockerdns0 comme suivant :

~ # cat > /etc/network/interfaces.d/dockerdns
# Créer une interface dummy
auto dockerdns0
iface dockerdns0 inet manual
        pre-up ip link add $IFACE type dummy
        pre-up ip link set dev $IFACE up
        post-down ip link set dev $IFACE down || true
        post-down ip link delete dev $IFACE type dummy || true

# Configurer une adresse IP statique
iface dockerdns0 inet static
        netmask 255.255.255.0
        address 192.168.7.1
~ # sudo ifup dockerdns0
~ # ip address show dev dockerdns0
5: dockerdns0: <BROADCAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default qlen 1000
    link/ether de:2e:54:a0:3f:f7 brd ff:ff:ff:ff:ff:ff
    inet 192.168.7.1/24 brd 192.168.7.255 scope global dockerdns0
       valid_lft forever preferred_lft forever
    inet6 fe80::dc2e:54ff:fea0:3ff7/64 scope link
       valid_lft forever preferred_lft forever
~ #

Parfait ! Maintenant, il faut que notre dnsmasq écoute sur le port UDP 53 de l'adresse 192.168.7.1. Voici comment configurer dnsmasq :

~ # cat > /etc/dnsmasq.d/docker.conf
listen-address=192.168.7.1
~ # systemctl restart dnsmasq
~ # host cae.li 192.168.7.1
Using domain server:
Name: 192.168.7.1
Address: 192.168.7.1#53
Aliases:

cae.li has address 54.36.101.202
~ #

Ça fonctionne. Reste à indiquer au moteur Docker de l'utiliser. En bon citoyen de notre système, Docker lit les serveurs DNS dans resolv.conf. On va donc notifier resolvconf pour utiliser ce serveur en ajoutant un post-up et un post-down à la configuration de l'interface.

# /etc/network/interfaces.d/dockerdns
iface dockerdns0 inet static
        netmask 255.255.255.0
        address 192.168.7.1
        post-up   echo nameserver 192.168.7.1 | resolvconf -a lo.inet
        post-down resolvconf -d lo.inet || true

Les commandes post-up et post-down indiquent à resolvconf qu'un nouveau serveur DNS est disponible. On associe l'IP 192.168.7.1 à lo.inet pour prendre le pas sur lo.dnsmasq dans l'ordre des nameserver du fichier resolv.conf. En fait, tout notre système interrogera dnsmasq via cette IP désormais. Cf. interface-order(1).

Il faut redémarrer le moteur Docker avec systemctl restart docker. Pour valider ça, on va résoudre un nom de domaine interne dans un conteneur ad-hoc :

$ docker run --rm alpine:latest getent hosts registry.lan.mycompany.net
10.0.12.1     registry.lan.mycompany.net
$

Voilà ! Maintenant la configuration DNS est unifiée entre le système et les conteneurs. Les conteneurs vont profiter du cache DNS et de l'aiguillage. Prochaine étape, résoudre les conteneurs avec le domaine .docker !