4、DNS解析的“根”治:修改resolv.conf的正确姿势,防止被覆盖

DNS解析出问题,网络基本就废了一半。我见过太多工程师在resolv.conf上栽跟头——明明改好了,重启网络服务又变回原样。说白了,这个文件在Linux系统里就是个“烫手山芋”,谁都想管,谁都能改。

4.1 为什么resolv.conf总被覆盖?

先搞清楚敌人是谁。resolv.conf被覆盖,通常有三大“元凶”:

  • NetworkManager:桌面环境下的“管家”,它觉得自己有权管理DNS
  • systemd-resolved:systemd家族的DNS组件,默认会接管解析
  • DHCP客户端:dhclient、udhcpc这些家伙,拿到租约就写resolv.conf

我在项目中遇到过最离谱的一次:客户在嵌入式设备上改了resolv.conf,结果每次插拔网线,DHCP就把配置冲掉了。排查了一下午,才发现是udhcpc的脚本在作祟。

4.2 根治方案一:直接锁死文件

最简单粗暴的方法——用chattr加不可变属性。这招在嵌入式rootfs里特别好使。

# 先备份原配置
cp /etc/resolv.conf /etc/resolv.conf.bak

# 写入你想要的DNS
echo "nameserver 8.8.8.8" > /etc/resolv.conf
echo "nameserver 114.114.114.114" >> /etc/resolv.conf

# 加锁!谁都别想改
chattr +i /etc/resolv.conf

# 验证一下
lsattr /etc/resolv.conf
# 输出应该包含 ----i-----------
⚠️ 注意:加锁后,你自己也改不了。想修改得先解锁:chattr -i /etc/resolv.conf。这在调试阶段容易忘,我曾经就因为这个被坑过。

4.3 根治方案二:让resolv.conf变成符号链接

嗯,这个方法更优雅。把resolv.conf指向一个你自己控制的文件,谁也覆盖不了。

# 创建你自己的DNS配置文件
mkdir -p /etc/systemd
echo "nameserver 8.8.8.8" > /etc/systemd/resolved.conf.d/my-dns.conf
echo "nameserver 114.114.114.114" >> /etc/systemd/resolved.conf.d/my-dns.conf

# 删除原文件,建立符号链接
rm -f /etc/resolv.conf
ln -s /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf

# 重启systemd-resolved
systemctl restart systemd-resolved

你想想看,符号链接的好处是什么?就算DHCP脚本想写resolv.conf,它也只能写到一个“不存在”的文件上——因为真正的文件已经被重定向了。

4.4 根治方案三:修改DHCP客户端脚本

如果你用的是udhcpc(嵌入式Linux标配),可以修改它的钩子脚本。

# 找到udhcpc的脚本目录
ls /usr/share/udhcpc/default.script

# 编辑它,在写入resolv.conf的地方加个判断
# 找到类似这样的代码段:
# echo "nameserver $dns" >> /etc/resolv.conf

# 改成:
if [ ! -f /etc/resolv.conf.lock ]; then
    echo "nameserver $dns" >> /etc/resolv.conf
fi
💡 我的习惯:在rootfs里放一个空的/etc/resolv.conf.lock文件。脚本检测到它存在,就不动resolv.conf。这样既保留了DHCP的功能,又保护了DNS配置。

4.5 三种方案对比

方案 优点 缺点 适用场景
chattr +i 简单直接,一劳永逸 修改麻烦,需要先解锁 生产环境、固定DNS
符号链接 灵活,可配合systemd 依赖systemd-resolved 使用systemd的系统
修改DHCP脚本 可控性强,不影响DHCP 需要维护脚本 嵌入式Linux、自定义系统

4.6 实战:在rootfs中固化DNS配置

我个人习惯在构建rootfs时就把DNS配置固化好。以Buildroot为例:

# 在post-build脚本中添加
#!/bin/sh
# 固化DNS配置
echo "nameserver 8.8.8.8" > $TARGET_DIR/etc/resolv.conf
echo "nameserver 114.114.114.114" >> $TARGET_DIR/etc/resolv.conf
chattr +i $TARGET_DIR/etc/resolv.conf

# 或者用符号链接方式
# rm -f $TARGET_DIR/etc/resolv.conf
# ln -sf /tmp/resolv.conf $TARGET_DIR/etc/resolv.conf

核心要点:

  • chattr +i 是最彻底的方案,适合固定DNS场景
  • 符号链接方案更灵活,适合需要动态调整的场景
  • 修改DHCP脚本适合需要保留DHCP功能的嵌入式设备
  • 无论哪种方案,都要在rootfs构建阶段就处理好

我曾经在一个物联网项目里,设备部署到现场后DNS配置被覆盖,导致所有设备无法上报数据。最后发现是运营商的DHCP服务器会下发自己的DNS,把我们的配置冲掉了。从那以后,我所有项目都默认用chattr +i锁死resolv.conf。

为什么会这样?说白了,Linux的DNS管理机制太“民主”了——谁都能写resolv.conf。在服务器上可能问题不大,但在嵌入式设备上,这就是个定时炸弹。你想想看,设备部署到偏远地区,网络一重启DNS就丢了,谁来现场改?

所以,我的建议是:在rootfs阶段就把DNS配置“焊死”。不管是chattr +i还是符号链接,选一个适合你项目的方案,一劳永逸。

📌 避坑指南:我曾经在调试阶段用chattr +i锁了resolv.conf,结果忘了这茬,折腾了半天才发现是自己锁的。建议在开发板上先用符号链接方案,等固件定型了再换chattr +i。
DNS配置根治方案 resolv.conf被覆盖 三大元凶 NetworkManager systemd-resolved DHCP客户端 三种根治方案 方案一:chattr +i 直接锁死文件 方案二:符号链接 重定向到自控文件 方案三:修改脚本 控制DHCP写入行为 在rootfs阶段固化DNS配置 一劳永逸,防止被覆盖

这张图把整个逻辑串起来了。从问题出发,找到三个元凶,再对应三种方案。你按照这个思路去排查,DNS被覆盖的问题基本都能解决。

可可精品资料库