症状是:宿主机有正常的网络访问权限,但是在容器中运行的程序却无法解析 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 配置的解决方案是什么?
原文由 Matthieu Moy 发布,翻译遵循 CC BY-SA 4.0 许可协议
一个干净的解决方案是配置 docker+dnsmasq,以便将来自 docker 容器的 DNS 请求转发到主机上运行的 dnsmasq 守护程序。
为此,您需要通过添加文件
/etc/NetworkManager/dnsmasq.d/docker-bridge.conf
来 配置 dnsmasq 以监听 docker 使用的网络接口:然后重新启动网络管理器以考虑配置文件:
完成此操作后,您可以将
172.17.0.1
(即 docker 中的主机 IP 地址)添加到 DNS 服务器列表中。这可以使用命令行来完成:…或通过docker的配置文件
/etc/docker/daemon.json
(如果不存在则创建):(如果 dnsmasq 失败,这将退回到 Google 的公共 DNS)
您需要重新启动 docker 以考虑配置文件:
然后你可以像往常一样使用docker: