如何让Docker容器访问主机上的dnsmasq本地DNS解析程序?

如何让Docker容器访问主机上的dnsmasq本地DNS解析程序?,docker,dns,Docker,Dns,Docker容器可能会以多种方式混淆DNS设置(只需在SO或更广泛的internet上搜索“Docker DNS”以了解我的意思),建议的常见解决方法之一是: 将dnsmasq设置为主机系统上的本地DNS解析程序 将其绑定到docker0网络接口 将Docker配置为使用docker0IP地址进行DNS解析 然而,试图在许多现代Linux系统上天真地应用此解决方案将使您陷入Linux网络和进程管理复杂性的困境,因为systemd向您保证dnsmasq没有运行,但netstat告诉您它正在运行,实

Docker容器可能会以多种方式混淆DNS设置(只需在SO或更广泛的internet上搜索“Docker DNS”以了解我的意思),建议的常见解决方法之一是:

  • 将dnsmasq设置为主机系统上的本地DNS解析程序
  • 将其绑定到
    docker0
    网络接口
  • 将Docker配置为使用
    docker0
    IP地址进行DNS解析
  • 然而,试图在许多现代Linux系统上天真地应用此解决方案将使您陷入Linux网络和进程管理复杂性的困境,因为systemd向您保证
    dnsmasq
    没有运行,但
    netstat
    告诉您它正在运行,实际上,尝试启动
    dnsmasq
    失败,抱怨端口53已在使用中


    那么,即使系统默认已经运行了一个本地解析器,您如何可靠地让容器访问主机上运行的本地解析器呢?

    这里的问题是许多现代Linux系统都隐式运行dnsmasq,所以您现在的目标是专门为Docker设置第二个实例。要正确执行此操作,实际上需要3种设置:

    docker run -it centos curl google.com
    
    • --interface=docker0
      监听默认的Docker网络接口
    • --except interface=lo
      跳过环回接口的隐式添加
    • --绑定接口
      关闭dnsmasq功能,默认情况下它仍侦听所有接口,即使它只处理其中一个接口的通信量
    设置专用dnsmasq实例

    这些说明显示的不是更改默认系统范围dnsmasq实例的设置,而是在已定义默认dnsmasq服务的系统上使用systemd设置专用dnsmasq实例:

    $ sudo cp /usr/lib/systemd/system/dnsmasq.service /etc/systemd/system/dnsmasq-docker.service
    $ sudoedit /etc/systemd/system/dnsmasq-docker.service
    
    首先,我们将默认服务设置复制到一个专用的服务文件中。然后,我们编辑该服务文件,并查找服务定义部分,该部分应如下所示:

    [Service]
    ExecStart=/usr/sbin/dnsmasq -k
    
    ExecStart=/usr/bin/docker daemon \
              $OPTIONS \
              $DOCKER_STORAGE_OPTIONS \
              $DOCKER_NETWORK_OPTIONS \
              $INSECURE_REGISTRY
    
    EnvironmentFile=-/etc/sysconfig/docker
    
    OPTIONS='--selinux-enabled --log-driver=journald'
    
    OPTIONS='--selinux-enabled --log-driver=journald --dns=172.17.0.1'
    
    我们编辑该部分以定义其他选项:

    [Service]
    ExecStart=/usr/sbin/dnsmasq -k --interface=docker0 --except-interface=lo --bind-interfaces
    
    事实上,这个时间很短:

    [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
    
    [Unit]
    部分告诉systemd等待,直到网络堆栈和主docker守护程序都可以启动此服务,而
    [Install]
    指示启用服务时要将其添加到哪个系统状态目标

    然后,我们将新服务配置为在系统启动时启动,并显式启动它以立即使用:

    $ sudo systemctl enable dnsmasq-docker
    $ sudo systemctl start dnsmasq-docker
    
    作为运行服务的最后一步,我们检查服务是否已按预期启动:

    $ sudo systemctl status dnsmasq-docker
    
    我们在该输出中寻找的两条关键线是:

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

    为了能够从本地Docker容器中使用解析器,我们还需要删除主机和容器中运行的系统之间的网络防火墙:

    sudo firewall-cmd --permanent --zone=trusted --change-interface=docker0
    sudo firewall-cmd --reload
    
    (对于生产容器主机来说,这绝对是一个糟糕的想法,但对于开发人员工作站来说,这是一个有益的风险与便利的权衡)

    使用systemd环境文件配置Docker

    现在我们已经运行了本地解析器,我们需要将Docker配置为默认使用它。Docker需要的是
    docker0
    接口的IP地址,而不是接口名称,因此我们使用
    ifconfig
    检索:

    $ ifconfig docker0 | grep inet
            inet 172.17.0.1  netmask 255.255.0.0  broadcast 0.0.0.0
    
    因此,对于我的系统,默认的
    docker0
    网桥上的主机接口可以作为
    172.17.0.1
    访问(在该命令中添加
    |cut-f 10-d'
    应该只过滤IP地址的输出)

    由于我假设一个基于systemd的Linux,带有系统提供的Docker包,我们将查询系统包的服务文件,以了解服务是如何启动的:

    $ cat /usr/lib/systemd/system/docker.service
    
    我们要查找的第一件事是用于启动守护进程的确切命令,该命令应如下所示:

    [Service]
    ExecStart=/usr/sbin/dnsmasq -k
    
    ExecStart=/usr/bin/docker daemon \
              $OPTIONS \
              $DOCKER_STORAGE_OPTIONS \
              $DOCKER_NETWORK_OPTIONS \
              $INSECURE_REGISTRY
    
    EnvironmentFile=-/etc/sysconfig/docker
    
    OPTIONS='--selinux-enabled --log-driver=journald'
    
    OPTIONS='--selinux-enabled --log-driver=journald --dns=172.17.0.1'
    
    我们要查找的第二部分是服务是否配置为使用环境文件,如下面的一行所示:

    [Service]
    ExecStart=/usr/sbin/dnsmasq -k
    
    ExecStart=/usr/bin/docker daemon \
              $OPTIONS \
              $DOCKER_STORAGE_OPTIONS \
              $DOCKER_NETWORK_OPTIONS \
              $INSECURE_REGISTRY
    
    EnvironmentFile=-/etc/sysconfig/docker
    
    OPTIONS='--selinux-enabled --log-driver=journald'
    
    OPTIONS='--selinux-enabled --log-driver=journald --dns=172.17.0.1'
    
    当使用环境文件时(如Fedora 23上的情况),更改Docker后台程序设置的方法是编辑该文件并更新相关环境变量:

    $ sudoedit /etc/sysconfig/docker
    
    Fedora 23上现有的
    选项
    条目如下所示:

    [Service]
    ExecStart=/usr/sbin/dnsmasq -k
    
    ExecStart=/usr/bin/docker daemon \
              $OPTIONS \
              $DOCKER_STORAGE_OPTIONS \
              $DOCKER_NETWORK_OPTIONS \
              $INSECURE_REGISTRY
    
    EnvironmentFile=-/etc/sysconfig/docker
    
    OPTIONS='--selinux-enabled --log-driver=journald'
    
    OPTIONS='--selinux-enabled --log-driver=journald --dns=172.17.0.1'
    
    要更改默认DNS解析设置,我们将其修改为如下所示:

    [Service]
    ExecStart=/usr/sbin/dnsmasq -k
    
    ExecStart=/usr/bin/docker daemon \
              $OPTIONS \
              $DOCKER_STORAGE_OPTIONS \
              $DOCKER_NETWORK_OPTIONS \
              $INSECURE_REGISTRY
    
    EnvironmentFile=-/etc/sysconfig/docker
    
    OPTIONS='--selinux-enabled --log-driver=journald'
    
    OPTIONS='--selinux-enabled --log-driver=journald --dns=172.17.0.1'
    
    然后重新启动Docker守护程序:

    $ sudo systemctl restart docker
    
    实施此更改后,Docker容器现在应该能够可靠地访问主机系统可以访问的任何系统(包括通过VPN隧道,这是我自己需要解决的原因)

    您可以在容器内运行
    curl
    ,检查名称解析是否正常工作:

    docker run -it centos curl google.com
    
    用给您带来问题的主机名替换
    google.com
    (因为只有在Docker容器中运行进程时遇到名称解析问题时,您才应该找到这个答案)

    使用系统内置文件配置Docker

    (注意:由于我的系统使用环境文件,我无法测试下面基于文件的插入式方法,但它应该可以工作-我将其包括在内,因为Docker文档似乎表明他们现在更喜欢使用systemd插入式文件而不是使用环境文件)

    如果系统服务文件未使用
    EnvironmentFile
    ,则可以使用插入式配置文件替换整个
    ExecStart
    条目:

    $ sudo mkdir -p /etc/systemd/system/docker.service.d
    $ sudoedit /etc/systemd/system/docker.service.d/daemon.conf
    
    然后,我们告诉Docker清除现有的ExecStart条目,并将其替换为具有其他设置的新条目:

    [Service]
    ExecStart=
    ExecStart=/usr/bin/docker daemon \
              $OPTIONS \
              --dns 172.17.0.1 \
              $DOCKER_STORAGE_OPTIONS \
              $DOCKER_NETWORK_OPTIONS \
              $INSECURE_REGISTRY
    
    然后,我们告诉systemd加载配置更改并重新启动Docker:

    $ sudo systemctl daemon-reload
    $ sudo systemctl restart docker
    
    参考文献: