/etc/hosts 是什么,什么时候它赢
一句话总结
/etc/hosts 是一份在 DNS 之前查询的本地名称表,而 dig 完全不会读取这个文件。理解这一句话,就能走出许多故障迷宫。
为什么需要理解名称解析顺序
有时会收到这样的报障:“DNS 看起来正常,但只有应用程序仍然连接旧服务器。”
检查后发现,dig api.internal 确实返回了新 IP,应用程序却始终连接旧 IP。如果这时开始排查 DNS 服务器,一整天都会白白浪费。
罪魁祸首通常是 /etc/hosts。有人上周调试时临时加入的那一行还留在那里。而且,这种事故不会表现出 DNS 错误的样子。 名称能够正常解析,症状却表现为 connection refused 或 timeout,所以没有人会怀疑名称解析。
它是如何工作的
先看文件格式。每一行由IP、规范名称、多个别名组成。
127.0.0.1 localhost
127.0.1.1 myserver
::1 localhost ip6-localhost ip6-loopback
10.0.1.50 api.internal.mycompany.com api-internal
192.168.1.100 db-master.mycompany.com
- 第二个字段是规范名称(canonical name),从第三个字段起都是别名。使用别名也能解析到相同地址。
- IPv4 与 IPv6 要分别填写。如果缺少
::1那一行,通过 IPv6 连接的应用程序可能无法解析名称。 - 不支持通配符,也不支持 CNAME。
*.labhub.local之类的写法只是普通字符串,不会匹配任何其他名称。原则上每个名称单独写一行。 - 同一个名称可以对应多个 IP。此时
getent ahosts会返回多行,应用程序从上到下依次尝试。
接下来是决定查询顺序的文件——/etc/nsswitch.conf。
grep '^hosts' /etc/nsswitch.conf
# hosts: files dns myhostname
# hosts: files mdns4_minimal [NOTFOUND=return] dns myhostname
# hosts: files resolve [!UNAVAIL=return] dns myhostname
各个令牌的含义如下。
| 令牌 | 查询什么 |
|---|---|
files |
/etc/hosts |
dns |
直接查询 /etc/resolv.conf 中的 nameserver |
resolve |
通过 D-Bus 查询 systemd-resolved |
myhostname |
本机主机名与本地接口地址 |
mdns4_minimal |
通过 mDNS 解析 .local 名称 |
方括号内的是行为规则。[NOTFOUND=return] 表示前一个模块回答“没有这个名称”后就立即停止,不会继续查询后面的 dns。
因此,把公司内部域命名为 .local 的组织很容易踩中这个陷阱。mdns4_minimal [NOTFOUND=return] 会截获 .local 名称,解析失败后就在原地结束,不再转到 DNS。然而,dig 不读取 nsswitch,所以仍会返回正常响应。于是出现了dig 正常,只有应用程序失败的现象。
由此可以得到本课程最重要的一句话。
dig 展示 DNS 服务器给出的答案,getent 展示应用程序会得到的答案。如果两者不同,这个差异本身就指出了原因所在的位置。
dig、nslookup、host 都是只使用 DNS 协议的工具。它们既不读取 /etc/hosts,也不读取 nsswitch 或 systemd-resolved 的按链路配置,只会从 resolv.conf 获取 nameserver 地址,再向 UDP 53 端口发送查询。能够复现应用程序解析路径的工具只有 getent hosts、getent ahosts 和 resolvectl query。
实际工作中会遇到的情况
使用 hosts 临时绕行后忘记删除。 故障处置时决定“先写进 hosts 绕过去”很常见,而且通常是正确选择。问题是三个月后那一行仍然存在。因此,许多团队规定,修改 hosts 时必须在注释中写明日期、原因与负责人。
容器镜像中固化了 hosts。 构建时加入的记录会残留在镜像中,导致所有环境都指向同一个 IP。预发布环境连接到生产数据库的事故,就可能这样发生。
下一个实验要做什么
亲自在 /etc/hosts 中添加记录,通过 getent 确认结果,再审计一个损坏的 hosts 文件夹具,找出有问题的行,最后提交满足规则的最终版本。