- 在主机系统上设置dnsmasq作为本地DNS解析器
- 将其绑定到
docker0
网络接口上 - 配置Docker使用
docker0
IP地址进行DNS解析
dnsmasq
未运行,但netstat
告诉您它正在运行,并且实际尝试启动dnsmasq
时失败并抱怨端口53已经被占用。那么,如何可靠地让容器访问在主机上运行的本地解析器,即使系统默认已经有一个正在运行呢?
docker0
网络接口上docker0
IP地址进行DNS解析dnsmasq
未运行,但netstat
告诉您它正在运行,并且实际尝试启动dnsmasq
时失败并抱怨端口53已经被占用。--interface=docker0
监听默认的Docker网络接口--except-interface=lo
跳过环回接口的隐式添加--bind-interfaces
关闭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
[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>
$ sudo systemctl start dnsmasq-docker
配置主机防火墙
为了能够在本地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
ExecStart=/usr/bin/docker daemon \
$OPTIONS \
$DOCKER_STORAGE_OPTIONS \
$DOCKER_NETWORK_OPTIONS \
$INSECURE_REGISTRY
EnvironmentFile=-/etc/sysconfig/docker
当环境文件被使用(如在Fedora 23上),则更改Docker守护程序设置的方法是编辑该文件并更新相关的环境变量:
$ sudoedit /etc/sysconfig/docker
OPTIONS
入口如下所示: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 容器中运行进程时遇到名称解析问题,您应该只会找到此答案)
使用 systemd drop-in 文件配置 Docker
(注意:由于我的系统使用环境文件,我无法测试下面基于 drop-in 文件的方法,但它应该可以工作 - 我已经包含了它,因为 Docker 文档似乎表明他们现在更喜欢使用 systemd drop-in 文件而不是环境文件)
如果系统服务文件不使用EnvironmentFile
,则可以通过使用 drop-in 配置文件来替换整个ExecStart
条目:
$ sudo mkdir -p /etc/systemd/system/docker.service.d
$ sudoedit /etc/systemd/system/docker.service.d/daemon.conf
[Service]
ExecStart=
ExecStart=/usr/bin/docker daemon \
$OPTIONS \
--dns 172.17.0.1 \
$DOCKER_STORAGE_OPTIONS \
$DOCKER_NETWORK_OPTIONS \
$INSECURE_REGISTRY
$ sudo systemctl daemon-reload
$ sudo systemctl restart docker
参考资料:
docker0
接口已经可用后可靠地启动dnsmasq实例,因此我已相应地更新了建议的服务配置。 - ncoghlanifconfig
已经被弃用,如果有人遇到这个问题并想知道使用ip
的相应命令是什么,那么这里就是:ip -c addr show docker0
(-c用于着色)。 - swenzeldnsmasq
)中使用它们。在这种情况下,容器的/etc/resolv.conf
将具有名称服务器127.0.0.11
(即Docker的嵌入式DNS服务器),它可以正确地将DNS请求转发到主机的环回地址。$ 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
docker-compose
,它会自动为您的服务设置一个自定义网络(文件格式为v2+)。需要注意的是,尽管docker-compose
在用户定义的网络中运行容器,但它仍然在默认的bridge
网络中构建容器。要为构建指定自定义网络,可以在构建配置中指定network
参数(需要文件格式v3.4+)。