Docker · Ubuntu

Docker no usa los mismos DNS que el host

Docker_(container_engine)_logo

Docker, para los contenedores que no definen su propia red – donde docker usa su dns interno que «delega» en el del host -, rellena el fichero /etc/resolv.conf de los contenedores copiando el /etc/resolv.conf del host y filtrando cualquier servidor de nombres local como podría ser 127.0.0.53.  Si no hay ningún otro servidor de nombres, Docker añadirá los servidores DNS públicos de Google (8.8.8.8 y 8.8.4.4)

En Ubuntu 18.04, y otros sistemas que usan systemd-resolved, /etc/resolv.conf es un enlace a /run/systemd/resolve/stub-resolv.conf y su contenido siempre tiene la ip local 127.0.0.53:

nameserver 127.0.0.53
options edns0

y, como hemos explicado antes, Docker filtra cualquier dirección de loopback al leer el resolv.conf. Por tanto, nuestros contenedores Docker no usaran los DNS que utiliza el host, sino que siempre usará los DNS de Google.

Para solucionar esto, vamos a «tomar control» del fichero /etc/resolv.conf y a realizar las configuraciones necesarias para que los contenedores acaben usando la configuración de DNS del host.

La idea es sencilla.

  1. En /etc/resolv.conf seguiremos referenciando como primer dns el 127.0.0.53 (que sabemos que Docker eliminará)
  2. Además añadimos una nueva entrada referenciando al propio host (para que los contenedores Docker usen el host como servidor DNS)


Vamos con ello.

  1. Elimina y crea de nuevo el fichero /etc/resolv.conf (de modo que deje de ser un enlace a /run/systemd/resolve/stub-resolv.conf):

    sudo rm /etc/resolv.conf
    sudo touch /etc/resolv.conf

  2. Como ahora /etc/resolv.conf ya no es un enlace, NetworkManager tomaría el control del mismo. Para evitar esto, vamos a configurar explicitamente NetworkManager para que envíe notificaciones a systemd-resolved con los servidores DNS que establece en sus conexiones y para que no modifique el fichero /etc/resolv.conf. Esto lo conseguimos creando un fichero de configuración en /etc/NetworkManager/conf.d/, por ejemplo lo llamaremos /etc/NetworkManager/conf.d/systemd-resolved-for-docker.conf:

    [main]
    # NetworkManager will push the DNS configuration to systemd-resolved
    dns=systemd-resolved
    # NetworkManager won’t ever write anything to /etc/resolv.conf
    rc-manager=unmanaged

  3. Ahora editamos el fichero /etc/resolv.conf para establecer la configuración que nos interesa (172.17.0.1 es la dirección IP del host en la red virtual que crea Docker. Puedes averiguarla con el comando nmcli | grep -A2 docker0):

    # systemd-resolvd name server
    nameserver 127.0.0.53
    # docker host ip
    nameserver 172.17.0.1

Con esto debería ser suficiente, ya que los contenedores Docker realizarían las peticiones DNS al host (172.17.0.1), donde se está ejecutando systemd-resolved. El problema es que systemd-resolved tiene hardcoded que solamente escuche peticiones en la dirección 127.0.0.53 de la interfaz lookback local y, por tanto, no se puede configurar para que acepte las peticiones que le lleguen por la interfaz docker0. Para solucionar esto, vamos a instalar dnsmasq y configurarlo para que acepte peticiones de los contenedores Docker y use systemd-resolved para resolverlas.

  1. Instalamos dnsmasq (ojo, que seguramente la instalación de un error al intentar arrancar el servidor DNS con la configuración por defecto, ya que systemd-resolved ya está escuchando en el puerto 53):

    sudo apt-get -y install dnsmasq

  2. Editamos el fichero /etc/dnsmasq.conf para establecer la configuración indicada (una vez más, 172.17.0.1 es la IP del host en la red Docker):

    # Use interface docker0
    interface=docker0
    # Never forward plain names (without a dot or domain part)
    domain-needed
    # Don’t poll for changes in /etc/resolv.conf
    no-poll
    # Don’t use /etc/resolv.conf or any other file
    no-resolv
    # Increase cache-size (limit 10000)
    cache-size=500
    # Explicitly specify the address to listen on
    listen-address=172.17.0.1
    # This option forces dnsmasq to bind only the interfaces it is listening on
    bind-interfaces
    # Set systemd-resolved DNS server
    server=127.0.0.53

  3. Modificamos la definición del servicio dnsmasq de modo que espere a que la interfaz docker0 esté disponible.

    sudo vi /lib/systemd/system/dnsmasq.service

    Añadimos un nuevo apartado ExecStartPre:

    # Wait for doker interface to be ready as we bind dnsmasq to it
    ExecStartPre=/usr/lib/systemd/systemd-networkd-wait-online –interface=docker0

    Así como incrementamos el tiempo de máximo de espera para el arranque del servicio (para dar tiempo a que la interfaz docker0 esté lista):

    # Override default 90 seconds timeout
    TimeoutStartSec=120

  4. Actualizamos la configuración de los servicios

    sudo systemctl daemon-reload

  5. Reiniciamos la red, dnsmasq y docker:

    sudo service network-manager restart
    sudo service dnsmasq restart
    sudo service docker restart

Ahora tus contenedores deberían ser capaces de resolver los DNS usando la máquina host.

Algunos enlaces de interés

Deja un comentario