dnsmasq 实战:本地 DNS 缓存加速、内网域名解析与 DHCP 一体化配置


<!DOCTYPE html>

dnsmasq 实战:本地 DNS 缓存加速、内网域名解析与 DHCP 一体化配置

dnsmasq 实战:本地 DNS 缓存加速、内网域名解析与 DHCP 一体化配置

每次访问网站都要先做 DNS 解析,而系统默认走的公共 DNS(如 8.8.8.8、223.5.5.5)响应距离远、还有被污染的风险。在 VPS 或局域网服务器上部署一个 dnsmasq,可以一举三得:给全机做 DNS 本地缓存加速重复解析、为内网设备提供自定义域名(比如 nas.lan 指向内网 IP)、顺带接管 DHCP 给局域网设备分配 IP。dnsmasq 轻量到只有几百 KB,配置却非常灵活,是 Linux 上"DNS + DHCP"一体化的经典方案。

一、DNS 解析流程与缓存的价值

先看一次完整的域名解析流程,理解缓存为什么能加速:

浏览器 --> 1. 查 /etc/hosts
        --> 2. 查系统缓存(systemd-resolved / nscd)
        --> 3. 发请求给配置的 DNS 服务器(公共 DNS)
        --> 4. 公共 DNS 递归查询根服务器/权威服务器
        --> 5. 拿到 A 记录返回浏览器

第 4 步是整个链路最慢的环节:一条未缓存的域名可能耗时 50~300ms,而且每次请求都要走一遍。dnsmasq 跑在本地,第一次解析后把结果按 TTL 缓存起来,第二次起直接本地命中,解析耗时降到 1ms 以内,还能过滤掉部分 DNS 污染(因为它缓存的是你自己查询到的结果,而且可以配置上游直接转发到可信 DNS)。

对 VPS 来说,收益最明显的是:apt 更新、docker pull、定时脚本里大量重复解析同一个域名,缓存命中后整体速度明显提升。

二、安装与基础配置:5 分钟跑起来

Debian/Ubuntu 安装后会自动启动并监听 53 端口:

apt install -y dnsmasq

# 查看状态与端口
systemctl status dnsmasq
ss -tlnp | grep 53

dnsmasq 的主配置在 /etc/dnsmasq.conf,默认几乎全部是注释。基础配置只需要很少几行:

# /etc/dnsmasq.conf(追加到末尾)
# 只监听本机,不对局域网开放(纯本机缓存场景)
listen-address=127.0.0.1

# 上游 DNS:多个会用并发查询选最快返回
server=223.5.5.5
server=119.29.29.29

# 缓存条数上限
cache-size=10000

# 不需要的查询类型别缓存
no-resolv

把系统的 DNS 指到本地。以 systemd 系统为例(Ubuntu 18.04+/Debian 11+):

# 方式一:NetworkManager 管理时
nmcli con mod "Wired connection" ipv4.dns "127.0.0.1" ipv4.ignore-auto-dns yes
nmcli con up "Wired connection"

# 方式二:systemd-networkd 时,在网卡配置里加 DNS=127.0.0.1

# 验证解析走的是本地 dnsmasq
dig @127.0.0.1 www.example.com
dig example.com | grep -A 3 "SERVER"

重启服务并验证缓存生效:

systemctl restart dnsmasq

# 第一次解析看耗时
time dig @127.0.0.1 www.example.com
# 第二次解析,Query time 应降到 0~1ms
time dig @127.0.0.1 www.example.com

三、内网域名解析:让设备用名字访问

局域网里访问 NAS、路由器、打印机,用 IP 记不住,用公网域名又解析不到内网。dnsmasq 支持直接在配置里定义主机记录,比改每台机器的 /etc/hosts 高效得多:

# /etc/dnsmasq.conf
# 把内网域名指向内网 IP(DNS 服务器本身不查上游,直接返回)
address=/nas.lan/192.168.1.100
address=/printer.lan/192.168.1.50
address=/router.lan/192.168.1.1

# 泛域名:*.dev.lan 全部指向同一台开发机
address=/dev.lan/192.168.1.10

也可以把一整批记录放在独立文件里,用 addn-hosts 引入,方便按项目管理:

# /etc/dnsmasq.conf
addn-hosts=/etc/dnsmasq.hosts

# /etc/dnsmasq.hosts
192.168.1.100  nas.lan
192.168.1.50   printer.lan
192.168.1.10   dev.lan

注意区分两个机制:address=/域名/IP 是"域名到 IP 的静态映射"(不区分正反解,还会自动生成 PTR);addn-hosts 走 hosts 文件格式,推荐优先使用,语义更清晰。

配置完让局域网其他设备也能用这台服务器解析(把客户端 DNS 指向这台服务器的内网 IP),需要让 dnsmasq 监听内网网卡:

# /etc/dnsmasq.conf
listen-address=127.0.0.1,192.168.1.1
# 防火墙放行 53 端口
ufw allow 53/tcp
ufw allow 53/udp

验证内网解析:

# 在另一台机器上
nslookup nas.lan 192.168.1.1
dig @192.168.1.1 nas.lan

四、顺带接管 DHCP:局域网一体化

既然 dnsmasq 已经在跑,可以让它顺便承担 DHCP 服务,这样"分配的 IP 和 DNS 域名解析"天然联动,客户端拿到的 DNS 自动就是 dnsmasq 自己,内网域名开箱即用:

# /etc/dnsmasq.conf
# 网段与子网掩码
dhcp-range=192.168.1.100,192.168.1.200,255.255.255.0,12h
# 网关
dhcp-option=option:router,192.168.1.1
# 下发给客户端的 DNS 就是 dnsmasq 自己
dhcp-option=option:dns-server,192.168.1.1
# 根据 MAC 固定分配 IP(可选的静态绑定)
dhcp-host=AA:BB:CC:DD:EE:FF,192.168.1.101,nas

重启后客户端重新获取 IP 即可生效。注意:局域网里已有其他 DHCP 服务器(如路由器自带)时,不要把两者同时开启,否则会 IP 冲突。生产环境建议关掉路由器 DHCP,统一由 dnsmasq 管理。

五、与 systemd-resolved 共存:53 端口占用排错

Ubuntu 18.04+ 默认启用 systemd-resolved,它自己也监听 127.0.0.53:53。安装 dnsmasq 后最常见的报错就是:

# 启动失败,日志提示
dnsmasq: failed to create listening socket for 127.0.0.1:53: Address already in use

因为 dnsmasq 默认监听 0.0.0.0:53,而 resolved 占了 127.0.0.53:53。两个方案任选:

# 方案 A:关掉 systemd-resolved 的监听,让 dnsmasq 独占 53
systemctl stop systemd-resolved
systemctl disable systemd-resolved
# 把 /etc/resolv.conf 指向 127.0.0.1
echo "nameserver 127.0.0.1" > /etc/resolv.conf

# 方案 B:保留 resolved,让 dnsmasq 只监听内网网卡(比如 192.168.1.1)
# 在 /etc/dnsmasq.conf 里不要写 listen-address=127.0.0.1,只写内网 IP
listen-address=192.168.1.1
# 客户端查询走 resolved 转发或直接指 dnsmasq

方案 A 简单直接,适合"这台机器所有进程都用 dnsmasq"的场景;方案 B 适合还要保留 resolved 的 .local mDNS 等功能的场景。注意改完 resolv.conf 后,如果它被其他工具(NetworkManager、cloud-init)重写,需要额外配置相应工具的管理策略,否则重启后又被覆盖。

六、常见排错汇总

1. 缓存不生效:Query time 每次都是 50ms+
先确认查询确实打到 dnsmasq:dig example.com | grep SERVER,SERVER 应该显示 127.0.0.1。如果显示公共 DNS 的 IP,说明系统根本没走本地。再检查 cache-size 是否设置(默认 150 太小,建议 10000),以及是否误开了 no-cache。

# 查看缓存命中统计(cache 行)
kill -USR1 $(cat /var/run/dnsmasq.pid)
journalctl -u dnsmasq --since "1 min ago" | grep cache

2. 解析结果总是不对/被污染
确认上游 DNS 用的是可信的国内或国外 DNS,且 no-resolv 已开启(防止 dnsmasq 又去读 /etc/resolv.conf 里的旧配置导致转发循环)。如果还是不对,直接指定查询看上游:

dig @223.5.5.5 example.com   # 对比上游原始结果
dig @127.0.0.1 example.com   # 对比 dnsmasq 结果

3. 内网域名解析不到
检查 addn-hosts 文件权限(dnsmasq 以 dnsmasq 用户运行,文件需要可读):

chmod 644 /etc/dnsmasq.hosts
chown root:root /etc/dnsmasq.hosts
dnsmasq --test   # 配置语法检查
systemctl restart dnsmasq

4. 客户端拿不到内网域名(能解析公网)
确认客户端的 DNS 指向的是 dnsmasq 所在机器的内网 IP,而不是 127.0.0.1(127.0.0.1 只对 dnsmasq 本机生效)。用 dhcp-option=option:dns-server 下发正确地址,并在客户端上验证:dig @192.168.1.1 nas.lan。

5. 转发循环:日志里报 "no servers could be reached"
典型原因:dnsmasq 把上游指向了 127.0.0.53(systemd-resolved),而 resolved 又转发回 127.0.0.53 上的 dnsmasq,形成死循环。解决:上游 server 指令写公共 DNS 的 IP,或在方案 A 里干脆关掉 resolved。

七、小结

dnsmasq 用一份轻量配置同时解决 DNS 缓存加速、内网域名解析和 DHCP 分配三个问题,是 Linux 服务器和局域网网关场景的高性价比选择。核心要点:一是 listen-address 决定服务范围,本机缓存用 127.0.0.1、局域网服务加内网 IP;二是内网域名用 address/addn-hosts 定义,客户端 DNS 指向 dnsmasq 内网地址即可生效;三是和 systemd-resolved 的 53 端口冲突是最常见启动故障,按第五节方案处理。配置改完记得 dnsmasq --test 校验再重启,排错时先用 dig 指定 @127.0.0.1 逐层对比,很快就能定位问题在哪一层。


手机扫码阅读

微信或手机浏览器扫一扫,随时随地随心阅读与分享

PHP爆出2个高危漏洞(CVSS 9.2),今晚22点官方发布修复包,附临时加固方案

Python Web 应用部署实战:Gunicorn 安装配置、systemd 托管与 Nginx 反向代理调优

评 论
更换验证码