蚊章汇 - Zwischenzugs
type: channel
link: https://linuxcn.bwsl.wang/author/1050/
description: Zwischenzugs
items: Linux DNS 查询剖析（第四部分）
items: Linux DNS 查询剖析（第三部分）
items: Linux DNS 查询剖析（第二部分）
items: Linux DNS 查询剖析（第一部分）

Linux DNS 查询剖析（第四部分）
type: item
date: 2018-09-28T06:02:33+08:00
description: 在 Linux DNS 查询剖析（第一部分），Linux DNS 查询剖析（第二部分） 和 Linux DNS 查询剖析（第三部分） 中，我们已经介绍了以下内容：nsswitch/etc/hosts/etc/resolv.confping 与 host 查询方式的对比systemd 和对应的 networking 服务ifup 和 ifdowndhclientresolvconfNetworkManagerdnsmasq在第四部分中，我将介绍容器如何完成 DNS 查询。你想的没错，也不是那么简单。1) Docker 和 DNS在 Linux DNS 查询剖析（第三部分） 中，我们介绍了 dnsmasq，其工作方式如下：将 DNS 查询指向到 localhost 地址 127.0.0.1，同时启动一个进程监听 53 端口并处理查询请求。在按上述方式配置 DNS 的主机上，如果运行了一个 Docker 容器，容器内的 /etc/resolv.conf 文件会是怎样的呢？我们来动手试验一下吧。按照默认 Docker 创建流程，可以看到如下的默认输出：$ docker run ubuntu 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 # 127.0.0.53 is the systemd-resolved stub resolver. # run "systemd-resolve --status" to see details about the actual nameservers. search home nameserver 8.8.8.8 nameserver 8.8.4.4奇怪！地址 8.8.8.8 和 8.8.4.4 从何而来呢？当我思考容器内的 /etc/resolv.conf 配置时，我的第一反应是继承主机的 /etc/resolv.conf。但只要稍微进一步分析，就会发现这样并不总是有效的。如果在主机上配置了 dnsmasq，那么 /etc/resolv.conf 文件总会指向 127.0.0.1 这个 回环地址 loopback address 。如果这个地址被容器继承，容器会在其本身的 网络上下文 networking context 中使用；由于容器内并没有运行（在 127.0.0.1 地址的）DNS 服务器，因此 DNS 查询都会失败。“有了！”你可能有了新主意：将 主机的 的 IP 地址用作 DNS 服务器地址，其中这个 IP 地址可以从容器的 默认路由 default route 中获取：root@79a95170e679:/# ip route default via 172.17.0.1 dev eth0 172.17.0.0/16 dev eth0 proto kernel scope link src 172.17.0.2使用主机 IP 地址真的可行吗？从默认路由中，我们可以找到主机的 IP 地址 172.17.0.1，进而可以通过手动指定 DNS 服务器的方式进行测试（你也可以更新 /etc/resolv.conf 文件并使用 ping 进行测试；但我觉得这里很适合介绍新的 dig 工具及其 @ 参数，后者用于指定需要查询的 DNS 服务器地址）：root@79a95170e679:/# dig @172.17.0.1 google.com | grep -A1 ANSWER.SECTION ;; ANSWER SECTION: google.com. 112 IN A 172.217.23.14但是还有一个问题，这种方式仅适用于主机配置了 dnsmasq 的情况；如果主机没有配置 dnsmasq，主机上并不存在用于查询的 DNS 服务器。在这个问题上，Docker 的解决方案是忽略所有可能的复杂情况，即无论主机中使用什么 DNS 服务器，容器内都使用 Google 的 DNS 服务器 8.8.8.8 和 8.8.4.4 完成 DNS 查询。我的经历：在 2013 年，我遇到了使用 Docker 以来的第一个问题，与 Docker 的这种 DNS 解决方案密切相关。我们公司的网络屏蔽了 8.8.8.8 和 8.8.4.4，导致容器无法解析域名。这就是 Docker 容器的情况，但对于包括 Kubernetes 在内的容器 编排引擎 orchestrators ，情况又有些不同。2) Kubernetes 和…

Linux DNS 查询剖析（第三部分）
type: item
date: 2018-09-02T05:30:59+08:00
description: 在 Linux DNS 查询剖析（第一部分）中，我们介绍了：nsswitch/etc/hosts/etc/resolv.confping 与 host 查询方式的对比而在 Linux DNS 查询剖析（第二部分），我们介绍了：systemd 和对应的 networking 服务ifup 和 ifdowndhclientresolvconf剖析进展如下：（大致）准确的关系图很可惜，故事还没有结束，还有不少东西也会影响 DNS 查询。在第三部分中，我将介绍 NetworkManager 和 dnsmasq，简要说明它们如何影响 DNS 查询。1) NetworkManager在第二部分已经提到，我们现在介绍的内容已经偏离 POSIX 标准，涉及的 DNS 解析管理部分在各个发行版上形式并不统一。在我使用的发行版 （Ubuntu）中，有一个名为 NetworkManager 的 可用 available 服务，它通常作为一些其它软件包的依赖被安装。它实际上是 RedHat 在 2004 年开发的一个服务，用于帮助你管理网络接口。它与 DNS 查询有什么关系呢？让我们安装这个服务并找出答案：$ apt-get install -y network-manager对于 Ubuntu，在软件包安装后，你可以发现一个新的配置文件：$ cat /etc/NetworkManager/NetworkManager.conf [main] plugins=ifupdown,keyfile,ofono dns=dnsmasq [ifupdown] managed=false看到 dns=dnsmasq 了吧？这意味着 NetworkManager 将使用 dnsmasq 管理主机上的 DNS。2) dnsmasqdnsmasq 程序是我们很熟悉的程序：只是 /etc/resolv.conf 之上的又一个间接层。理论上，dnsmasq 有多种用途，但主要被用作 DNS 缓存服务器，缓存到其它 DNS 服务器的请求。dnsmasq 在本地所有网络接口上监听 53 端口（标准的 DNS 端口）。那么 dnsmasq 运行在哪里呢？NetworkManager 的运行情况如下：$ ps -ef | grep NetworkManager root 15048 1 0 16:39 ? 00:00:00 /usr/sbin/NetworkManager --no-daemon但并没有找到 dnsmasq 相关的进程：$ ps -ef | grep dnsmasq $令人迷惑的是，虽然 dnsmasq 被配置用于管理 DNS，但其实并没有安装在系统上！因而，你需要自己安装它。安装之前，让我们查看一下 /etc/resolv.conf 文件的内容：$ 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 10.0.2.2 search home可见，并没有被 NetworkManager 修改。如果安装 dnsmasq：$ apt-get install -y dnsmasq然后启动运行 dnsmasq：$ ps -ef | grep dnsmasq dnsmasq 15286 1 0 16:54 ? 00:00:00 /usr/sbin/dnsmasq -x /var/run/dnsmasq/dnsmasq.pid -u dnsmasq -r /var/run/dnsmasq/resolv.conf -7 /etc/dnsmasq.d,.dpkg-dist,.dpkg-old,.dpkg-new --local-service --trust-anchor=.,19036,8,2,49AAC11D7B6F6446702E54A1607371607A1A41855200FD2CE1CDDE32F24E8FB5然后，/etc/resolv.conf 文件内容又改变了！root@linuxdns1:~# 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 127.0.0…

Linux DNS 查询剖析（第二部分）
type: item
date: 2018-08-25T07:23:00+08:00
description: 在 Linux DNS 查询剖析 - 第一部分 中，我介绍了：nsswitch/etc/hosts/etc/resolv.confping 与 host 查询方式对比并且发现大多数程序选择要查询的 DNS 服务器时会参考 /etc/resolv.conf 配置文件。这种方式在 Linux 上比较普遍 1 。虽然我使用了特定的发行版 Ubuntu，但背后的原理与 Debian 甚至是那些基于 CentOS 的发行版有相通的地方；当然，与更低或更高的 Ubuntu 版本相比，差异还是存在的。也就是说，接下来，你主机上的行为很可能与我描述的不一致。在第二部分中，我将介绍 resolv.conf 的更新机制、systemctl restart networking 命令的运行机制 ，以及 dhclient 是如何参与其中。1) 手动更新 /etc/resolv.conf我们知道 /etc/resolv.conf （有极大的可能性）被用到，故你自然可以通过该文件增加一个 nameserver，那么主机也将会（与已有的 nameserver 一起）使用新加入的 nameserver 吧？你可以尝试如下：$ echo nameserver 10.10.10.10 >> /etc/resolv.conf看上去新的 nameserver 已经加入：# 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 10.0.2.3 search home nameserver 10.10.10.10但主机网络服务重启后问题出现了：$ systemctl restart networking $ 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 10.0.2.3 search home我们的 10.10.10.10 的 nameserver 不见了！在上一篇文章中我们忽略了这一点，本文进行补充说明。2) resolvconf你在 /etc/resolv.conf 文件中看到 generated by resolvconf 词组了吧？这就是我们的线索。如果深入研究 systemctl restart networking 命令，你会发现它做了很多事情，结束时调用了 /etc/network/if-up.d/000resolvconf 脚本。在该脚本中，可以发现一次对 resolvconf 命令的调用：/sbin/resolvconf -a "${IFACE}.${ADDRFAM}"稍微研究一下 man 手册，发现-a 参数允许我们：Add or overwrite the record IFACE.PROG then run the update scripts if updating is enabled.（增加或覆盖 IFACE.PROG 记录，如果开启更新选项，则运行更新脚本）故而也许我们可以直接调用该命令增加 namserver：echo 'nameserver 10.10.10.10' | /sbin/resolvconf -a enp0s8.inet测试表明确实可以！$ cat /etc/resolv.conf | grep nameserver nameserver 10.0.2.3 nameserver 10.10.10.10是否已经找到答案，这就是 /etc/resolv.conf 更新的逻辑？调用 resolvconf 将 nameserver 添加到某个地方的数据库，然后（“如果配置了更新”，先不管具体什么含义）更新 resolv.conf 文件。并非如此。$ systemctl restart networking root@linuxdns1:/etc# 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 OVERWRIT…

Linux DNS 查询剖析（第一部分）
type: item
date: 2018-08-23T07:05:00+08:00
description: 我经常与虚拟机集群打交道（文1、文2、文3、文4、文5、文6），因此最终花费了大量时间试图掌握 DNS 查询的工作原理。遇到问题时，我只是不求甚解的使用 StackOverflow 上的“解决方案”，而不知道它们为什么有时工作，有时不工作。最终我对此感到了厌倦，决定一并找出所有问题的原因。我没有在网上找到完整的指南，我问过一些同事，他们不知所以然（或许是问题太具体了）。既然如此，我开始自己写这样的手册。结果发现，“Linux 执行一次 DNS 查询”这句话的背后有相当多的工作。“究竟有多难呢？”本系列文章试图将 Linux 主机上程序获取（域名对应的） IP 地址的过程及期间涉及的组件进行分块剖析。如果不理解这些块的协同工作方式，调试解决 dnsmasq、vagrant landrush 和 resolvconf 等相关的问题会让人感到眼花缭乱。同时这也是一份有价值的说明，指出原本很简单的东西是如何随着时间的推移变得相当复杂。在弄清楚 DNS 查询的原理的过程中，我了解了大量不同的技术及其发展历程。我甚至编写了一些自动化脚本，可以让我在虚拟机中进行实验。欢迎读者参与贡献或勘误。请注意，本系列主题并不是“DNS 工作原理”，而是与查询 Linux 主机配置的真实 DNS 服务器（这里假设查询了一台 DNS 服务器，但后面你会看到有时并不需要）相关的内容，以及如何确定使用哪个查询结果，或者如何使用其它方式确定 IP 地址。1) 其实并没有名为“DNS 查询”的系统调用工作方式并非如此首先要了解的一点是，Linux 上并没有一个单独的方法可以完成 DNS 查询工作；没有一个有这样的明确接口的核心 系统调用 system call 。不过，有一个标准 C 库函数调用 getaddrinfo，不少程序使用了该调用；但不是所有程序或应用都使用该调用！让我们看一下两个简单的标准程序：ping 和 host：root@linuxdns1:~# ping -c1 bbc.co.uk | head -1 PING bbc.co.uk (151.101.192.81) 56(84) bytes of data.root@linuxdns1:~# host bbc.co.uk | head -1 bbc.co.uk has address 151.101.192.81对于同一个域名，两个程序得到的 IP 地址是相同的；那么它们是使用同样的方法得到结果的吧？事实并非如此。下面文件给出了我主机上 ping 对应的 DNS 相关的系统调用：root@linuxdns1:~# strace -e trace=open -f ping -c1 google.com open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3 open("/lib/x86_64-linux-gnu/libcap.so.2", O_RDONLY|O_CLOEXEC) = 3 open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3 open("/etc/resolv.conf", O_RDONLY|O_CLOEXEC) = 4 open("/etc/resolv.conf", O_RDONLY|O_CLOEXEC) = 4 open("/etc/nsswitch.conf", O_RDONLY|O_CLOEXEC) = 4 open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 4 open("/lib/x86_64-linux-gnu/libnss_files.so.2", O_RDONLY|O_CLOEXEC) = 4 open("/etc/host.conf", O_RDONLY|O_CLOEXEC) = 4 open("/etc/hosts", O_RDONLY|O_CLOEXEC) = 4 open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 4 open("/lib/x86_64-linux-gnu/libnss_dns.so.2", O_RDONLY|O_CLOEXEC) = 4 open("/lib/x86_64-linux-gnu/libresolv.so.2", O_RDONLY|O_CLOEXEC) = 4 PING google.com (216.58.204.46) 56(84) bytes of data. open("/etc/hosts", O_RDONLY|O_CLOEXEC) = 4 64 bytes from lhr25s12-in-f14.1e100.net …