本地主机 DNS 缓存
说 DNS 是我最喜欢的协议会不会很奇怪?因为 DNS *确实* 是我最喜欢的协议。UDP 数据包的简洁性,加上整个互联网都依赖的服务的强大功能,着实吸引着我。多年来,我一直对运行一个适度的内部网络 DNS 基础设施所需的资源之少印象深刻。
最近,随着我的一个环境开始扩展,我注意到,即使 DNS 服务器能够跟上负载,查询日志也充斥着对相同主机的重复查询,且间隔只有几秒。您知道,通常默认的 Linux 安装不会自带任何类型的本地 DNS 缓存。这意味着,每次需要将主机名解析为 IP 时,无论您为该记录设置了什么 TTL(生存时间),都会访问外部 DNS 服务器。
本文解释了设置轻量级本地 DNS 缓存是多么简单,它所做的只是将 DNS 请求转发到您的普通解析器,并遵守它收到的记录的 TTL。
有很多不同的方法来实现 DNS 缓存。过去,我曾使用过像 nscd 这样的系统,它会在 DNS 查询发送到 /etc/resolv.conf 中的名称服务器之前拦截它们,并查看缓存中是否已存在。虽然它有效,但我总是觉得 nscd 比 DNS 更难排除故障,尤其是在出现问题时。我真正想要的是一个本地 DNS 服务器,它能够遵守 TTL,但会将所有请求转发到我的真实名称服务器。这样,我既能获得本地缓存带来的速度和负载优势,又能使用标准的 DNS 工具来排除任何错误。
我找到的解决方案是 dnsmasq。通常我不是 dnsmasq 的大力倡导者,因为它经常被吹捧为易于配置的完整 DNS 和 DHCP 服务器解决方案,而我更喜欢使用独立的服务器来完成这些任务。Dnsmasq 通常会被配置为读取 /etc/resolv.conf 以获取要转发的上游名称服务器列表,并使用 /etc/hosts 进行区域配置。我想要一些完全不同的东西。我已经有了功能齐全的 DNS 服务器,而且如果我喜欢依赖 /etc/hosts 而不是 DNS 来进行主机名解析,我就会跳上我的 DeLorean 时光车回到 20 世纪 80 年代早期。相反,我的 dnsmasq 配置的大部分将集中在禁用许多默认功能上。
第一步是安装 dnsmasq。这款软件在大多数发行版中都广泛可用,因此只需使用您的标准包管理器来安装 dnsmasq 包即可。就我而言,我是在 Debian 上安装它,因此有一些 Debian 特有的问题需要处理,如果您使用不同的发行版,可能不必考虑这些问题。首先是 /etc/default/dnsmasq 中放置了一些相当重要的设置。该文件有完整的注释,因此我不会在此处粘贴它。相反,我列出我确保设置的两个变量
ENABLED=1
IGNORE_RESOLVCONF=yes
第一个变量确保服务启动,第二个变量将告诉 dnsmasq 忽略来自 resolvconf 服务(如果已安装)的任何输入,以确定要使用的名称服务器。无论如何,我都会手动指定这些服务器。
下一步是配置 dnsmasq 本身。默认配置文件可以在 /etc/dnsmasq.conf 中找到,您可以直接编辑它,但就我而言,Debian 会自动设置一个 /etc/dnsmasq.d 目录,并将从您在那里找到的任何文件中加载配置。作为配置管理系统的重度用户,我更喜欢 servicename.d 配置模型,因为它使得推送不同用途的不同配置变得容易。如果您的发行版没有为您设置此目录,您可以直接编辑 /etc/dnsmasq.conf,或者考虑在 dnsmasq.conf 中添加类似这样的选项
conf-dir=/etc/dnsmasq.d
就我而言,我创建了一个名为 /etc/dnsmasq.d/dnscache.conf 的新文件,其中包含以下设置
no-hosts
no-resolv
listen-address=127.0.0.1
bind-interfaces
server=/dev.example.com/10.0.0.5
server=/10.in-addr.arpa/10.0.0.5
server=/dev.example.com/10.0.0.6
server=/10.in-addr.arpa/10.0.0.6
server=/dev.example.com/10.0.0.7
server=/10.in-addr.arpa/10.0.0.7
让我们来了解一下每个设置。第一个设置 no-hosts 告诉 dnsmasq 忽略 /etc/hosts,不要将其用作 DNS 记录的来源。您希望 dnsmasq 仅使用您的上游名称服务器。no-resolv 设置告诉 dnsmasq 不要使用 /etc/resolv.conf 作为要使用的名称服务器列表。这很重要,因为稍后,您会将 dnsmasq 自己的 IP 添加到 /etc/resolv.conf 的顶部,您不希望它最终陷入某种循环。接下来的两个设置 listen-address 和 bind-interfaces 确保 dnsmasq 绑定并仅侦听本地主机接口 (127.0.0.1)。您不希望冒着外部人员将您的服务用作开放 DNS 中继的风险。
server 配置行是您添加要 dnsmasq 使用的上游名称服务器的地方。就我而言,我按照我的首选顺序添加了三个不同的上游名称服务器。此行的语法是 server=/domain_to_use/nameserver_ip
。因此在上面的示例中,它将使用这些名称服务器进行 dev.example.com 解析。就我而言,我还希望 dnsmasq 使用这些名称服务器进行 IP 到名称的解析(PTR 记录),因此由于所有内部 IP 都在 10.x.x.x 网络中,我添加了 10.in-addr.arpa 作为域。
完成此配置文件后,重启 dnsmasq 以使设置生效。然后您可以使用指向 localhost 的 dig
来测试 dnsmasq 是否工作
$ dig ns1.dev.example.com @localhost
; <<>> DiG 9.8.4-rpz2+rl005.12-P1 <<>> ns1.dev.example.com @localhost
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 4208
;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0
;; QUESTION SECTION:
;ns1.dev.example.com. IN A
;; ANSWER SECTION:
ns1.dev.example.com. 265 IN A 10.0.0.5
;; Query time: 0 msec
;; SERVER: 127.0.0.1#53(127.0.0.1)
;; WHEN: Thu Sep 18 00:59:18 2014
;; MSG SIZE rcvd: 56
在这里,我测试了 ns1.dev.example.com,看到它正确解析为 10.0.0.5。如果您检查 dig
输出,您可以在输出的底部附近看到 SERVER: 127.0.0.1#53(127.0.0.1)
确认我确实在与 127.0.0.1 通信以获得我的答案。如果您在此命令之后不久再次运行此命令,您应该注意到输出中的 TTL 设置(在上面的示例中,它设置为 265)将递减。Dnsmasq 正在缓存响应,一旦 TTL 变为 0,dnsmasq 将再次查询远程名称服务器。
在您验证 dnsmasq 功能正常后,最后一步是编辑 /etc/resolv.conf 并确保您将 nameserver 127.0.0.1 列在所有其他 nameserver 行之上。请注意,您可以保留所有现有的名称服务器。事实上,这提供了一种安全手段,以防 dnsmasq 发生崩溃。如果您使用 DHCP 获取 IP 或以其他方式从不同的文件(例如在安装 resolvconf 时的情况)设置这些值,您需要追踪要修改哪些文件;否则,下次您获得 DHCP 租约时,它将使用您的新设置覆盖此设置。
我在特定环境中的大约 100 台服务器上部署了这个简单的更改,令人惊讶的是,我的内部名称服务器上的 DNS 流量、负载和日志条目大幅下降。更重要的是,有了这个设置,即使下游 DNS 服务器真的出现问题,环境也更具容错性——现有的缓存条目仍然会为主机解析,直到 TTL 过期。因此,如果您发现您的内部名称服务器正受到大量流量的冲击,那么内部 DNS 缓存是您绝对应该考虑的事情。