我如何让Docker容器访问主机上的dnsmasq本地DNSparsing器?
有很多方法可以让Docker容器对DNS设置感到困惑(只需在SO或更广泛的互联网上search“Docker DNS”来查看我的意思),并且build议的常用解决方法之一是:
- 在主机系统上将dnsmasq设置为本地DNSparsing器
- 将其绑定到
docker0
networking接口 - 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.com
replace为任何主机名给你的问题(如果你在Docker容器中运行一个进程时遇到了名称parsing问题,你应该只能find这个答案)
使用systemd插件文件configurationDocker
(注意:因为我的系统使用的是环境文件,所以我还没有能够testing下面的基于插入文件的方法,但它应该工作 – 我已经包含了它,因为Docker文档似乎表明他们现在更喜欢使用systemd的drop-in文件到使用环境文件)
如果系统服务文件不使用EnvironmentFile
,那么整个ExecStart
条目可以使用一个ExecStart
configuration文件来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