我如何让Docker容器访问主机上的dnsmasq本地DNSparsing器?

有很多方法可以让Docker容器对DNS设置感到困惑(只需在SO或更广泛的互联网上search“Docker DNS”来查看我的意思),并且build议的常用解决方法之一是:

  1. 在主机系统上将dnsmasq设置为本地DNSparsing器
  2. 将其绑定到docker0networking接口
  3. configurationDocker以使用docker0 IP地址进行DNSparsing

然而,试图在许多现代Linux系统上天真地运用这个解决方法,会让你失望一些Linuxnetworking和stream程pipe理的复杂性,因为systemd向你保证dnsmasq没有运行,但是netstat告诉你它已经并且实际上试图启动dnsmasq失败,抱怨端口53已被使用。

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

这里的问题是许多现代Linux系统隐式地运行dnsmasq,所以你现在想要做的是专门为Docker设置第二个实例。 实际上有3个设置需要做到这一点:

  • --interface=docker0在默认的Dockernetworking接口上侦听
  • --except-interface=lo跳过隐藏的环回接口
  • --bind-interfaces可以closures一个dnsmasqfunction,在默认情况下它仍然监听所有的接口,即使只有其中的一个处理stream量

设置一个专用的dnsmasq实例

这些指令显示的是在已经定义了默认dnsmasq服务的系统上,使用systemd设置了一个专用的dnsmasq实例,而不是更改默认系统范围的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 

我们编辑该部分来定义我们的附加选项:

 [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等待,直到networking堆栈和主docker守护进程都可用于启动该服务,而[Install]指示将该服务添加到哪个系统状态目标。

然后,我们将我们的新服务configuration为在系统启动时启动,并明确启动以立即使用:

 $ 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> 

在第一行中,记下“启用”状态,而第二行logging“活动(运行)”状态。 如果服务没有正确启动,那么额外的诊断信息将有希望解释为什么(尽pipe可能不幸的是有时,因此这个职位)。

注意:此configuration可能无法在系统重新启动时启动dnsmasq-docker docker0没有定义docker0接口的错误。 在等待docker.service似乎是非常可靠的,以避免这个问题,如果docker容器的名称parsing系统重新启动后不工作,然后尝试运行:

 $ sudo systemctl start dnsmasq-docker 

configuration主机防火墙

为了能够使用本地Docker容器中的parsing器,我们还需要在运行在容器中的主机和系统之间放置networking防火墙:

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

(这对生产容器主机来说是一个绝对可怕的想法,但在开发人员工作站上可能是一种有益的风险与方便的权衡)

使用systemd环境文件configurationDocker

现在我们已经运行了本地parsing器,我们需要configurationDocker默认使用它。 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访问(对此命令的docker0 | cut -f 10 -d ' '应将输出过滤为IP地址)

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

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

我们要找的第一件事是用来启动守护进程的确切命令,它应该看起来像这样:

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

我们正在寻找的第二部分是服务是否configuration为使用环境文件,如下面其中一行所示:

 EnvironmentFile=-/etc/sysconfig/docker 

当一个环境文件被使用时(就像在Fedora 23上一样),那么改变Docker守护进程设置的方法就是编辑这个文件并更新相关的环境variables:

 $ sudoedit /etc/sysconfig/docker 

Fedora 23上现有的OPTIONS条目如下所示:

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

要更改默认的DNSparsing设置,我们修改它看起来像这样:

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

然后重新启动Docker守护进程:

 $ sudo systemctl restart docker 

通过实现这个改变,Docker容器现在应该可以可靠地访问主机系统可以访问的任何系统(包括通过VPN隧道,这是我自己需要弄清楚的原因)

您可以在容器中运行curl来检查名称parsing是否正常工作:

 docker run -it centos curl google.com 

google.comreplace为任何主机名给你的问题(如果你在Docker容器中运行一个进程时遇到了名称parsing问题,你应该只能find这个答案)

使用systemd插件文件configurationDocker

(注意:因为我的系统使用的是环境文件,所以我还没有能够testing下面的基于插入文件的方法,但它应该工作 – 我已经包含了它,因为Docker文档似乎表明他们现在更喜欢使用systemd的drop-in文件到使用环境文件)

如果系统服务文件使用EnvironmentFile ,那么整个ExecStart条目可以使用一个ExecStartconfiguration文件来replace:

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

然后我们告诉Docker清除现有的ExecStart条目,并用新的设置replace它。

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

然后,我们告诉systemd加载configuration更改并重新启动Docker:

 $ sudo systemctl daemon-reload $ sudo systemctl restart docker 

参考文献:

  • Docker systemdconfiguration参考: https : //docs.docker.com/engine/admin/systemd/
  • systemd服务文件参考: https : //www.freedesktop.org/software/systemd/man/systemd.exec.html
  • dnsmasq参考: http : //www.thekelleys.org.uk/dnsmasq/docs/dnsmasq-man.html
  • firewalld参考: https : //fedoraproject.org/wiki/FirewallD
  • 在主机上设置不带现有本地parsing器的dnsmasq: http : //docs.blowb.org/setup-host/dnsmasq.html