ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

Linux系统DNS配置详解:从resolv.conf到systemd-resolved彻底解决

Linux系统DNS配置详解:从resolv.conf到systemd-resolved彻底解决 这周一个朋友找我排查Ubuntu服务器的问题现象很典型服务器能ping通网关但解析外网域名就是超时。他信誓旦旦说已经改了 /etc/resolv.conf把DNS写成了114.114.114.114结果一重启网络配置又变回了原样。我隔着屏幕都能猜到问题出在哪——Linux系统配置DNS这件事表面上是改一个文件实际牵扯到systemd-resolved、NetworkManager、netplan、DHCP客户端好几个组件后台任何一个在“抢”配置你的修改就会在重启后消失得干干净净。这篇文章不打算把DNS协议理论从头抄一遍而是按我日常运维的真实路径来写先讲清楚Linux解析链路里到底谁说了算再按主流发行版给出能落地的配置方法最后把“配置不生效”“重启被还原”这类高频问题逐个拆解。适合刚接触Linux的运维新人也适合被DNS折腾过、想彻底搞明白的进阶用户。1. 配置之前先看懂Linux的DNS解析链路1.1 一次域名解析请求是怎么走完的你在终端敲下 curl https://www.example.com计算机并不会直接拿着这个域名去连网络。它要先把域名“翻译”成IP地址整个流程大致是这样第一步应用调用系统解析器glibc的getaddrinfo或getnameinfo把域名交给操作系统。第二步系统解析器会读取 /etc/nsswitch.conf按照里面 hosts 这一行的顺序决定查找策略。绝大多数发行版默认是 files dns意思是先查 /etc/hosts 文件文件里找不到才走DNS。这个顺序很关键很多人给某些域名加了hosts记录后一直生效就是因为files排在dns前面。第三步确定要查DNS时解析器读取 /etc/resolv.conf拿到nameserver列表。第四步如果系统装了systemd-resolved你看到 /etc/resolv.conf 里往往是 nameserver 127.0.0.53这是本地缓存服务的地址真正的上游查询由它代理。第五步查询结果逐层返回到应用拿到IP之后才能建立TCP连接。这一段链路里任何一环出问题表现都是“域名解析不了”但根因可能完全不同。很多新手只盯着 /etc/resolv.conf 改改了半天没用就是因为没搞明白第四步你改的文件可能根本不是解析器真正查询的那个入口。1.2 三个核心组件先认识一下Linux下和DNS强相关的组件主要有三个先把它们的关系理清楚。/etc/resolv.conf 是传统意义上的DNS配置文件定义nameserver、search、options三个关键字段。但现在很多发行版把它做成了软链接或者由其他服务动态生成直接编辑往往会丢。systemd-resolved 是Ubuntu 18.04之后、Debian 12之后默认启用的DNS解析与缓存服务监听127.0.0.53负责把上游DNS、域名路由、本地主机名这些统一管理起来。它启动时会把一份stub解析配置写到 /run/systemd/resolve/ 目录下。NetworkManager 是桌面版Ubuntu和绝大多数RHEL系发行版的网络管理服务在连接激活时把DHCP下发的DNS或手动配置的DNS写入resolv.conf。这三个组件的关系可以这样理解NetworkManager负责“拿到网络参数”systemd-resolved负责“解析域名并缓存”/etc/resolv.conf只是它们对外展示结果的窗口。窗口内容由谁写取决于发行版的整合方式。在Ubuntu上/etc/resolv.conf 通常是个软链接指向 /run/systemd/resolve/stub-resolv.conf在RHEL系上它一般由NetworkManager的dns插件动态生成。你手写覆盖文件相当于硬改窗户上的贴纸系统一刷新就没了。1.3 为什么不能只改 /etc/resolv.conf直接编辑resolv.conf不是不行而是要看场景。如果是一台不重启、不重启网络、不跑NetworkManager的最小化容器直接改完全没问题。但只要系统里有NetworkManager或systemd-resolved在接管网络下一次DHCP续租、网络重启、网卡down/up都可能把文件内容覆盖掉。这就是大家搜索“linux修改dns后重启网络还原”时遇到的头号问题。另外很多新手忽略resolv.conf里几个字段的真正含义。nameserver最多写3个系统解析器按顺序尝试第一个不响应、超时之后才会切换下一个。search是给不带点的主机名自动补全搜索域的比如 search example.com你查询server时会先试server.example.com。options里常见的是timeout和attempts控制单次查询超时时间和总重试次数。大部分场景只需要精确配置nameserversearch和options保持默认即可乱加search反而会让每次查询都多出好几轮无意义的尝试。1.4 不同发行版的默认接管方式我自己维护的机器横跨多个发行版总结下来配置方法必须跟着“谁在接管网络”走而不是跟着直觉走发行版默认网络管理器DNS配置入口直接改resolv.conf是否有效Ubuntu 18.04systemd-resolved netplannetplan的yaml或resolvctl临时有效重启失效Debian 12systemd-resolved/etc/systemd/resolved.conf或resolvctl临时有效重启失效Rocky/AlmaLinux 8/9NetworkManagernmcli或nmtui临时有效重启失效CentOS 7NetworkManagernmcli或ifcfg文件临时有效重启失效精简容器/无systemd环境无/etc/resolv.conf永久有效推荐直接改这张表是我踩了无数坑之后整理出来的。说白了只要发行版默认带着systemd-resolved或NetworkManager你就老老实实走官方入口别跟resolv.conf较劲。2. 按发行版选对配置方式主流方案对比2.1 Ubuntu/Debiansystemd-resolved 的正确用法“ubuntu 22.04 系统怎么修改 dns”应该是运维群里被问得最多的问题之一。答案要分两层临时生效和永久生效。临时生效直接改 /etc/resolv.conf 确实可以但前面说过改之前先确认它是不是软链接。ls -l 一看如果指向 /run/systemd/resolve/stub-resolv.conf说明系统正在用systemd-resolved你改了也白改。正确的临时做法是用 resolvectl 命令sudo resolvectl dns ens18 223.5.5.5这条命令直接修改ens18网卡的上游DNS立即生效重启失效非常适合拿来验证“换这个DNS到底能不能解决问题”。永久生效桌面版Ubuntu推荐在系统设置的网络界面里改服务器版则走netplan。netplan是Ubuntu的默认网络配置工具配置文件在 /etc/netplan/ 下常见的名字是 01-network-manager-all.yaml 或 00-installer-config.yaml。具体写法我会在下一章给出完整例子。2.2 RHEL/CentOS/RockyNetworkManager 是真正的入口在Rocky、AlmaLinux、CentOS Stream这些RHEL系发行版上配置DNS的第一选择是nmcli而不是直接改resolv.conf。因为NetworkManager接管了所有连接配置文件DNS必须写进连接配置里才会稳定。老一点的CentOS 6/7时代确实可以直接改 /etc/resolv.conf 和 /etc/sysconfig/network-scripts/ifcfg-eth0由network服务负责应用。但到了RHEL 9/Rocky Linux 9系统默认用NetworkManager管理连接你再手工改ifcfg文件很容易和nmcli看到的状态不一致。生产中我强烈建议统一用nmcli管理命令可审计、可回滚出问题也好排查。2.3 老系统与精简容器直接改 resolv.conf 的适配场景也不是所有环境都那么复杂。如果你维护的是一台老旧的CentOS 6机器或者一个不跑systemd的精简容器直接编辑 /etc/resolv.conf 反而是最可靠的方式cat /etc/resolv.conf EOF nameserver 223.5.5.5 nameserver 119.29.29.29 options timeout:2 attempts:2 EOF这种环境下没有NetworkManager没有netplan也没有systemd-resolved谁来读这个文件glibc解析器直接读。改完就生效重启也不变。为了防止某些DHCP客户端或者脚本不小心覆盖这个文件我还会顺手加个不可变属性chattr i /etc/resolv.conf这个操作会把文件锁住连root都无法修改。要解除时执行 chattr -i /etc/resolv.conf 即可。但要注意这招只能在确认没有NetworkManager管理的环境里用否则系统更新resolv.conf会失败出现“网络正常但DNS一直不变”的怪现象。2.4 公网DNS服务器怎么选几个常用地址的实测感受配置DNS最头疼的其实是选服务器。我常年维护国内外多台机器常用的公共DNS有这些223.5.5.5阿里DNS国内访问速度快解析结果干净IPv6对应用是2400:3200::1是我给国内服务器的首选。119.29.29.29DNSPod和1.2.4.8也是老牌公共DNS稳定可靠适合做备选。114.114.114.114在老一辈运维里用得多全国节点多。1.1.1.1Cloudflare和8.8.8.8Google是全球通用DNS海外服务器常用但在国内部分网络环境下延迟偏高。我自己的原则是国内服务器优先 223.5.5.5 加 119.29.29.29 组合一主一备跨境或海外业务再考虑把1.1.1.1或8.8.8.8加进去做第三备选。DNS不是越多越好三个以内足够多了反而会因为顺序查询的超时机制拖慢整体解析速度。3. 实操全流程从修改到生效的完整步骤3.1 Ubuntu 22.04 永久修改DNSnetplan 写法假设服务器是Ubuntu 22.04网卡名叫ens18想把DNS改成223.5.5.5和119.29.29.29完整步骤如下。先看现有配置ls /etc/netplan/创建备份sudo cp /etc/netplan/00-installer-config.yaml /etc/netplan/00-installer-config.yaml.bak编辑YAML文件network: version: 2 ethernets: ens18: dhcp4: true nameservers: addresses: - 223.5.5.5 - 119.29.29.29保存后应用sudo netplan apply这里要特别提醒一个坑如果你用的是 dhcp4: trueDHCP服务器下发的DNS可能会覆盖你写进YAML的nameserver。要让手工配置的DNS优先最稳妥的办法是给网卡配置静态IP或者使用dhcp4-overrides忽略DHCP的DNSnetwork: version: 2 ethernets: ens18: dhcp4: true dhcp4-overrides: use-dns: false nameservers: addresses: - 223.5.5.5 - 119.29.29.29use-dns: false 的意思是“忽略DHCP下发的DNS”这样你手写的nameserver才能真正生效。这个参数是我早期踩坑后总结出来的很多人改了YAML却发现DNS还是DHCP给的十有八九是少了这一行。应用之后用下面的命令确认resolvectl status如果看到Current DNS Server是223.5.5.5说明生效了。3.2 Rocky Linux 9 用 nmcli 配置DNSRocky Linux 9和RHEL 9上先用 nmcli connection show 找到你的连接名通常是ens18或者System ens18nmcli connection show然后设置DNS一共三条命令缺一不可nmcli connection modify ens18 ipv4.dns 223.5.5.5 119.29.29.29 nmcli connection modify ens18 ipv4.ignore-auto-dns yes nmcli connection up ens18第一行设置DNS列表注意两个地址用空格分隔。第二行是忽略DHCP自动下发的DNS忘了它就会出现“明明设了DNS网络一重启又变回DHCP给的”现象。第三行重新激活连接让修改生效。如果想同时配置IPv6的DNS再加一句nmcli connection modify ens18 ipv6.dns 2400:3200::1改完后同样有验证命令nmcli connection show ens18 | grep -i dns顺便提一句如果你不太习惯命令行可以用 nmtui 打开文本交互界面在连接编辑里找到IPv4配置页把DNS手动填进去效果和nmcli完全一样。3.3 临时修改与永久修改的边界排查问题时我经常先用临时修改做快速验证echo nameserver 223.5.5.5 /etc/resolv.conf这在最小化系统或容器里很好用但前面反复强调过只要系统里存在NetworkManager或systemd-resolved这种修改随时可能被覆盖。所以我的习惯是临时验证用直接覆盖文件正式变更永远走发行版的官方配置入口。两者边界要心里有数千万别拿临时手段做生产变更。顺带说一个很多人不知道的命令systemd-resolved也支持临时切换DNS效果和直接改resolv.conf类似但更规范sudo resolvectl dns ens18 223.5.5.5这个命令改的是运行时状态不需要动文件适合快速对比测试。3.4 容器场景Docker 的 DNS 配置容器里的DNS问题也很常见。Docker容器默认会继承宿主机的DNS配置但有些场景需要单独指定。最常用的是docker run时加参数docker run --dns 223.5.5.5 --dns 119.29.29.29 nginx如果想让所有容器都走同一个DNS就写到Docker daemon配置里sudo mkdir -p /etc/docker sudo tee /etc/docker/daemon.json EOF { dns: [223.5.5.5, 119.29.29.29] } EOF sudo systemctl restart docker改完之后起一个测试容器进去cat /etc/resolv.conf验证。有人问Docker Desktop在Linux下的DNS“国内源设置”怎么弄本质也是改daemon.json的dns字段原理和上面完全一样只不过GUI界面里把它包装成了设置项。还有一个容易忽略的点Docker默认使用内置的127.0.0.11作为容器的DNS解析器它会把请求转发到宿主机配置的DNS。如果宿主机DNS配置有问题容器里就会出现“能ping通IP但解析不了域名”的诡异现象。遇到这种问题先查宿主机DNS别一上来就怀疑容器网络。4. 配置常见问题与排查实录4.1 重启网络后DNS被“还原”了这个问题在搜索引擎里是经典中的经典。现象改了 /etc/resolv.conf执行 systemctl restart network 或重启系统后文件内容又变回旧的。原因基本就两个。一是resolv.conf是软链接指向/run下的动态文件重启后由systemd或NetworkManager重新生成二是NetworkManager的连接配置里没改DNSDHCP一续租就覆盖。正确的排查顺序是这样ls -l /etc/resolv.conf cat /etc/nsswitch.conf | grep hosts resolvectl status nmcli connection show先看软链接指向哪个文件再看解析器优先用files还是dns接着看当前上游DNS是谁最后看NetworkManager连接里配了什么。四步走完基本能定位是哪个组件在覆盖。定位之后回到第3章的永久配置方法去改而不是继续硬刚resolv.conf。我自己处理过不少类似的工单90%都是因为当初没走官方入口。4.2 systemd-resolved 与 127.0.0.53 的恩怨Ubuntu系统上 /etc/resolv.conf 里写着 nameserver 127.0.0.53很多新手以为这是“本机DNS服务”其实它是systemd-resolved的本地缓存监听地址。127.0.0.53不是真正的解析服务器它负责接收本机应用的解析请求再转发给上游DNS。问题通常出现在这里你改了上游DNS但解析结果还是旧的很可能是缓存作祟。执行一下sudo resolvectl flush-caches清掉缓存再测试基本就好了。还有一种情况部分程序绕过系统解析器自己直接解析域名。比如某些Java应用、Go程序默认写法它们可能跳过127.0.0.53直接读 /etc/resolv.conf 里的nameserver。这时候你在resolv.conf里写什么它们压根不看需要在应用层面配置DNS或者把 /etc/resolv.conf 直接改成指向真实的上游DNS。判断方法是先用strace追一下strace -f -e openat -e tracenetwork java -version 21 | grep -i resolv看到程序读取了哪些配置文件就能确定它走的是哪条解析路径。4.3 dig 能解析但应用连不上这是最迷惑人的故障终端里 dig www.example.com 返回正确IP但浏览器、curl、数据库连接全部失败。原因要分几层看。dig默认直接用 /etc/resolv.conf 里的服务器而应用可能走代理、走hosts、走IPv6优先等策略。所以先确认环境cat /etc/hosts env | grep -i proxy curl -v https://www.example.com如果curl输出里有“正在通过代理”之类的信息说明代理配置抢了DNS的活检查 http_proxy、https_proxy 这组环境变量。还有一种常见情况是IPv6优先问题本机没有IPv6连通性但DNS返回了AAAA记录系统优先尝试IPv6一直超时才回退IPv4表现就是解析慢、连接超时。处理办法是调整 /etc/gai.conf把precedence相关的注释打开或者在resolv.conf里加一行options single-request-reopen这个选项能解决双栈环境下“第一个DNS请求超时导致整体变慢”的问题我在混合IPv4/IPv6的网络里实测很有效。4.4 常见问题速查表故障现象可能原因处理手段重启后DNS还原resolv.conf被动态覆盖改用netplan/nmcli永久配置改了DNS不生效没关闭DHCP下发的DNS加dhcp4-overrides或ignore-auto-dns能ping通IP但不能解析域名DNS服务器不可达或解析器顺序错检查resolv.conf和nsswitch.conf解析时快时慢第一个nameserver超时调整options timeout/attempts换DNS后结果没变systemd-resolved缓存旧结果resolvectl flush-cachesdig正常但应用失败代理或hosts干扰检查proxy环境变量和/etc/hosts容器内解析异常宿主机DNS配置问题先排查宿主机再重置容器DNS这张表我贴在了自己的运维手册里每次排障先对着表走一遍能省不少时间。5. 配置完成后如何验证与测试5.1 用 dig 验证解析链路dig是排查DNS最趁手的工具没有之一。基础用法dig 223.5.5.5 www.example.com指定用哪个DNS服务器查询立刻能看出问题出在哪一段。如果不带参数dig会使用 /etc/resolv.conf 里第一个nameserver这就能验证“当前系统配置的DNS到底能不能用”。想看解析耗时加time和triesdig time2 tries1 www.example.com这个命令只等待2秒、只尝试1次适合快速判断上游DNS是否可靠。如果频繁出现超时就要考虑换服务器了。想确认系统实际把请求发给了谁配合tcpdump抓UDP 53端口流量效果最好sudo tcpdump -i any udp port 53 -n一边抓包一边执行dig能看到请求真的发往了哪个IP。这一步做一次你对“系统到底用了哪个DNS”就有底了比看任何配置文件都直观。5.2 用 resolvectl 检查 systemd-resolved 状态在Ubuntu/Debian系上resolvectl 是查看解析状态的标配resolvectl status输出里重点看Global段的Current DNS Server和DNS Servers以及每个网卡下的DNS字段。如果Current DNS Server和预期不一致说明配置没生效。如果想单独看某个网卡resolvectl dns ens18这个命令只会显示ens18当前使用的DNS地址简单直接。另外resolvectl status还能显示哪些域名走了特殊路由、当前有没有开启DNSSEC验证这些信息在高级排障时很有用。5.3 多DNS切换时的踩坑建议我见过很多同事在 /etc/resolv.conf 里堆了五六个nameserver以为越多越安全结果反而拖慢了整体解析。因为glibc解析器是顺序查询的第一个服务器超时后才轮到第二个一个不响应的nameserver能让所有查询都卡上十几秒。我的建议是压到三个以内并且把质量高的放前面。切换DNS之后不要急着做业务压测先跑一轮批量解析确认无超时、无解析失败再说。批量检查可以这样写while read domain; do dig short $domain /dev/null 21 echo $domain OK || echo $domain FAIL done domain_list.txt拿生产域名列表跑一遍比单独测一个域名靠谱得多。我每次变更DNS之后都会这么跑一次十分钟就能完成几百个域名的健康检查大大降低线上翻车的概率。还有一个小细节如果一台机器同时配置了多个DNS要注意不同DNS对同一个域名可能返回不同结果这是由DNS服务器的缓存状态和策略差异决定的。遇到解析结果忽A忽B的情况优先考虑是不是多DNS之间结果不一致不要一上来就怀疑程序有bug。我在运维岗这些年DNS相关的故障占了网络类问题的三成以上而且大多是“改了不生效”“重启被还原”这类低门槛但高迷惑性的坑。核心原因就一句话Linux上解析DNS不是单一配置文件的事而是一套由systemd-resolved、NetworkManager、netplan、DHCP客户端共同参与的分层体系。你只有先搞清楚当前发行版是哪套体系在接管再按它的规则去配置才能一劳永逸。最后再分享一个小技巧每次改完DNS我都会把 /etc/resolv.conf、相关YAML或nmcli命令的输出存一份到变更记录里标注时间、变更人和验证结果。等哪天线上再出“稀奇古怪”的解析问题翻记录就能快速排除“是谁改了什么”这个最头疼的变量。配置DNS本身不难难的是让它稳定地留在你设定的状态上——希望这篇内容能帮你少踩几个坑。
返回列表