web-dev-qa-db-fra.com

Comment puis-je donner aux conteneurs Docker l'accès à un résolveur DNS local dnsmasq sur l'hôte?

Il existe de nombreuses façons dont les conteneurs Docker peuvent être confus au sujet des paramètres DNS (recherchez simplement SO ou sur Internet pour "Docker DNS" pour voir ce que je veux dire), et l'une des solutions de contournement courantes suggéré est de:

  1. Configurer dnsmasq en tant que résolveur DNS local sur le système hôte
  2. Liez-le au docker0 interface réseau
  3. Configurer Docker pour utiliser le docker0 Adresse IP pour la résolution DNS

Cependant, essayer d'appliquer cette solution de contournement naïvement sur de nombreux systèmes Linux modernes vous enverra un rabbithole de complexité de gestion de réseau et de processus Linux, car systemd vous assure que dnsmasq n'est pas en cours d'exécution, mais netstat vous indique que c'est le cas et que la tentative de démarrage de dnsmasq échoue avec la plainte que le port 53 est déjà utilisé.

Alors, comment donnez-vous de manière fiable à vos conteneurs un accès à un résolveur local exécuté sur l'hôte, même si le système en a déjà un par défaut?

16
ncoghlan

Le problème ici est que de nombreux systèmes Linux modernes exécutent implicitement dnsmasq, donc ce que vous essayez maintenant de faire est de configurer une instance seconde spécifiquement pour Docker. Il y a en fait 3 paramètres nécessaires pour le faire correctement:

  • --interface=docker0 pour écouter sur l'interface réseau Docker par défaut
  • --except-interface=lo pour ignorer l'ajout implicite de l'interface de bouclage
  • --bind-interfaces pour désactiver une fonctionnalité dnsmasq où elle écoute toujours sur toutes les interfaces par défaut, même lorsque son seul trafic de traitement pour l'une d'entre elles

Configuration d'une instance dnsmasq dédiée

Plutôt que de modifier les paramètres de l'instance dnsmasq à l'échelle du système par défaut, ces instructions montrent la configuration d'une instance dnsmasq dédiée avec systemd, sur un système qui définit déjà un service dnsmasq par défaut:

$ Sudo cp /usr/lib/systemd/system/dnsmasq.service /etc/systemd/system/dnsmasq-docker.service
$ sudoedit /etc/systemd/system/dnsmasq-docker.service

Tout d'abord, nous copions les paramètres de service par défaut dans un fichier de service dédié. Nous éditons ensuite ce fichier de service et recherchons la section de définition de service, qui devrait ressembler à ceci:

[Service]
ExecStart=/usr/sbin/dnsmasq -k

Nous modifions cette section pour définir nos options supplémentaires:

[Service]
ExecStart=/usr/sbin/dnsmasq -k --interface=docker0 --except-interface=lo --bind-interfaces

Le fichier entier est en fait assez court:

[Unit]
Description=DNS caching server.
After=network.target
After=docker.service
Wants=docker.service

[Service]
ExecStart=/usr/sbin/dnsmasq -k --interface=docker0 --except-interface=lo --bind-interfaces

[Install]
WantedBy=multi-user.target

Le [Unit] la section indique à systemd d'attendre que la pile réseau et le démon docker principal soient disponibles pour démarrer ce service, tandis que [Install] indique à quelle cible d'état du système ajouter le service lors de son activation.

Nous configurons ensuite notre nouveau service pour qu'il démarre au démarrage du système, et le démarrons également explicitement pour une utilisation immédiate:

$ Sudo systemctl enable dnsmasq-docker
$ Sudo systemctl start dnsmasq-docker

Comme dernière étape pour faire fonctionner le service, nous vérifions qu'il a réellement démarré comme prévu:

$ Sudo systemctl status dnsmasq-docker

Les deux lignes clés que nous recherchons dans cette sortie sont:

Loaded: loaded (/etc/systemd/system/dnsmasq-docker.service; enabled; vendor preset: disabled)
Active: active (running) since <date & time>

Sur la première ligne, notez l'état "activé", tandis que sur la seconde, l'état "actif (en cours)". Si le service n'a pas démarré correctement, alors les informations de diagnostic supplémentaires expliqueront, espérons-le, pourquoi (bien qu'il puisse parfois être malheureusement cryptique, d'où ce post).

Remarque: cette configuration peut ne pas démarrer dnsmasq-docker au redémarrage du système avec une erreur sur le docker0 interface non définie. En attendant docker.service semble être assez fiable pour éviter ce problème, si la résolution de noms à partir des conteneurs Docker ne fonctionne pas après un redémarrage du système, essayez d'exécuter:

$ Sudo systemctl start dnsmasq-docker

Configuration du pare-feu hôte

Pour pouvoir utiliser le résolveur à partir de conteneurs Docker locaux, nous devons également supprimer le pare-feu réseau entre l'hôte et les systèmes s'exécutant dans des conteneurs:

Sudo firewall-cmd --permanent --zone=trusted --change-interface=docker0
Sudo firewall-cmd --reload

(Ce serait une idée absolument terrible sur un hôte de conteneur de production, mais peut être un compromis risque/commodité utile sur un poste de travail de développeur)

Configuration de Docker à l'aide d'un fichier d'environnement systemd

Maintenant que notre résolveur local est en cours d'exécution, nous devons configurer Docker pour l'utiliser par défaut. Docker a besoin de l'adresse IP du docker0 interface plutôt que le nom de l'interface, nous utilisons donc ifconfig pour récupérer cela:

$ ifconfig docker0 | grep inet
        inet 172.17.0.1  netmask 255.255.0.0  broadcast 0.0.0.0

Donc, pour mon système, l'interface de l'hôte sur la valeur par défaut docker0 le pont est accessible en tant que 172.17.0.1 (Ajout de | cut -f 10 -d ' ' à cette commande devrait filtrer la sortie à l'adresse IP uniquement)

Puisque je suppose un Linux basé sur systemd avec un package Docker fourni par le système, nous interrogerons le fichier de service du package système pour savoir comment le service est démarré:

$ cat /usr/lib/systemd/system/docker.service

La première chose que nous recherchons est la commande exacte utilisée pour démarrer le démon, qui devrait ressembler à ceci:

ExecStart=/usr/bin/docker daemon \
          $OPTIONS \
          $DOCKER_STORAGE_OPTIONS \
          $DOCKER_NETWORK_OPTIONS \
          $INSECURE_REGISTRY

La deuxième partie que nous recherchons est de savoir si le service est configuré ou non pour utiliser un fichier d'environnement, comme indiqué par une ou plusieurs lignes comme celle-ci:

EnvironmentFile=-/etc/sysconfig/docker

Lorsqu'un fichier d'environnement est utilisé (comme c'est le cas sur Fedora 23), la façon de modifier les paramètres du démon Docker consiste à modifier ce fichier et à mettre à jour la variable d'environnement appropriée:

$ sudoedit /etc/sysconfig/docker

L'entrée OPTIONS existante sur Fedora 23 ressemble à ceci:

OPTIONS='--selinux-enabled --log-driver=journald'

Pour modifier les paramètres de résolution DNS par défaut, nous le modifions pour qu'il ressemble à ceci:

OPTIONS='--selinux-enabled --log-driver=journald --dns=172.17.0.1'

Et puis redémarrez le démon Docker:

$ Sudo systemctl restart docker

Avec cette modification mise en œuvre, les conteneurs Docker devraient désormais pouvoir accéder de manière fiable à tous les systèmes auxquels votre système hôte peut accéder (y compris via des tunnels VPN, ce qui était ma propre raison de devoir le comprendre)

Vous pouvez exécuter curl dans un conteneur pour vérifier que la résolution de noms fonctionne correctement:

docker run -it centos curl google.com

Remplacer google.com avec le nom d'hôte qui vous posait des problèmes (car vous n'auriez dû trouver cette réponse que si vous aviez un problème de résolution de nom lors de l'exécution d'un processus dans un conteneur Docker)

Configuration de Docker à l'aide d'un fichier de dépôt systemd

(Attention: étant donné que mon système utilise un fichier d'environnement, je n'ai pas pu tester l'approche basée sur un fichier de dépôt ci-dessous, mais cela devrait fonctionner - je l'ai inclus car la documentation Docker semble indiquer qu'ils préfèrent maintenant l'utilisation des fichiers drop-in systemd à l'utilisation des fichiers d'environnement)

Si le fichier de service système ne le fait pas utilise EnvironmentFile, alors l'entrée entière ExecStart peut être remplacée à l'aide d'un fichier de configuration de dépôt:

$ Sudo mkdir -p /etc/systemd/system/docker.service.d
$ sudoedit /etc/systemd/system/docker.service.d/daemon.conf

Nous demandons ensuite à Docker d'effacer l'entrée ExecStart existante et de la remplacer par notre nouvelle avec les paramètres supplémentaires:

[Service]
ExecStart=
ExecStart=/usr/bin/docker daemon \
          $OPTIONS \
          --dns 172.17.0.1 \
          $DOCKER_STORAGE_OPTIONS \
          $DOCKER_NETWORK_OPTIONS \
          $INSECURE_REGISTRY

Nous demandons ensuite à systemd de charger cette modification de configuration et de redémarrer Docker:

$ Sudo systemctl daemon-reload
$ Sudo systemctl restart docker

Références:

24
ncoghlan

Vous pouvez utiliser le résolveur DNS local de l'hôte (par exemple dnsmasq) à partir de vos conteneurs Docker s'ils se trouvent sur un réseau défini par l'utilisateur . Dans ce cas, un conteneur /etc/resolv.conf aura le serveur de noms 127.0.0.11 (alias le Docker serveur DNS intégré ), qui peut transmettre correctement les requêtes DNS à l'adresse de bouclage de l'hôte.

$ cat /etc/resolv.conf
nameserver 127.0.0.1
$ docker run --rm Alpine cat /etc/resolv.conf
nameserver 8.8.8.8
nameserver 8.8.4.4
$ docker network create demo
557079c79ddf6be7d6def935fa0c1c3c8290a0db4649c4679b84f6363e3dd9a0
$ docker run --rm --net demo Alpine cat /etc/resolv.conf
nameserver 127.0.0.11
options ndots:0    

Si tu utilises docker-compose , il créera automatiquement un réseau personnalisé pour vos services (avec un format de fichier v2 + ). Notez, cependant, que si docker-compose exécute les conteneurs dans un réseau défini par l'utilisateur, il les construit toujours dans le réseau par défaut . Pour utiliser un réseau personnalisé pour les builds, vous pouvez spécifier le paramètre network dans configuration de build (nécessite le format de fichier v3.4 + ).

3
Eugene Yarmash