前言
DNS(Domain Name System,域名系统)是互联网最基础的服务之一,它的核心作用只有一个:把人类易记的域名转换成机器可读的 IP 地址 。没有 DNS,你访问网站就得记 142.250.70.78 而不是 google.com。
本文从实战出发,亲手搭建一套完整的 DNS 服务体系,涵盖:
- 高速缓存 DNS ------ 加速域名解析,减少出口流量
- 正向解析(A 记录) ------ 域名 → IPv4 地址
- 邮件交换记录(MX 记录) ------ 指定邮件服务器
- 反向解析(PTR 记录) ------ IP 地址 → 域名
- 多向解析(View 视图) ------ 不同网段返回不同结果
- 主从同步(辅助 DNS) ------ 高可用、自动数据同步
一、高速缓存 DNS(Caching DNS)
1.1 什么是缓存 DNS?
当客户端向缓存 DNS 发起查询时,缓存 DNS 替它向互联网上的权威 DNS 服务器递归查询,拿到结果后缓存到本地。下次同样的查询直接返回缓存结果,大幅提高解析速度。
工作流程图解:
客户端 → 缓存DNS → 根DNS → .com DNS → baidu.com DNS(权威)
↓ ↑
缓存 ← ← ← ← ← ← ← ← ← ← ← ← ← ← ← ← ← ←
1.2 实验环境
| 主机名 | IP | 用途 |
|---|---|---|
| red(DNS 服务器) | 192.168.17.138 | 部署缓存 DNS |
| ub(客户端 1) | 192.168.17.131 | 测试查询 |
| ub2(客户端 2) | 192.168.17.137 | 测试查询 |
1.3 安装 BIND 并启动服务
bash
# 安装 BIND 软件包(bind.x86_64 是 Linux 上最主流的 DNS 服务器软件)
[root@bogon ~]# dnf install bind.x86_64 -y
# 启用并立即启动 named 服务(named = name daemon,BIND 的守护进程名)
[root@bogon ~]# systemctl enable --now named
1.4 修改主配置文件
bash
[root@bogon ~]# vim /etc/named.conf
nginx
options {
listen-on port 53 { any; }; # 监听所有网卡的 53 端口(默认只监 listening 127.0.0.1)
listen-on-v6 port 53 { ::1; }; # IPv6 保持监听本机
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";
secroots-file "/var/named/data/named.secroots";
recursing-file "/var/named/data/named.recursing";
allow-query { any; }; # 允许所有客户端向本机发起 DNS 查询
forwarders { 144.144.144.144; }; # 指定上游 DNS 服务器(缓存未命中时向谁转发)
};

重点参数说明:
| 参数 | 作用 |
|---|---|
listen-on port 53 { any; } |
默认只监听 127.0.0.1,必须改为 any 才能对外服务 |
allow-query { any; } |
访问控制,默认只允许本机,改为 any 开放给局域网 |
forwarders |
上游 DNS,缓存没命中的请求会转发给这个地址(可根据你的网络环境设为 114.114.114.114 或 8.8.8.8) |
1.5 重启并验证服务状态
bash
[root@bogon ~]# systemctl restart named
# 查看 named 是否正常监听端口
[root@bogon ~]# netstat -antlupe | grep named
tcp 0 0 127.0.0.1:53 0.0.0.0:* LISTEN 25 89111 35133/named
tcp 0 0 192.168.17.138:53 0.0.0.0:* LISTEN 25 89115 35133/named

看到 192.168.17.138:53 在 LISTEN,说明 DNS 服务器已正常对外提供服务。
1.6 客户端测试
在客户端 ub(192.168.17.131)上测试:
bash
# dig 是最常用的 DNS 查询工具,@ 指定要查询的 DNS 服务器
root@qwe-virtual-machine:~# dig www.baidu.com @192.168.17.138

二、DNS 权威解析部署
缓存 DNS 是"代理查询",而下面要搭建的是权威 DNS------ 自己管理一个域名,自己决定域名指向哪个 IP。
2.1 正向解析(A 记录)
作用:将域名解析为 IPv4 地址,这是 DNS 最基础、最常用的功能。
2.1.1 添加区域声明
首先要告诉 named:"我负责管理 qwer.com 这个域"。
bash
[root@bogon ~]# vim /etc/named.rfc1912.zones
在文件末尾添加:
nginx
zone "qwer.com" IN {
type master; # 类型为主 DNS 服务器
file "qwer.com.zone"; # 区域数据文件的路径(相对于 /var/named/)
allow-update { none; }; # 是否允许动态更新(这里不允许,安全考虑)
};

配置参数解读:
| 参数 | 含义 |
|---|---|
type master |
本机是这个域的主 DNS,数据由本机管理 |
file "qwer.com.zone" |
指向 /var/named/qwer.com.zone 这个区域文件 |
allow-update { none; } |
禁止外部动态更新 DNS 记录(防止被篡改) |
2.1.2 创建区域数据文件
bash
[root@bogon ~]# cd /var/named/
# -p 表示保留原文件的权限和属组(BIND 对文件权限十分敏感)
[root@bogon named]# cp -p named.localhost qwer.com.zone
[root@bogon named]# vim qwer.com.zone
dns
$TTL 1D
@ IN SOA admin.qwer.com. root.qwer.com. (
0 ; serial 序列号,从 0 开始,每次修改递增
1D ; refresh 辅 DNS 刷新间隔(1 天)
1H ; retry 刷新失败后重试间隔(1 小时)
1W ; expire 辅 DNS 数据过期时间(1 周)
3H ) ; minimum 否定缓存 TTL(3 小时)
NS dns.qwer.com. ; 声明权威 DNS 服务器为 dns.qwer.com
dns A 192.168.17.138 ; dns.qwer.com 指向本机 IP
www A 192.168.17.138 ; www.qwer.com 指向本机 IP

逐行详解:
| 记录 | 含义 |
|---|---|
$TTL 1D |
默认 TTL(Time To Live)为 1 天,告诉其他 DNS 缓存这条记录多久 |
@ |
代表当前域名(qwer.com),是 zone 语句里域名的简写 |
SOA |
Start of Authority,起始授权记录,每个域必须有且只有一条 |
NS dns.qwer.com. |
Nameserver 记录,指定该域的权威 DNS 是哪个 |
dns A 192.168.17.138 |
A 记录,dns.qwer.com 指向 192.168.17.138 |
www A 192.168.17.138 |
A 记录,www.qwer.com 指向 192.168.17.138 |
⚠️ 注意域名结尾的点 :
dns.qwer.com.最后一个点表示这是一个"完全限定域名(FQDN)",不加点的话 BIND 会自动追加上 zone 名变成dns.qwer.com.qwer.com。
2.1.3 重启 named 并测试
bash
[root@bogon named]# systemctl restart named
客户端测试:
bash
root@qwe-virtual-machine:~# dig www.qwer.com @192.168.17.138
;; ANSWER SECTION:
www.qwer.com. 86400 IN A 192.168.17.138

成功! www.qwer.com 成功解析到 192.168.17.138。
2.2 邮件交换记录(MX 记录)
作用:MX 记录告诉发件服务器:"发往 user@qwer.com 的邮件应该投递到哪个邮件服务器"。
MX 记录 = 邮件服务器的"导航牌"。
2.2.1 添加 MX 记录
bash
[root@bogon named]# vim qwer.com.zone
在文件末尾添加一行:
dns
qwer.com. MX 5 192.168.17.138.
格式拆解:
域名 类型 优先级 邮件服务器域名
qwer.com. MX 5 192.168.17.138.
优先级(Preference)说明:
-
数字越小优先级越高
-
如果配置了多条 MX:
qwer.com. MX 5 mail1.qwer.com. qwer.com. MX 10 mail2.qwer.com.发件方会优先尝试
mail1(优先级 5),失败后才尝试mail2(优先级 10) -
相同优先级可在多台服务器间负载均衡
⚠️ MX 记录的目标必须是域名,虽然上面例子直接写了 IP(简单演示),但生产环境标准的 MX 记录应该指向一个 A 记录域名,再通过 A 记录指向 IP:
qwer.com. MX 5 mail.qwer.com. mail A 192.168.17.138
重启并测试:
bash
[root@bogon named]# systemctl restart named
bash
root@qwe-virtual-machine:~# dig qwer.com MX @192.168.17.138

2.3 反向解析(PTR 记录)
作用:PTR 记录是 A 记录的"反向操作"------给定一个 IP 地址,查出它对应的域名。
为什么需要反向解析?
- 反垃圾邮件:接收方检查发件 IP 的 PTR 记录是否匹配发件域名。如果不匹配,邮件可能被标记为垃圾邮件或被拒收
- 日志审计:根据 IP 反查域名,更容易读懂访问来源
- 访问控制:某些服务(如 SSH、FTP)可根据 PTR 限制访问
2.3.1 添加反向区域声明
反向解析的 zone 名是 IP 反转 + .in-addr.arpa:
- 网段
192.168.17.0/24→ 反转17.168.192→ 得到17.168.192.in-addr.arpa
bash
[root@localhost ~]# vim /etc/named.rfc1912.zones
nginx
zone "17.168.192.in-addr.arpa" IN {
type master;
file "192.168.17.ptr";
allow-update { none; };
};
2.3.2 创建反向区域文件
bash
[root@localhost ~]# cd /var/named/
# -p 保留权限,named.loopback 是反向区域文件的模板
[root@localhost named]# cp -p named.loopback 192.168.17.ptr
[root@localhost named]# vim 192.168.17.ptr
dns
$TTL 1D
@ IN SOA admin.qwer.com. root.qwer.com. (
0 ; serial
1D ; refresh
1H ; retry
1W ; expire
3H ) ; minimum
NS dns.qwer.com. ; 权威 DNS 服务器
dns A 192.168.17.138 ; 必须有一条 A 记录指向 DNS 本身
111 PTR haha.qwer.com. ; 192.168.17.111 → haha.qwer.com
重点说明:
111是 IP 地址192.168.17.111的最后一段,BIND 会自动拼接成111.17.168.192.in-addr.arpaPTR haha.qwer.com.------ 表示 IP192.168.17.111解析为域名haha.qwer.com.- 反向区域文件中必须有对应的 A 记录 (
dns A 192.168.17.138),否则 BIND 会报错
2.3.3 测试反向解析
bash
root@qwe-virtual-machine:~# dig -x 192.168.17.111 @192.168.17.138
;; ANSWER SECTION:
111.17.168.192.in-addr.arpa. 86400 IN PTR haha.qwer.com.

参数 -x 是 dig 的反向查询快捷方式,相当于帮你把 IP 反转成 in-addr.arpa 格式。
三、DNS 多向解析
3.1 什么是多向解析?
同一个 DNS 服务器,根据客户端来源 IP 的不同,返回不同的解析结果。这在实际生产环境中非常实用:
| 客户端网段 | 查询 www.example.com |
返回的 IP | 目的 |
|---|---|---|---|
| 内网 16 网段(192.168.16.x) | → | 10.16.0.10 |
访问内网服务器 |
| 内网 15 网段(192.168.15.x) | → | 10.15.0.10 |
访问就近的内网服务器 |
| 外网(互联网) | → | 1.2.3.4 |
访问公网入口 |
为什么要这么做?
- 内网用户不需要绕到外网再回来,直接解析到内网 IP,速度快、流量省
- 不同网段可以指向不同服务器(负载分担 / 就近接入)
- 外网用户解析到公网入口,走正常的外网访问路径
3.2 实验环境
| 主机名 | IP | 用途 |
|---|---|---|
| DNS 服务器 | 192.168.17.1 | 部署多向解析(View),同时服务 16 和 15 网段 |
| 客户端 A | 192.168.16.100 | 16 网段用户 |
| 客户端 B | 192.168.15.100 | 15 网段用户 |
3.3 配置多向解析
3.3.1 主配置文件启用 view
BIND 的 view 配置写在 /etc/named.conf 中。注意:一旦使用了 view,所有的 zone 都必须放在 view 内部。
bash
[root@dns ~]# vim /etc/named.conf
整体结构如下:
nginx
options {
listen-on port 53 { any; };
listen-on-v6 port 53 { ::1; };
directory "/var/named";
allow-query { any; };
};
# ---------- 视图 1:16 网段 ----------
view "lan16" {
match-clients { 192.168.16.0/24; }; # 匹配来自 16 网段的请求
zone "." IN {
type hint;
file "named.ca"; # 根域提示(每个 view 都要有)
};
zone "example.com" IN {
type master;
file "example.com.zone.lan16"; # 16 网段专用的区域文件
};
};
# ---------- 视图 2:15 网段 ----------
view "lan15" {
match-clients { 192.168.15.0/24; }; # 匹配来自 15 网段的请求
zone "." IN {
type hint;
file "named.ca";
};
zone "example.com" IN {
type master;
file "example.com.zone.lan15"; # 15 网段专用的区域文件
};
};
# ---------- 视图 3:其他(外网) ----------
view "external" {
match-clients { any; }; # 其他所有来源
zone "." IN {
type hint;
file "named.ca";
};
zone "example.com" IN {
type master;
file "example.com.zone.external"; # 外网看到的区域文件
};
};
配置详解:
| 配置 | 作用 |
|---|---|
view "lan16" |
视图名称,可自定义,无实际功能影响 |
match-clients { 192.168.16.0/24; } |
匹配规则------只有源 IP 匹配的客户端才会进入这个 view |
match-clients { any; } |
兜底匹配------必须放在最后,匹配所有不满足上面条件的客户端 |
| 多个 view 的匹配顺序 | BIND 从上到下依次匹配,命中了就进入,不再继续 |
3.3.2 创建三个区域文件
每个 view 引用了不同的区域文件,现在分别创建:
16 网段区域文件------给内网 16 网段的用户看:
bash
[root@dns ~]# cd /var/named/
[root@dns named]# vim example.com.zone.lan16
dns
$TTL 1D
@ IN SOA admin.example.com. root.example.com. (
0 ; serial
1D ; refresh
1H ; retry
1W ; expire
3H ) ; minimum
NS dns.example.com.
dns A 192.168.17.1
www A 10.16.0.10 # 16 网段用户访问 www,返回内网 10.16.0.10
15 网段区域文件------给内网 15 网段的用户看:
bash
[root@dns named]# vim example.com.zone.lan15
dns
$TTL 1D
@ IN SOA admin.example.com. root.example.com. (
0 ; serial
1D ; refresh
1H ; retry
1W ; expire
3H ) ; minimum
NS dns.example.com.
dns A 192.168.17.1
www A 10.15.0.10 # 15 网段用户访问 www,返回 10.15.0.10
外网区域文件------给互联网用户看:
bash
[root@dns named]# vim example.com.zone.external
dns
$TTL 1D
@ IN SOA admin.example.com. root.example.com. (
0 ; serial
1D ; refresh
1H ; retry
1W ; expire
3H ) ; minimum
NS dns.example.com.
dns A 192.168.17.1
www A 1.2.3.4 # 外网用户访问 www,返回公网 IP
⚠️ 注意文件名:BIND 对区域文件的名称没有强制要求,但建议用有意义的命名区分不同视图,避免混淆。
3.3.3 重启 named
bash
[root@dns named]# systemctl restart named
3.4 多向解析的核心要点
| 要点 | 说明 |
|---|---|
| 匹配顺序 | view 按 named.conf 中定义的顺序从上到下匹配,命中即止 |
| 兜底 view | 最后一个 view 必须用 match-clients { any; },防止没有匹配的客户端被拒绝 |
| 根域提示 | 每个 view 里都要配 zone "." { type hint; file "named.ca"; },否则递归查询失效 |
| 区域文件独立 | 不同 view 的区域文件互不干扰,可以同名区域不同内容 |
| 安全隔离 | 内网用户解析不到公网 IP,外网用户也看不到内网拓扑 |
四、主从 DNS 同步(辅助 DNS)
4.1 为什么需要主从架构?
单台 DNS 服务器会面临单点故障------服务器宕机,整个域名解析就瘫痪。主从架构解决了这个问题:
- 主 DNS(Master):数据源,可读可写
- 从 DNS(Slave):从主 DNS 同步数据,只读
- 客户端可以配置两个 DNS,一个宕机自动切到另一个
4.2 实验环境
| 主机名 | IP | 角色 |
|---|---|---|
| node1 | 172.25.254.10 | DNS 主 服务器(Master),拥有 timinglee.org 的完整数据 |
| node2 | 172.25.254.20 | DNS 从服务器(Slave),从 node1 同步数据 |
4.3 配置主 DNS(Node1)
4.3.1 安装 BIND
bash
[root@node1 ~]# dnf install bind -y
4.3.2 修改主配置文件
bash
[root@node1 ~]# vim /etc/named.conf
nginx
options {
listen-on port 53 { any; }; # 监听所有网卡
listen-on-v6 port 53 { ::1; };
directory "/var/named";
allow-query { any; }; # 允许所有客户端查询
recursion yes;
};
4.3.3 添加主区域声明
bash
[root@node1 ~]# vim /etc/named.rfc1912.zones
nginx
zone "timinglee.org" IN {
type master;
file "timinglee.org.zone";
allow-update { none; };
allow-transfer { 172.25.254.20; }; # 仅允许从 DNS node2 来同步数据
};
⚠️
allow-transfer是关键:如果不加这一行,从 DNS 请求区域传输时会被拒绝,无法同步任何数据。
4.3.4 创建区域文件
bash
[root@node1 ~]# cd /var/named/
[root@node1 named]# cp -p named.localhost timinglee.org.zone
[root@node1 named]# vim timinglee.org.zone
dns
$TTL 1D
@ IN SOA admin.timinglee.org. root.timinglee.org. (
2026071901 ; serial
1D ; refresh
1H ; retry
1W ; expire
3H ) ; minimum
NS dns.timinglee.org.
dns A 172.25.254.10
timinglee A 172.25.254.254
bash
[root@node1 named]# systemctl restart named
4.4 配置从 DNS(Node2)
4.4.1 安装 BIND
bash
[root@node2 ~]# dnf install bind -y
4.4.2 修改主配置文件
从 DNS 的 named.conf 配置与主 DNS 类似,开放监听和查询权限:
bash
[root@node2 ~]# vim /etc/named.conf
nginx
options {
listen-on port 53 { any; };
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";
secroots-file "/var/named/data/named.secroots";
recursing-file "/var/named/data/named.recursing";
allow-query { any; };
};
4.4.3 添加从区域声明
bash
[root@node2 ~]# vim /etc/named.rfc1912.zones
nginx
zone "timinglee.org" IN {
type slave; # 类型为从 DNS
masters { 172.25.254.10; }; # 指定主 DNS 的 IP
file "slaves/timinglee.org.zone"; # 同步来的数据存放位置
};
file "slaves/timinglee.org.zone"------ 为什么是slaves/目录?因为 named 的 SELinux 策略只允许从 DNS 在slaves/目录下创建文件。
4.4.4 启动从 DNS
bash
[root@node2 ~]# systemctl enable --now named
4.5 测试主从同步是否成功
在**从 DNS(node2)**上查询 timinglee.org 域内的域名,看看数据是否同步过来了:
bash
[root@node2 ~]# dig timinglee.timinglee.org @172.25.254.20
; <<>> DiG 9.16.23-RH <<>> timinglee.timinglee.org @172.25.254.20
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1
;; ANSWER SECTION:
timinglee.timinglee.org. 86400 IN A 172.25.254.254
status: NOERROR + ANSWER SECTION 返回了正确的 IP 172.25.254.254,主从同步成功 🎉
此时查看从 DNS 的 slaves 目录,可以看到数据文件已被自动创建:
bash
[root@node2 ~]# ls -l /var/named/slaves/
-rw-r--r--. 1 named named 432 Jul 19 15:45 timinglee.org.zone
4.6 主 DNS 数据变更后自动通知从 DNS
问题 :默认配置下,主 DNS 修改了区域数据后,从 DNS 要等 1 天(refresh 字段) 才会去检查并同步。更新跟不上变化。
解决方案 :在主 DNS 上配置 also-notify,当数据变更时主动通知从 DNS 过来拉取最新数据。
4.6.1 主 DNS 添加通知配置
bash
[root@node1 named]# vim /etc/named.rfc1912.zones
nginx
zone "timinglee.org" IN {
type master;
file "timinglee.org.zone";
allow-update { none; };
allow-transfer { 172.25.254.20; }; # 允许从 DNS 同步
also-notify { 172.25.254.20; }; # 数据变更时主动通知这个从 DNS
};
4.6.2 修改区域文件并递增序列号
模拟一个变更------将 timinglee 的 A 记录从 172.25.254.254 改为 172.25.254.222:
bash
[root@node1 named]# vim /var/named/timinglee.org.zone
dns
$TTL 1D
@ IN SOA admin.timinglee.org. root.timinglee.org. (
2026071903 ; serial ← 关键:每次修改都要递增这个数字!
1D ; refresh
1H ; retry
1W ; expire
3H ) ; minimum
NS dns.timinglee.org.
dns A 172.25.254.10
timinglee A 172.25.254.222 ; 从 254 改为 222
timinglee.org. MX 5 172.25.254.10.
serial(序列号)为什么重要? 从 DNS 通过比较序列号来判断数据是否更新。主 DNS 序列号更大 → 从 DNS 发起同步。改了记录但忘记递增序列号,从 DNS 永远不会同步。
4.6.3 重启主 DNS
bash
[root@node1 named]# systemctl restart named
4.6.4 验证自动同步
在从 DNS(Node2)上再次查询,此刻从 DNS 没有做任何操作,但数据已经刷新:
bash
[root@node2 ~]# dig timinglee.timinglee.org @172.25.254.20
;; ANSWER SECTION:
timinglee.timinglee.org. 86400 IN A 172.25.254.222
IP 从原来的 172.25.254.254 更新成了 172.25.254.222,also-notify 实时同步成功!
五、知识点总结
DNS 记录类型速查表
| 记录类型 | 方向 | 格式示例 | 用途 |
|---|---|---|---|
| A | 域名 → IPv4 | www.example.com. A 192.168.1.10 |
网站访问基础 |
| AAAA | 域名 → IPv6 | www.example.com. AAAA 2001:db8::1 |
IPv6 访问 |
| MX | 域名 → 邮件服务器 | example.com. MX 10 mail.example.com. |
邮件路由 |
| PTR | IP → 域名 | 10.1.168.192.in-addr.arpa. PTR www.example.com. |
反查验证 |
| NS | 域名 → DNS 服务器 | example.com. NS ns1.example.com. |
声明权威 DNS |
| SOA | 起始授权 | @ SOA admin.example.com. root.example.com. (...) |
区域管理参数 |
| CNAME | 域名 → 别名 | www.example.com. CNAME example.com. |
域名别名 |
主从 DNS 同步流程
1. 管理员修改主 DNS 区域文件
2. 递增 serial 序列号
3. 重启 named
4. also-notify 主动通知从 DNS
5. 从 DNS 比较序列号,发现更新
6. 从 DNS 发起区域传输(AXFR/IXFR)
7. 从 DNS 数据同步完成