手把手教你搭建DNS服务器,告别网络卡顿的痛苦!
你有没有遇到过网络又卡得要命,打开个网页都要等半天,想要解决网络问题却找不到解决的办法!其实很多时候网络慢不是带宽的问题,而是DNS解析太慢了。今天就跟大家聊聊DNS这个东西,顺便教大家怎么搭建一个自己的DNS服务器。
说起DNS,可能很多人觉得这玩意儿很神秘,其实它就像是互联网的电话簿。你想打电话给张三,但是你只记得他的名字,不记得电话号码,这时候就需要查电话簿找到对应的号码。DNS就是干这个活的,把我们容易记住的域名(比如baidu.com)转换成计算机能理解的IP地址(比如182.61.201.211)。
DNS的前世今生
回想起刚接触IT那会儿,我对DNS的理解还停留在"改个DNS就能上网"的层面。后来才知道这套系统的历史还挺有意思的。
早期的互联网规模很小,那时候大家都用一个叫HOSTS.TXT的文件来做域名解析。想象一下,全世界就一个文件,每次有新的主机加入网络,就要手动更新这个文件,然后分发给所有人。这简直就是噩梦,特别是到了80年代初,互联网开始快速发展的时候。
1983年,保罗·莫卡派乔斯(Paul Mockapetris)提出了DNS的概念,并在1984年发布了RFC 1034和RFC 1035两个标准。这套系统采用了分层的树状结构,彻底解决了集中式管理的问题。说实话,这个设计到现在都还在用,可见当时设计得有多巧妙。
DNS的工作原理其实很简单,就是一个分布式的数据库系统。根域名服务器在最顶层,然后是顶级域名服务器(比如.com、.cn),再下面是二级域名服务器,以此类推。当你访问一个网站时,DNS查询就像问路一样,一级一级地往下找,直到找到对应的IP地址。
为什么要自建DNS服务器
可能有人会问,用公共DNS不是挺好的吗,比如8.8.8.8或者114.114.114.114,为什么还要自己搭建呢?
我之前在一家小公司工作的时候,就遇到过这样的问题。公司有个内部的OA系统,域名是oa.company.local,这种内网域名公共DNS肯定是解析不了的。每次新员工入职,都要手动修改hosts文件,麻烦得要死。后来搭建了内部DNS服务器,这个问题就彻底解决了。
还有一个原因就是速度。虽然公共DNS服务器性能很好,但是毕竟距离远,而且查询量大。自建DNS服务器可以针对常用的域名做缓存,响应速度会快很多。特别是对于一些经常访问的网站,第二次查询基本上是秒级响应。
另外,自建DNS还能实现一些特殊功能,比如广告屏蔽、恶意网站过滤等等。我见过有些公司用DNS来屏蔽员工访问某些网站,虽然这种做法有点争议,但确实是一个应用场景。
DNS服务器的功能
- 正向解析:根据注册的域名查找其对应的IP地址
- 反向解析:根据IP地址查找对应的注册域名,不常用
DNS分布式结构
根域名:
一级域名:
.cn
.us
.tw
hkjp.
.kr
二级域名:
.com.cn .org.cn .net.cn
三级域名:
haha.com.cn xixi.com.cn .nb.com.cn
FQDN (完全合格的主机名) :站点名+注册的域名
DNS域名管理机构
IANA,互联网数字分配机构
- -Internet Assigned Numbers Authority
- 整个域名系统的最高权威机构
- 主管DNS根、.int、.arpa等国际化域名资源
CNNIC,中国互联网络信息中心
- China Internet Network nformation Center
- 主管国家顶级域名.cn
开始搭建DNS服务器
好了,废话不多说,开始动手搭建。我这里用的是CentOS 7系统,DNS软件选择BIND9,这是目前最流行的DNS服务器软件。
安装BIND9
首先更新系统包:
yum update -y
安装BIND及相关工具:
yum install -y bind bind-utils
安装完成后,BIND的主配置文件在/etc/named.conf
,区域文件默认放在/var/named/
目录下。
配置主配置文件
编辑/etc/named.conf
文件:
vim /etc/named.conf
这个文件的配置看起来挺复杂,但其实理解了结构就很简单。主要分为几个部分:options(全局选项)、logging(日志配置)、zone(区域配置)。
options {
listen-on port 53 { 127.0.0.1; 192.168.1.100; };
listen-on-v6 port 53 { ::1; };
directory "/var/named";
dump-file "/var/named/data/cache_dump.db";
statistics-file "/var/named/data/named_stats.txt";
memstatistics-file "/var/named/data/named_mem_stats.txt";
recursing-file "/var/named/data/named.recursing";
secroots-file "/var/named/data/named.secroots";
allow-query { localhost; 192.168.1.0/24; };
recursion yes;
dnssec-enable yes;
dnssec-validation yes;
bindkeys-file "/etc/named.root.key";
managed-keys-directory "/var/named/dynamic";
pid-file "/run/named/named.pid";
session-keyfile "/run/named/session.key";
};
这里有几个重要的配置项需要注意:
listen-on
:指定DNS服务器监听的IP地址和端口allow-query
:允许哪些客户端进行DNS查询recursion
:是否允许递归查询
配置区域文件
接下来配置正向解析区域。假设我们要为example.local
这个域名配置DNS解析。
在/etc/named.conf
文件末尾添加区域配置:
zone "example.local" IN {
type master;
file "example.local.zone";
allow-update { none; };
};
然后创建区域文件:
vim /var/named/example.local.zone
区域文件的内容:
$TTL 86400
@ IN SOA ns1.example.local. admin.example.local. (
2023111501 ; Serial
3600 ; Refresh
1800 ; Retry
604800 ; Expire
86400 ; Minimum TTL
)
; Name servers
@ IN NS ns1.example.local.
; A records
ns1 IN A 192.168.1.100
web IN A 192.168.1.101
mail IN A 192.168.1.102
ftp IN A 192.168.1.103
; CNAME records
www IN CNAME web.example.local.
这个文件看起来有点复杂,我来解释一下各个部分:
- SOA记录:Start of Authority,定义了这个区域的权威信息
- NS记录:Name Server,指定这个域名的名称服务器
- A记录:Address,将域名指向IPv4地址
- CNAME记录:Canonical Name,域名别名
配置反向解析
除了正向解析,我们通常还需要配置反向解析,也就是通过IP地址查询域名。
在/etc/named.conf
中添加反向解析区域:
zone "1.168.192.in-addr.arpa" IN {
type master;
file "192.168.1.rev";
allow-update { none; };
};
创建反向解析文件:
vim /var/named/192.168.1.rev
文件内容:
$TTL 86400
@ IN SOA ns1.example.local. admin.example.local. (
2023111501 ; Serial
3600 ; Refresh
1800 ; Retry
604800 ; Expire
86400 ; Minimum TTL
)
; Name servers
@ IN NS ns1.example.local.
; PTR records
100 IN PTR ns1.example.local.
101 IN PTR web.example.local.
102 IN PTR mail.example.local.
103 IN PTR ftp.example.local.
设置文件权限
BIND对文件权限要求比较严格,需要设置正确的所有者和权限:
chown named:named /var/named/example.local.zone
chown named:named /var/named/192.168.1.rev
chmod 640 /var/named/example.local.zone
chmod 640 /var/named/192.168.1.rev
启动服务
检查配置文件语法:
named-checkconf /etc/named.conf
named-checkzone example.local /var/named/example.local.zone
named-checkzone 1.168.192.in-addr.arpa /var/named/192.168.1.rev
如果没有报错,就可以启动服务了:
systemctl start named
systemctl enable named
配置防火墙:
firewall-cmd --permanent --add-service=dns
firewall-cmd --reload
测试DNS服务器
服务启动后,我们需要测试一下是否工作正常。
使用nslookup测试
nslookup web.example.local 192.168.1.100
如果配置正确,应该会返回对应的IP地址。
使用dig测试
dig命令提供了更详细的查询信息:
dig @192.168.1.100 web.example.local
测试反向解析:
dig @192.168.1.100 -x 192.168.1.101
测试递归查询
测试DNS服务器是否能正确处理外部域名查询:
dig @192.168.1.100 www.baidu.com
如果能正常返回结果,说明递归查询功能工作正常。
常见问题排查
搭建过程中可能会遇到一些问题,我把常见的几个列出来:
服务启动失败
通常是配置文件语法错误导致的,可以用journalctl -u named
查看详细错误信息。我之前就因为区域文件中少了一个分号,折腾了半天。
查询超时
检查防火墙设置,确保53端口已经开放。还有就是检查allow-query
配置,确保客户端IP在允许范围内。
解析结果不正确
可能是缓存问题,可以用rndc flush
清空DNS缓存,或者重启named服务。
权限问题
BIND对文件权限要求很严格,确保区域文件的所有者是named用户,权限设置为640。
高级配置和优化
基本功能搞定后,我们还可以做一些优化和高级配置。
DNS负载均衡配置
DNS负载均衡是个很实用的功能,特别是当你有多台服务器提供相同服务的时候。我之前在一个电商项目中就用过这个功能,效果还不错。
在区域文件中,你可以为同一个域名配置多个A记录:
web IN A 192.168.1.101
web IN A 192.168.1.102
web IN A 192.168.1.103
这样当客户端查询web.example.local时,DNS服务器会轮询返回这三个IP地址,实现简单的负载均衡。不过这种方式有个问题,就是无法检测服务器的健康状态,如果其中一台服务器挂了,DNS还是会把流量分发过去。
更高级一点的配置可以使用权重:
web IN A 192.168.1.101 ; 权重高的服务器
web IN A 192.168.1.101 ; 重复配置增加权重
web IN A 192.168.1.102
虽然看起来有点笨拙,但在简单场景下还是很管用的。
泛域名解析配置
泛域名解析就是用通配符来匹配所有子域名,这个功能在很多场景下都很有用。比如你想让所有的*.example.local都指向同一个服务器。
在区域文件中添加:
* IN A 192.168.1.200
这样配置后,任何以.example.local结尾的域名(如test.example.local、api.example.local、随便什么.example.local)都会解析到192.168.1.200这个IP。
我记得之前做一个多租户系统的时候,每个客户都有自己的子域名,用泛域名解析就省事多了,不用每次新增客户都去改DNS配置。
不过要注意,泛域名的优先级比较低,如果你同时配置了具体的子域名,具体的会优先匹配:
* IN A 192.168.1.200
api IN A 192.168.1.201
这种情况下,api.example.local会解析到192.168.1.201,其他子域名才会解析到192.168.1.200。
有规律解析配置
有时候我们需要根据某种规律来解析域名,比如按照数字递增的方式。BIND本身不直接支持这种动态规律解析,但可以通过一些技巧来实现。
一种方法是预先配置好所有可能的解析:
server1 IN A 192.168.1.11
server2 IN A 192.168.1.12
server3 IN A 192.168.1.13
server4 IN A 192.168.1.14
server5 IN A 192.168.1.15
; ... 继续到server100
虽然看起来很笨,但对于有限范围的规律解析还是可行的。
另一种更灵活的方法是使用脚本动态生成区域文件:
#!/bin/bash
ZONE_FILE="/var/named/example.local.zone"
# 生成server1-100的解析记录
for i in {1..100}; do
echo "server$i IN A 192.168.1.$((10+i))" >> $ZONE_FILE
done
# 重新加载区域文件
rndc reload example.local
这种方式比较适合初始化配置,如果需要动态更新,可能需要结合其他工具。
别名解析配置详解
除了基本的CNAME记录,还有一些高级的别名配置技巧。
多级别名:
www IN CNAME web.example.local.
blog IN CNAME www.example.local.
这样blog.example.local会先解析到www.example.local,然后再解析到web.example.local对应的IP。不过要注意,CNAME链不要搞得太长,会影响解析性能。
条件别名:
虽然BIND不直接支持条件别名,但可以通过视图(view)功能实现:
view "internal" {
match-clients { 192.168.1.0/24; };
zone "example.local" {
type master;
file "example.local.internal";
};
};
view "external" {
match-clients { any; };
zone "example.local" {
type master;
file "example.local.external";
};
};
然后在不同的区域文件中配置不同的别名:
内网区域文件(example.local.internal):
api IN CNAME internal-api.example.local.
外网区域文件(example.local.external):
api IN CNAME external-api.example.local.
这样内网用户访问api.example.local会指向内网服务器,外网用户会指向外网服务器。
动态别名更新:
如果需要经常更新别名配置,可以启用动态更新功能:
zone "example.local" IN {
type master;
file "example.local.zone";
allow-update { key "update-key"; };
};
然后用nsupdate命令动态添加或删除记录:
nsupdate -k /etc/named/update.key << EOF
server 192.168.1.100
zone example.local
update add temp.example.local 300 CNAME web.example.local
send
EOF
这种方式特别适合需要程序化管理DNS记录的场景,比如容器化环境中的服务发现。
配置转发器
如果不想让DNS服务器直接进行递归查询,可以配置转发器,将查询转发给上游DNS服务器:
options {
forwarders {
8.8.8.8;
8.8.4.4;
};
forward only;
};
配置访问控制列表
为了安全考虑,可以配置ACL来限制访问:
acl "trusted" {
192.168.1.0/24;
10.0.0.0/8;
};
options {
allow-query { trusted; };
allow-recursion { trusted; };
};
启用查询日志
为了方便排查问题,可以启用查询日志:
logging {
channel query_log {
file "/var/log/named/query.log";
severity info;
print-time yes;
print-category yes;
};
category queries { query_log; };
};
记得创建日志目录并设置权限:
mkdir /var/log/named
chown named:named /var/log/named
监控和维护
DNS服务器搭建完成后,日常的监控和维护也很重要。
我通常会写个简单的脚本来监控DNS服务的状态:
#!/bin/bash
DNS_SERVER="192.168.1.100"
TEST_DOMAIN="web.example.local"
if nslookup $TEST_DOMAIN $DNS_SERVER > /dev/null 2>&1; then
echo "DNS server is working fine"
else
echo "DNS server is not responding"
systemctl restart named
fi
把这个脚本加到crontab里,每5分钟执行一次,基本上就能保证DNS服务的稳定性了。
另外,定期检查日志文件也很重要,特别是/var/log/messages和named的专用日志,能及时发现一些潜在问题。
区域文件的备份也不能忘记,虽然DNS的配置相对简单,但是一旦出问题重新配置还是很麻烦的。我一般会把整个/var/named目录定期打包备份。
总结
搭建DNS服务器其实并不复杂,关键是要理解DNS的工作原理和BIND的配置逻辑。从最初的HOSTS.TXT文件到现在的分布式DNS系统,这套架构经过了几十年的发展和完善,已经非常成熟稳定了。
对于企业内网来说,自建DNS服务器确实能带来很多好处:解决内网域名解析问题、提高查询速度、实现访问控制等等。当然,如果只是个人使用或者小规模应用,直接用公共DNS服务器可能更简单一些。
整个搭建过程中,配置文件的语法检查很重要,BIND对语法要求比较严格,一个小错误就可能导致服务启动失败。还有就是文件权限问题,这个经常被忽略,但确实很关键。
DNS作为互联网的基础设施,虽然平时不太显眼,但是一旦出问题影响面很大。所以在生产环境中,建议做好冗余和监控,确保服务的高可用性。下次来讲讲如何搭建DNS主从架构
希望这篇文章能帮助大家更好地理解和使用DNS服务器。如果在搭建过程中遇到问题,欢迎留言讨论。网络技术这东西,实践出真知,多动手试试总是没错的。
如果这篇文章对你有帮助,别忘了点赞转发支持一下!想了解更多运维实战经验和技术干货,记得关注微信公众号@运维躬行录,领取学习大礼包!!!我会持续分享更多接地气的运维知识和踩坑经验。让我们一起在运维这条路上互相学习,共同进步!
公众号:运维躬行录
个人博客:躬行笔记