
最近好几个朋友问我同一件事公司内部想上个内网域名解析或者家里想搞个本地DNS缓存到底该怎么部署DNS服务器。说实话这个问题看起来简单真做起来坑不少从选型到配置再到被系统“还原”DNS配置每一步都能踩出花来。所以我干脆把一套完整的部署流程和排障笔记整理出来覆盖BIND9、dnsmasq、系统配置固化、常见故障这几块内容适合刚接手服务器、想搞内网域名解析、或者单纯想优化上网解析速度的朋友做参考。1. 部署前先想清楚你真的需要自建DNS吗1.1 哪些场景适合自建DNSDNS服务器部署这事不是所有人都需要做但一旦你有下面这几类需求自建就是最省事的解法。第一类是内网域名解析。公司内部有GitLab、Jenkins、NAS、监控面板这类系统IP地址记不住也不稳定给它们起个像gitlab.internal这样的内部域名比每次翻文档查IP强一百倍。第二类是DNS缓存加速。局域网内几十上百台设备频繁访问外网如果每台设备都直接请求公共DNS延迟和流量都不划算部署一个带缓存的DNS服务器命中一次缓存后续响应就是毫秒级。第三类是配合AD域控或vCenter这类基础设施。域控环境里DNS是刚需SRV记录、动态注册都依赖它vCenter虽然理论上可以后补DNS但部署时没有规划好DNS后面补起来要改一堆配置特别折腾。也有一部分场景其实不需要自建DNS。如果你的需求只是“上不了网”先排查网络、运营商、路由器别第一时间怀疑DNS服务器。如果你的网络里只有三五台设备对域名解析没有特殊要求那直接填公共DNS地址就够了自建纯属增加维护量。1.2 常见DNS软件的选型对比市面上常用的DNS服务软件就这么几个选型只看两点你的规模有多大你要不要做权威解析。软件定位适合场景学习曲线BIND9全能型权威递归正经的内网DNS服务器、域名托管配置复杂概念多dnsmasq轻量缓存转发小型局域网、开发环境、嵌入式极简单上手快Unbound递归解析器注重性能和安全性的缓存DNS中等CoreDNS云原生DNSKubernetes集群内部域名配置风格偏新如果你要管一批内网域名并且希望它稳定运行一两年不用动BIND9是首选它是最标准的权威DNS实现几乎所有运维文档和教程都绕不开它。如果你只是想给局域网加个DNS缓存再顺便解析几个自定义域名dnsmasq一行配置就能搞定没必要上BIND9。CoreDNS属于另一个赛道主要服务容器和K8s这里不展开细说。1.3 环境准备系统、IP和端口规划部署DNS服务建议用Linux服务器Ubuntu、Debian、openEuler、银河麒麟都可以核心依赖就一个glibc和网络栈对发行版没有特殊要求。IP地址必须用静态IP这是很多人容易忽略的点。DNS服务器如果通过DHCP获取IP一旦租约变了所有客户端都会断解析。建议在部署前就把服务器IP固化比如规划为192.168.1.2后续所有客户端都把DNS指向它。端口方面DNS默认监听UDP/TCP 53端口。UDP用于常规查询TCP用于区域传送和大响应。如果你的服务器上装了Docker、K8s或者其他占用53端口的服务部署前先查一下端口占用情况不然DNS服务起不来ss -lntup | grep :532. BIND9完整部署从安装到第一个内网域名解析2.1 安装BIND9并调整主配置BIND9在不同发行版上安装命令略有不同Debian/Ubuntu系用aptopenEuler和银河麒麟这些RPM系用dnf或yum。# Ubuntu / Debian apt update apt install -y bind9 bind9utils # openEuler / 银河麒麟RPM系 dnf install -y bind bind-utils装完后主配置文件在/etc/bind/named.confRPM系是/etc/named.conf它一般会通过include引入named.conf.options、named.conf.local这些子配置。我们重点改named.conf.optionsoptions { directory /var/cache/bind; # 工作目录 listen-on port 53 { any; }; # 监听所有网卡 listen-on-v6 { none; }; # 没有IPv6环境就关掉 allow-query { localhost; 192.168.1.0/24; }; # 只允许本机和内网网段查询 allow-recursion { localhost; 192.168.1.0/24; }; # 只允许内网递归 recursion yes; # 开启递归查询 forwarders { 223.5.5.5; 114.114.114.114; }; # 配置上游DNS forward only; # 只转发不自己迭代 dnssec-validation auto; # 开启DNSSEC校验 };这里有几个点值得多说一下。allow-query控制的是“谁能问你”allow-recursion控制的是“谁能让你帮忙查外部域名”这两个如果不限制你的DNS服务器就会变成开放递归容易被人利用做DDoS放大攻击。forwarders填的是上游公共DNS我习惯用223.5.5.5阿里和114.114.114.114114如果你在境外有服务器也可以根据地区选择更近的公共DNS但境内环境我建议优先用国内公共DNS。2.2 配置正向区域与主机记录改完主配置接下来要定义“我负责哪些域名”。编辑named.conf.local添加一个正向区域zone internal.local { type master; file /etc/bind/db.internal.local; };internal.local不是真实公网域名用.local后缀可以避免和公网冲突这是内网DNS的通用做法你也可以用example.internal等自定义后缀但不要用真实业务域名否则会和公网解析产生冲突。然后创建区域文件/etc/bind/db.internal.local内容如下$TTL 86400 IN SOA ns1.internal.local. admin.internal.local. ( 2025012001 ; 序列号每次修改都要递增 3600 ; 刷新时间 1800 ; 重试时间 604800 ; 过期时间 86400 ; 缓存时间 ) IN NS ns1.internal.local. IN A 192.168.1.2 ns1 IN A 192.168.1.2 web IN A 192.168.1.10 git IN A 192.168.1.11 nas IN A 192.168.1.12这个文件的核心是SOA记录和NS记录。SOA记录里的序列号很重要每次修改区域文件都必须递增这个数字否则从服务器不会同步更新。NS记录声明了权威服务器是谁这里就是本机ns1.internal.local。最后那几行web、git、nas就是常见的A记录IP地址换成你实际的内网地址就行。2.3 配置反向区域与PTR记录正向解析是把域名翻译成IP反向解析是把IP翻译回域名。很多工具和系统会做反向查询比如邮件服务器、日志分析所以建议顺手配上。在named.conf.local里追加反向区域zone 1.168.192.in-addr.arpa { type master; file /etc/bind/db.192.168.1; };注意反向区域的名字有讲究IP是192.168.1.x对应的区域名就是1.168.192.in-addr.arpa也就是把IP段倒过来写。IP是10.0.0.x就写0.0.10.in-addr.arpa千万不要写反。创建文件/etc/bind/db.192.168.1$TTL 86400 IN SOA ns1.internal.local. admin.internal.local. ( 2025012001 3600 1800 604800 86400 ) IN NS ns1.internal.local. 2 IN PTR ns1.internal.local. 10 IN PTR web.internal.local. 11 IN PTR git.internal.local. 12 IN PTR nas.internal.local.PTR记录只写IP最后一位比如10代表192.168.1.10对应的是web.internal.local。写完后建议用命令校验一下配置正确性named-checkconf /etc/bind/named.conf named-checkzone internal.local /etc/bind/db.internal.local如果没有报错说明语法没问题。有报错就按提示改named-checkzone会明确告诉你哪行有问题排查起来很快。2.4 启动验证与开机自启配置完成后重启BIND9服务并设置开机自启systemctl restart named # Debian系是 bind9RPM系是 named systemctl enable named检查服务状态确保是active (running)systemctl status named然后在本机验证解析是否正常dig 127.0.0.1 web.internal.local dig 127.0.0.1 -x 192.168.1.10dig返回status: NOERROR并且A记录指向192.168.1.10说明正向解析OK。-x参数是查反向解析返回PTR记录就说明反向也通了。如果返回REFUSED多半是allow-query没放行你当前的IP。3. 轻量级方案dnsmasq做内网缓存与泛解析3.1 什么场景下更推荐dnsmasqBIND9功能全但配置也确实繁琐。如果你只是个小办公室网络几十台机器需要解析几个内部系统域名顺便做个缓存加速那dnsmasq更合适。dnsmasq本身是个极轻量的DNS转发器配置复杂度比BIND9低一个数量级。它可以把一个域名的所有解析都指向某个内网IP比如开发环境里经常要模拟线上域名一行address配置就搞定不需要维护zone文件。它还自带DHCP功能可以把DNS下发给所有客户端省去一台台配置的麻烦。3.2 dnsmasq的核心配置说明安装还是老规矩apt install dnsmasq或dnf install dnsmasq。装完后配置文件在/etc/dnsmasq.conf这个文件本身就有一大堆注释文档不用全看核心配置就几行# 监听内网网卡 interfaceeth0 bind-interfaces # 不读取 /etc/hosts避免干扰如果你要本地hosts解析可以保留 no-hosts # 上游DNS服务器 server223.5.5.5 server114.114.114.114 # 缓存条数 cache-size10000 # 泛解析把 *.dev.local 都指向 192.168.1.50 address/dev.local/192.168.1.50 # 单个域名指定解析 address/jenkins.internal/192.168.1.21 # 记录查询日志排查问题非常有用 log-queries log-facility/var/log/dnsmasq.logaddress/dev.local/192.168.1.50的意思是所有以dev.local结尾的域名都解析到192.168.1.50。这个功能在BIND9里要做一堆配置在dnsmasq里就是一行所以开发测试环境我特别推荐用它。cache-size10000表示最多缓存10000条记录对小型网络完全够用。日志一定要开排障的时候能看到每个域名请求是从哪台机器来的、命中了缓存还是走了上游信息量极大。3.3 缓存命中与日志验证启动dnsmasq后用同样的方法验证systemctl restart dnsmasq dig 127.0.0.1 dev.local dig 127.0.0.1 www.baidu.com本地域名能解析到192.168.1.50外网域名能正常返回IP说明服务已经通。再看一眼日志确认缓存是否生效tail -f /var/log/dnsmasq.log第一次查询外部域名时日志会显示forwarded to 223.5.5.5第二次再查相同域名会显示cached说明缓存生效了。如果你部署后发现53端口起不来十有八九是系统自带的systemd-resolved占了端口这个在第四节详细讲。4. 系统DNS配置与防还原实战4.1 resolv.conf为什么改完就“还原”DNS服务器搭建好了你自己的服务器得先指向它吧。很多朋友直接在/etc/resolv.conf里写nameserver 192.168.1.2改完当下是生效的一重启网络或者重启系统发现又恢复成原来的样子。原因很简单/etc/resolv.conf现在大多是一根软链接由NetworkManager或systemd-resolved这类工具动态生成你直接改它等于在改一个系统随时会覆盖的临时文件。这是很多Linux初学者最容易困惑的点不是配置写错了是改错了地方。正规做法是通过你系统里的网络管理工具去设DNS而不是直接改resolv.conf。4.2 Ubuntu/Debian系的正规改法Ubuntu不同版本的改法还不一样。老版本用/etc/network/interfaces新版本用Netplan还有用NetworkManager的桌面环境容易搞混。先看你的系统用的是什么网络管理方式systemctl status NetworkManager # 看NetworkManager是否运行 ls /etc/netplan/ # 看是否有netplan目录如果是NetplanUbuntu 18.04服务器版常见编辑对应配置文件一般是/etc/netplan/00-installer-config.yamlnetwork: ethernets: eth0: dhcp4: false addresses: - 192.168.1.2/24 routes: - to: default via: 192.168.1.1 nameservers: addresses: - 192.168.1.2 - 223.5.5.5 version: 2改完后执行netplan apply生效。如果你确定系统是用NetworkManager管理推荐用nmcli命令行修改最不容易出错nmcli connection modify Wired connection 1 ipv4.dns 192.168.1.2 223.5.5.5 nmcli connection modify Wired connection 1 ipv4.ignore-auto-dns yes nmcli connection up Wired connection 1ipv4.ignore-auto-dns yes这行很关键意思是忽略DHCP下发的DNS优先用手动指定的。不然你可能明明在NetworkManager里配好了下一分钟DHCP续租又把DNS覆盖了回去。4.3 欧拉与银河麒麟的配置差异国产系统这几年用的人越来越多它们的DNS配置方式和主流Linux大体相同但有小差异需要注意。openEuler欧拉系列和RHEL系一脉相承默认NetworkManager管理网络。推荐直接用nmcli命令和Ubuntu上几乎一样nmcli connection modify eth0 ipv4.dns 192.168.1.2 223.5.5.5 nmcli connection modify eth0 ipv4.ignore-auto-dns yes nmcli connection up eth0银河麒麟分两个大系桌面版很多基于Ubuntu/Debian体系服务器版有些基于CentOS/RHEL体系具体看你的内核和包管理器。如果是apt系参照Ubuntu的方法用Netplan或NetworkManager如果是yum/dnf系照openEuler的方法用nmcli。最稳妥的办法是先跑一下systemctl status NetworkManager确认网络管理工具后再动手。另外银河麒麟桌面版可以直接在“设置 → 网络 → 有线 → IPv4 设置”里填DNS这个图形化操作对新手很友好但记得把IPv4/DHCP模式改成“自动”或“手动指定”并填好DNS否则改了也会被覆盖。4.4 Windows端配置自建DNS服务器端的DNS指向自己客户端的Windows机器要把DNS指到你的DNS服务器上。正确路径是控制面板 → 网络和共享中心 → 更改适配器设置 → 右键当前网卡 → 属性 → 双击“Internet协议版本4(TCP/IPv4)”然后选择“使用下面的DNS服务器地址”填入你部署的DNS服务器IP。也可以用命令行的方式适合批量配置netsh interface ip set dns 以太网 static 192.168.1.2如果网卡名称带空格或中文先运行ipconfig看一下网卡全称。配完之后立刻验证ipconfig /flushdns nslookup web.internal.local nslookup www.baidu.comnslookup能返回结果说明Windows到DNS服务器的链路是通的。5. 常见问题与排查技巧实录5.1 部署和排障速查表我把自己踩过的坑和帮别人排查到的问题整理成了速查表按症状直接对号入座效率最高。症状可能原因排查方式DNS服务起不来53端口被systemd-resolved或其他服务占用ss -lntup | grep :53服务启动成功但客户端查询超时防火墙未放行53端口ufw status/firewall-cmd --list-ports内网域名解析返回REFUSEDallow-query范围没包含客户端IPdig 服务器IP 域名看回包状态外网域名解析失败上游DNS不通或转发配置错误dig 223.5.5.5 www.baidu.com测试上游内网域名解析出公网IP有同名公网域名且没做优先处理检查zone配置和/etc/hosts修改DNS重启后失效直接改了resolv.conf而不是通过网络管理器改用nmcli/netplan配置日志显示大量外部查询客户端被下发错DNS或DNS被外部设备使用检查DHCP配置option 65.2 被“还原”的DNS与1014断网案例有个典型的Windows问题事件查看器里出现DNS Client Events 1014系统日志提示DNS客户端无法解析某条记录然后网络看起来就“断网”了。这个现象很迷惑人因为IP网络其实是通的就是域名解析失败。我遇到过这样的情况用户电脑手动把DNS改成8.8.8.8后在某些网络环境里始终解析不稳定事件日志不断刷1014。换回运营商DNS或内网DNS后立刻恢复正常。排查思路很简单先ping一个IP地址确认网络通不通再nslookup确认是解析问题还是网络问题最后ipconfig /flushdns清掉缓存把DNS地址改成可用的内网或公共DNS。另外谷歌浏览器里偶尔会显示DNS_PROBE_STARTED的错误页这就是浏览器向系统发起DNS探测后迟迟没有拿到结果的提示。遇到这个错误优先检查系统DNS是否配置正确、是否指向了一个不可达的DNS服务器而不是重装浏览器。5.3 公共DNS到底选运营商还是大厂既然提到了公共DNS这块是绕不开的纠结到底用本地运营商自动下发的DNS还是改成大厂的公共DNS本地运营商DNS最大的优势是延迟低你离它物理距离近响应速度快。但它有几个问题一是缓存内容有时候不干净个别地区运营商DNS存在解析劫持和广告注入的现象访问一个正常网站可能会被插播提示页二是运营商的DNS记录更新有时滞后新域名解析不及时。公共DNS大厂一般不会做劫持这类小动作稳定性更好还支持DNSSEC和DoH这类安全特性缺点是延迟稍微高一点点但这几毫秒在绝大多数场景里感知不到。如果你在网络环境里碰到网页被插内容、或者解析到了奇怪的IP建议把DNS换成大厂公共DNS比如阿里223.5.5.5、腾讯119.29.29.29、114的114.114.114.114。至于“用某个国外公共DNS会不会有危险”这个问题答案是“解析行为本身不会让你的电脑中毒但它会让你的所有域名查询请求都暴露给第三方”而且在国内网络环境里延迟和稳定性都不理想我自己的建议是内网有自建DNS就优先用自建外网解析交给国内公共DNS别在这上面折腾。5.4 与AD域控、vCenter等系统集成的注意点如果你部署DNS服务器是为了配合AD域控有几个特性必须搞清楚。域控依赖DNS里的SRV记录来定位服务包括_ldap._tcp、_kerberos._tcp这些记录所以最稳妥的做法是DNS服务器和AD域控部署在同一台机器上或者至少保证域控使用的DNS服务器能动态注册这些记录。如果你拿一个普通BIND9当成纯转发DNS指给域控用服务会发现不了域控、登录验证超时这类坑我见过太多次了。vCenter部署时也强烈建议提前规划好DNS。虽然vCenter 7.0理论上可以在没有DNS的环境下安装部署后也能通过IP访问但后续配置证书、加主机、做扩展这些操作全都会因为域名解析不对而报错补起来相当麻烦。我的建议是部署DNS服务器这件事最好在装vCenter和域控之前完成。6. 进阶用Docker容器化部署DNS服务6.1 最小化容器部署dnsmasq如果你的环境已经全面容器化或者不想在宿主机上装一堆依赖可以把DNS服务跑在Docker里。dnsmasq镜像很小跑起来几乎不占资源特别适合在开发测试环境临时起一个DNS服务。一条命令就能跑起来docker run -d \ --name dnsmasq \ --restart unless-stopped \ -p 53:53/tcp \ -p 53:53/udp \ -v /opt/dnsmasq/dnsmasq.conf:/etc/dnsmasq.conf \ -v /opt/dnsmasq/resolv.conf:/etc/resolv.conf \ jpillora/dnsmasq把宿主机的/opt/dnsmasq/dnsmasq.conf挂载进去里面按第三节的方式写配置启动后宿主机53端口就提供服务了。6.2 端口映射与持久化容器化部署有个坑必须提醒很多系统默认systemd-resolved占着本地53端口Docker启动时会报端口冲突。最快的解决方式是修改/etc/systemd/resolved.confDNSStubListenerno改完重启systemd-resolvedsystemctl restart systemd-resolved然后再次启动dnsmasq容器。注意关掉DNSStubListener后本机的/etc/resolv.conf需要手动指定一个有效DNS地址比如127.0.0.1指向dnsmasq容器映射的53端口或者直接填上游公共DNS否则本机自己的DNS解析会断。6.3 容器化部署的取舍容器化部署的好处是干净、可复制、回滚方便配置错了直接删掉容器重建一个就行。坏处是排查问题多一层抽象比如容器内日志和宿主机日志是隔开的遇到网络问题要先确认端口映射和容器网络模式。我的建议是生产环境中的长期DNS服务还是用宿主机直接部署BIND9更靠谱运维视角清晰临时环境、CI流水线、或者纯缓存节点用容器跑dnsmasq完全没问题。DNS这种基础服务稳定和可维护性永远优先于“部署起来很酷”。按我自己的习惯最后分享一个部署经验内网DNS正式上线前一定要把所有客户端的DNS地址通过DHCP统一下发不要在每台机器上手动配置。DHCP服务器里设置好option 6DNS服务器地址客户端自动获取就行以后要切换DNS服务器只需要改一处不用两台两台机器去敲命令。我最初就是图省事手动给机器配DNS后来IP规划调整改了几十台机器吃够了苦头。现在任何环境我都先用DHCP把DNS下发机制理顺再调整上游省心很多。