症状是:主机可以正常访问网络,但在容器内运行的程序无法解析DNS名称(可能会在更深入的调查之前出现“无法访问网络”的错误信息)。
$ sudo docker run -ti mmoy/ubuntu-netutils /bin/bash
root@082bd4ead733:/# ping www.example.com
... nothing happens (timeout) ... ^C
root@082bd4ead733:/# host www.example.com
... nothing happens (timeout) ... ^C
这个 Docker 镜像 mmoy/ubuntu-netutils 是一个基于 Ubuntu 的简单镜像,包含了 ping
和 host
工具。由于网络出现故障,我们无法通过 apt install
安装这些工具,因此这个镜像非常方便。
问题在于 Docker 自动将 Google 的公共 DNS 配置为容器内的 DNS 服务器:
root@082bd4ead733:/# cat /etc/resolv.conf
# Dynamic resolv.conf(5) file for glibc resolver(3) generated by resolvconf(8)
# DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN
nameserver 8.8.8.8
nameserver 8.8.4.4
这在许多配置中都有效,但显然当主机运行在某些防火墙规则过滤Google公共DNS的网络上时,它就不起作用了。
出现这种情况的原因是:
- Docker首先尝试在主机和容器内配置相同的DNS服务器。
- 主机运行dnsmasq,一个DNS缓存服务。dnsmasq充当DNS请求的代理,因此主机中
/etc/resolve.conf
中的DNS服务器看起来是nameserver 127.0.1.1
,即localhost。 - 主机的dnsmasq只监听来自localhost的请求,并阻止来自docker容器的请求。
- 由于在docker中使用
127.0.1.1
不起作用,因此docker退回到Google的公共DNS,这也不起作用。
Docker容器中DNS无法正常工作可能有多种原因。这个问题(及答案)涉及以下情况:
- 使用了dnsmasq。要检查是否存在这种情况:
- 在主机上运行
ps -e | grep dnsmasq
。如果输出为空,则未运行dnsmasq。 - 检查主机的resolv.conf,它可能包含类似
nameserver 127.0.1.1
的条目。如果它包含nameserver 127.0.0.53
,则您可能正在运行systemd-resolved
而不是dnsmasq。如果是这样,您将无法使用转发DNS请求到dnsmasq的解决方案(使用listen-address=172.17.0.1
的解决方案)。早于247版本的systemd-resolved已经硬编码了仅在“lo”接口上监听的事实,因此在这些版本中没有简单的方法来适应这个解决方案。下面的其他答案可以与systemd-resolved一起使用。
- 在主机上运行
- Google的公共DNS被过滤。运行
host www.example.com 8.8.8.8
。如果失败或超时,则处于该情况。
在这种配置下获得适当的DNS配置的解决方案是什么?
172.17.0.1
,即从docker内部访问主机的IP地址”。问题在于,默认情况下,172.17.0.1
不会响应DNS请求(因为dnsmasq只监听本地接口),所以仅配置172.17.0.1
是不够的,需要在dnsmasq方面进行一些配置。我猜测docker的开发人员和打包人员决定安装docker时不修改dnsmasq的配置。 - Matthieu Moydnsmasq.d/
下),那么这就不是一个干净的解决方案。 - Szczepan Hołyszewski.conf
结尾的任何其他名称。但无论如何,您都必须告诉dnsmasq在172.17.0.1上正确监听,因为来自docker的请求将被定向到此处。在这里,Docker不仅仅是在机器上运行的守护程序,它看起来像一个具有单独IP地址的独立机器,默认情况下,dnsmasq将拒绝其请求。 - Matthieu Moy