ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Unbound DNS服务器故障排查指南:从dig到DNSSEC的完整实战

Unbound DNS服务器故障排查指南:从dig到DNSSEC的完整实战 1. 故障定位先用 dig 把问题类型定下来unbound 这个递归 DNS 服务器平时确实省心跑起来几年不动它都没事。但一出问题表现往往让人挠头有时候是网页打不开有时候是域名时不时解析超时有时候干脆nslookup都提示 server failure。我见过不少人第一反应就是重装 unbound重装完了问题还在最后发现自己连故障类型都没搞清楚。接到这种问题我的习惯是先别碰配置先用dig或者drill把解析链路拆开看。手动指定 unbound 的地址去解析比如 unbound 跑在本机 53 端口命令是dig 127.0.0.1 www.example.com看返回结果里的status字段这个字段基本能给你指个方向NOERROR解析流程正常走通了问题在别处。NXDOMAIN域名本身不存在或者 unbound 的本地 zone 配置把域名劫走了。SERVFAIL典型的上游解析失败或者 DNSSEC 验证失败这几乎是 unbound 故障里最常见的 status。TIMEOUT请求发出去半天没响应一般是网络不通、上游服务器不可达或者 unbound 线程卡死。如果配合trace参数可以看到每一级 DNS 服务器的响应时间dig 127.0.0.1 www.example.com trace这个方法能把问题切成两大块是 unbound 自身往外递归出了问题还是 unbound 处理本地请求出了问题。根据我的经验前者占七成后者占三成。还有一种容易被忽略的情况dig 127.0.0.1一切正常但系统里其他程序解析就是不行。这种时候问题大概率不在 unbound而在系统层面的 resolver 配置比如/etc/resolv.conf没指向 unbound或者 nscd、systemd-resolved 这类服务在前面拦截了。这个坑后面细说。2. 配置文件里最容易埋雷的三处interface、access-control、local-zoneunbound 的配置主体是unbound.conf这个文件默认路径在/etc/unbound/unbound.conf。很多人上来就把网上抄来的配置整段粘贴进去然后重新加载发现问题依旧。其实 unbound 的配置非常有逻辑先搞清楚几个高频雷区能少走一半弯路。2.1 interface 写少了请求根本进不来interface指定 unbound 监听哪些地址。默认配置通常只监听127.0.0.1和::1也就是说只有本机能用。如果局域网内其他机器想把这台服务器当 DNS 用必须在配置里加入对应的网卡地址server: interface: 127.0.0.1 interface: 192.168.1.10 interface: ::1注意interface不是“写一个就行”而是多行并列每一行一个监听地址。改完配置要重启 unbound再用ss -lunp | grep 53确认端口真的监听在预期地址上。我遇到过有人改了配置但没重启ss一看还只有 127.0.0.1白白排查了一个小时。2.2 access-control 不放行配置再好也白搭就算监听地址对了access-control也会拦一道。unbound 默认只允许127.0.0.0/8和::1访问局域网其他客户端发过来的查询会被直接拒绝。日志里会看到类似access-control: 192.168.1.20 refused的记录。放行写法很简单server: access-control: 127.0.0.0/8 allow access-control: 192.168.1.0/24 allow access-control: ::1 allow有些人为了省事直接写access-control: 0.0.0.0/0 allow这等于把 unbound 裸奔在公网上配合开放的 53 端口很快会被利用做反射放大攻击。我在实际项目里见过一个真实案例某内部 DNS 服务器对外开放后又没做任何防护结果被外部流量打到 CPU 持续 100%最后只能封禁整个网段。安全底线不能省。2.3 local-zone 和 local-data 是内网解析的关键但也最容易写错local-zone和local-data用于配置本地域名解析比如内网某个服务想固定解析到一个地址server: local-zone: internal.example.com static local-data: git.internal.example.com. 3600 IN A 192.168.1.100这里有个典型的误区local-data里的域名必须带末尾的.否则 unbound 会认为它是一个相对域名自动补全后面的搜索域结果解析目标完全不是你想的那样。另外local-zone的类型也很关键static表示只返回明确配置的记录其他记录一律 NXDOMAINtransparent则允许未配置的子域名继续走递归。选错类型内网域名解析结果就会莫名其妙。我还遇到过一种更隐蔽的情况把内网域名配置在local-zone里但 unbound 仍然把请求发到上游递归解析。原因通常是 local-zone 的域名和查询的域名没匹配上带了www前缀的域名和配置的根域名是两码事。排查时可以用unbound-control list_local_data看看当前生效的本地记录都有哪些这个命令比反复读配置靠谱得多。配置改完一定要用unbound-checkconf做语法检查unbound-checkconf /etc/unbound/unbound.conf如果它提示什么错误照着改就完了没有输出错误再重启 unbound。这个习惯我强烈建议养成能避免改错配置导致 unbound 直接起不来。3. 上游解析失效为什么域名老是超时或 SERVFAILunbound 默认是递归模式它自己从根服务器开始一级一级往下查。这个模式的好处是不依赖某个具体上游坏处是对网络环境比较敏感。如果你所在网络禁止对外发 DNS 流量或者防火墙只允许访问特定 DNS 服务器递归模式就会频繁超时。3.1 递归模式依赖 root hints缺了就是 SERVFAIL递归查询得从根服务器开始所以 unbound 需要一份根服务器列表叫 root hints。正常安装的 unbound 会自带/var/lib/unbound/root.hints或者类似路径。如果这个文件缺失或为空unbound 就不知道该去找谁解析自然会失败。检查办法cat /var/lib/unbound/root.hints | grep \. NS unbound-anchor -a /var/lib/unbound/root.key前一条命令能看到根服务器记录后一条命令用于更新根信任锚和 root hints。我做故障排查时拿到一台新机器第一步就会看 root.hints 是否存在且非空。如果文件没了可以用unbound-anchor -a重新生成或者干脆临时切换到 forward 模式把业务跑通再说。3.2 改用 forward 模式适合内网环境但别把上游写错内网环境通常不允许直接访问外部根服务器所以更多时候会配置成转发模式把所有查询统一交给上游 DNS。常见配置forward-zone: name: . forward-addr: 223.5.5.5 forward-addr: 114.114.114.114这里name: .表示所有域名都走这个转发区。我见过有人把 forward-zone 写成name: example.com以为这样是“针对 example.com 做转发”结果其他域名全部解析失败。其实forward-zone里的 name 是“哪些域名交给这个上游”不是“只转发这个域名”这个逻辑刚接触时容易绕进去。还有一个常见问题是 forward-addr 写了 IP 但没写端口。默认端口是 53如果你的上游跑在非标准端口必须写全forward-addr: 10.0.0.15353。我记得曾经踩过这个坑上游公司内部 DNS 跑在 5353 端口我漏写了端口结果每次查询都返回 timeout查了半天才发现。测试上游是否可用可以先用 dig 直接打上游dig 223.5.5.5 www.example.com如果 dig 直接打上游没问题但 unbound 转发就超时重点检查 unbound 到上游的路径上有没有防火墙拦截 UDP/TCP 53 端口以及 unbound 的outgoing-interface配置是否正确。多网卡环境下outgoing-interface没指定unbound 可能用了错误的源地址导致上游不回包。3.3 timeout 和重试参数遇到慢速上游怎么办默认情况下 unbound 对上游的等待时间不算长如果上游 DNS 响应慢客户端就会觉得解析变卡。可以在server段里调整几个参数server: infra-host-ttl: 900 jostle-timeout: 100 timeout: 2 serve-expired: yes serve-expired-ttl: 86400timeout是单个查询的超时秒数jostle-timeout是等待时间超过一定阈值就主动选新上游重试。serve-expired是 unbound 比较实用的功能缓存里的记录过了 TTL 以后如果新的查询还没回来它会先把过期结果返回给客户端同时继续在后台刷新。对于网页浏览来说这个特性可以大大降低“解析等待”的体感值得开启。不过参数不是越大越好。timeout设成 10 秒客户端早就自己超时放弃了反而雪上加霜。我的经验是 2 到 3 秒是比较平衡的数值。4. DNSSEC 验证SERVFAIL 的头号元凶如果你开启了 DNSSEC 验证解析失败时常表现为SERVFAIL而且用dig直接打上游正常打 unbound 就失败。这里有一个我至少排查过十几次的经典场景。4.1 系统时间不对DNSSEC 必挂DNSSEC 验证依赖签名时间有效性。服务器系统时间假如快了几个小时或慢几个小时unbound 做验证时就可能认为签名过期或尚未生效直接返回 SERVFAIL。这个故障最难排查因为配置、网络、上游全都正常你就是找不到原因。检查时间同步date timedatectl status systemctl status chronyd # 或者 systemd-timesyncd我处理过一个客户案例他们的内网服务器关了 NTP 同步系统时间慢了整整两天。unbound 日志里全是 DNSSEC 验证失败所有人都以为是网络问题最后我顺手看了下时间才真相大白。所以遇到 SERVFAIL先看时间这是成本最低的检查项。4.2 信任锚过期或损坏unbound 验证 DNSSEC 需要信任锚trust anchor默认存在/var/lib/unbound/root.key。这个文件如果损坏、被误删或者长时间未更新验证就会失败。处理方式unbound-anchor -a /var/lib/unbound/root.key systemctl restart unbound有些发行版用/etc/unbound/root.key路径不同但方法一样。如果实在不想维护 DNSSEC也可以在配置里临时关掉验证server: module-config: iterator改完重启看问题是否消失。如果确实是 DNSSEC 导致的问题再决定是修时间、修 trust anchor还是彻底关闭 DNSSEC 验证。内网环境如果只是做转发其实 DNSSEC 的价值没那么大关掉也完全能接受。4.3 用 drill 和 dig 把验证链路看透排查 DNSSEC 问题我习惯用drill这个工具是 ldns 套件里的输出比 dig 更直白drill -D www.example.com-D参数会显示 DNSSEC 验证细节包括哪些记录有签名、签名状态如何。如果输出里有[BOGUS]标记那就是 DNSSEC 验证失败直接往信任锚和时间方向排查。如果是[INVALID]说明签名本身有问题可能上游返回了错误数据也可能是中间有人做了篡改。说实话DNSSEC 是 DNS 里最“劝退”的一块涉及的概念多排障链路长。但如果能把SERVFAIL BOGUS 时间不对这条线捋顺以后遇到任何 DNS 解析问题都会心里有底。5. 53 端口被抢systemd-resolved、dnsmasq、named 和谐共处的学问unbound 故障有一种特别尴尬的情况服务显示是 activerunning但dig 127.0.0.1就是没响应。这时候十有八九是 53 端口被别的进程抢占了。在 Linux 系统上常见的 DNS 服务不止 unbound 一个。systemd 自带的systemd-resolved默认就会监听 127.0.0.53 和 127.0.0.54 的 53 端口。dnsmasq、bindnamed也是老熟人。你启动 unbound 时如果端口被占用它可能启动失败但某些发行版的服务管理机制下进程状态显示会骗人。排查命令ss -lunp | grep :53 lsof -i :53 -P -n如果看到其他进程占用了端口处理方案看场景。最简单的是停掉占用进程sudo systemctl stop systemd-resolved sudo systemctl disable systemd-resolved但注意systemd-resolved 不只是监听端口它还会维护系统 DNS 配置。停掉它之后/etc/resolv.conf往往需要手动调整否则系统可能就没有 DNS 解析通路了。更稳妥的做法是保留 systemd-resolved把 unbound 换到 5353 端口然后让 systemd-resolved 转发给 127.0.0.1#5353。这样两个服务共存互不干扰。我的实际推荐是在纯内网服务器上直接停用 systemd-resolvedunbound 独占 53 端口在有桌面环境的开发机上unbound 走 5353系统层面再做转发。不要试图同时让两个服务都监听 53 端口Linux 的 SO_REUSEADDR 在这种场景下帮不了你反而制造混乱。还有一个容易被忽略的文件是/etc/nsswitch.conf。它控制系统的名字解析顺序里面hosts: files dns这一行如果配置有误系统可能根本不走resolv.conf里的 DNS甚至出现getent hosts能查但程序解析不了的情况。排查时可以试getent hosts www.example.com和dig的结果对比如果 getent 查不到但是 dig 能查到说明 nsswitch.conf 或者 NSS 模块有问题跟 unbound 本身无关。6. 缓存与本地 zone 的坑改了配置却不生效unbound 作为递归服务器自带缓存。这个机制平时能显著提升解析速度但也会带来一些坑尤其是在你调整了上游配置或者本地记录之后。6.1 缓存不刷新改完配置怎么看都不对最常见的是你改了/etc/hosts对应的域名解析或者改了local-data但客户端查询还是返回旧结果。原因就是 unbound 的缓存还没过期。清掉缓存unbound-control flush www.example.com unbound-control flush_zone example.com如果不确定到底哪些记录被缓存了可以unbound-control dump_cache | grep example.com这里有个小技巧unbound-control flush需要开启 control 通道。如果提示连接被拒绝检查配置里有没有remote-control: control-enable: yes control-interface: 127.0.0.1 control-port: 8953control 通道默认是关闭的很多发行版装完 unbound 顺手就把 control 关了结果后面想清缓存只能干瞪眼只能 restart。所以建议从一开始就把 remote-control 打开用回环地址监听安全风险可控。6.2 reload 和 restart 的区别很多教程让人改配置后执行systemctl restart unbound。restart 会完全重启进程所有缓存丢失短时间内查询压力会明显上升。如果你只是改了local-data或者forward-addr用unbound-control reload就够了它会重新读取配置文件同时保留大部分缓存。但有一点要记住reload并不会让所有配置都生效。尤其是和server段里模块加载相关的参数改完还是老实 restart 吧。拿不准时先用unbound-checkconf检查再unbound-control reload再用 dig 验证一次不行再 restart。这个顺序基本不会出问题。6.3 缓存参数调优给高 QPS 场景留后路在内部 DNS 压力比较大的环境缓存参数很值得调。默认的cache-max-ttl是 86400 秒也就是一天。如果你想减少上游请求可以调大但 TTL 设置过大域名 IP 变更后所有内网机器都会继续访问旧地址这个风险要自己权衡。还有一个参数prefetch当某些热门域名的缓存即将过期时unbound 会在后台主动刷新而不是等客户端触发。开启后明显改善高频查询的延迟server: prefetch: yes prefetch-key: yes配上serve-expired缓存过期后如果在后台刷新完成前有查询进来unbound 会返回过期记录保证客户端体验。这两个参数配合使用是降低解析延迟的黄金组合。7. 日志、调试与场景化排查速查表说实话unbound 的日志功能非常强大但默认配置下输出少得可怜。很多人遇到问题只会看systemctl status unbound那个输出对故障定位的帮助有限。想要高效排查得把日志级别提上来。7.1 打开日志与查询追踪在配置里临时调整server: verbosity: 2 log-queries: yes log-replies: yes logfile: /var/log/unbound.logverbosity从 0 到 5数字越大输出越多。日常排查 2 就够能看到查询进来了没、上游返回了什么。logfile指定日志文件如果不设置默认走 syslog。我习惯单独指定文件配合tail -f实时看tail -f /var/log/unbound.log这种模式下你能直接看到某条查询从进入 unbound 到返回响应的全过程。比如日志里出现module iterator returned ... SERVFAIL那就清楚是递归环节出的问题出现validation failure就是 DNSSEC 的问题。有了日志不要再对着配置文件猜来猜去了。7.2 用 unbound-control 实时看运行状态即使不查日志unbound-control也能提供非常多的情报unbound-control status unbound-control stats_noreset unbound-control list_forwards unbound-control list_local_datastatus显示 unbound 版本、运行时间、线程数stats_noreset显示累积的查询统计比如缓存命中率、上游连接数list_forwards显示当前生效的转发区配置。这些命令的输出比dig更能反映 unbound 的真实健康状况。尤其是缓存命中率查询总次数和缓存命中的比例如果命中率明显偏低比如低于 50%说明缓存配置有问题或者查询的域名种类太杂命中率自然上不去。如果命中率过高超过 90%也可能是cache-min-ttl设置太大域名更新感知太慢。7.3 常见故障与解决方案速查表我把这些年遇到的 unbound 故障整理成一张速查表排查时按图索骥效率会高很多症状可能原因排查方法与处理建议本地 dig 有响应其他机器解析失败interface 没监听客户端地址或 access-control 未放行ss -lunp | grep 53确认监听地址检查 access-control 配置解析结果全部 SERVFAILroot hints 缺失、DNSSEC 信任锚过期、系统时间偏差大、上游不可达依次检查 root.hints、root.key、date 时间再按流量方向排查上游特定域名解析超时上游 DNS 对特定域名响应慢或 local-zone 类型选择错误dig 上游域名单独测试确认 local-zone 是否为 transparent/static内网域名解析到了公网 IPlocal-data 写错、搜索域干扰、上游确实有这条记录检查 local-data 域名末尾点号用unbound-control list_local_data核对改了 local-data 不生效缓存未过期unbound-control flush 域名或直接 flush_zoneunbound 启动失败53 端口被占用ss -lunp | grep :53处理 systemd-resolved 或改监听端口局域网 DNS 查询非常慢上游超时参数过小或 outgoing-interface 选择错误调大 timeout、jostle-timeout检查多网卡路由开启 DNSSEC 后大量 SERVFAIL系统时间未同步或上游不支持 DNSSEC同步 NTP必要时关闭 DNSSEC 验证观察客户端偶尔解析失败systemd-resolved 与 unbound 抢 53 端口统一端口占用方案把 unbound 固定在 53 或者 5353日志看不出问题但现象持续verbosity 太低调不出关键信息临时调高 verbosity 为 2 并开启 log-queries / log-replies这张表不是万能药但能覆盖绝大多数常见的 unbound 故障。我在排障时总是先对照这张表走一遍基本能定位到 80% 的问题。8. 几个值得长期坚守的配置习惯最后分享几条我自己在项目里长期坚持的配置习惯算不上什么高深技巧但确实帮我少踩了很多坑。第一保持最小化配置。unbound.conf 只要写清楚interface、access-control、forward-zone和必要的local-data就够。不要网上看到什么参数就往里加很多高级参数之间的交互效果在默认配置下是经过调优的擅自改动可能引入新问题。我见过一个配置被前人堆了上百行里面有一半参数互相冲突最后清理到只剩二十行后服务反而稳定了。第二统一日志策略。生产环境的 unbound 建议固定一个单独的日志文件配合 logrotate 做轮转。这样出了问题直接翻一个文件就能定位不用在 syslog 的海洋里捞针。第三升级前先看 changelog。unbound 每个版本都可能改变默认行为比如某些参数默认值调整、某些特性默认开启或关闭。升级后解析行为变化不一定是你配置有问题很可能是默认值变了。遇到这种情况不要急着改配置先去翻 release notes能省下大把无效排查时间。第四定期做一次基准测试。用unbound-control stats_noreset记录基线数据比如平均查询耗时、缓存命中率、上游请求数。下次出问题时和基线对比能快速判断是突发流量导致还是配置变更引起的退化。这个习惯帮助我多次从“看起来没问题但体验就是差”的困境中脱身。unbound 这类基础组件平时它安静得像不存在一旦出问题就是整个网络都在受影响。但好在它的排障思路很清晰从查询入口到上游出口一层一层剥开每一步都能用 dig、unbound-control 和日志验证。把上面这些路径走一遍大部分问题会在十几分钟内现出原形。实际操作中对unbound-checkconf的使用率比对dig还高改完配置先过一遍语法永远不会错。希望这篇拆解能帮你在 DNS 解析出问题时少点抓瞎的时间多点定位问题的从容。
返回列表