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
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还是符号链接,选一个适合你项目的方案,一劳永逸。
这张图把整个逻辑串起来了。从问题出发,找到三个元凶,再对应三种方案。你按照这个思路去排查,DNS被覆盖的问题基本都能解决。
可可精品资料库