为什么运维要会 DNS
DNS 是所有网络服务的寻址根基——域名解析慢/出错,再快的应用层也白搭。内网环境至少三个场景离不开自建 DNS:
| 场景 | 用 BIND 的理由 |
|---|---|
| 内网域名解析(service.internal) | 避免 hosts 文件散落维护,集中管理 |
| 缓存加速上游 | ISP DNS 慢/被劫持时,本地缓存 + 转发更可控 |
| 分区解析(split-horizon) | 内外网同名域名指向不同 IP,简化配置 |
BIND9 是事实标准(ISC 出品),稳定可靠、文档丰富,CentOS/Ubuntu 都预装。
安装与目录结构
# CentOS/RHEL
yum install -y bind bind-utils
systemctl enable --now named
# Ubuntu/Debian
apt install -y bind9 bind9utils dnsutils
systemctl enable --now bind9
关键目录(以 CentOS 为例,Ubuntu 路径略有不同):
| 路径 | 用途 |
|---|---|
/etc/named.conf |
主配置文件 |
/etc/named.rfc1912.zones |
zone 声明(推荐拆分到此文件) |
/var/named/ |
zone 数据文件目录 |
/etc/rndc.key |
rndc 控制密钥 |
/var/log/named/ |
日志目录(需手动配置) |
Ubuntu 的配置在
/etc/bind/,zone 数据在/var/cache/bind/,语法一致。
主配置文件 named.conf
options {
listen-on port 53 { 127.0.0.1; 192.168.1.10; }; # 监听地址
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";
# 递归查询白名单(谁可以用本机做递归)
allow-query { localhost; 192.168.0.0/16; 10.0.0.0/8; };
# 递归开关
recursion yes;
# 上游转发(缓存服务器模式推荐)
forwarders {
223.5.5.5; # 阿里 DNS
119.29.29.29; # 腾讯 DNS
8.8.8.8; # Google DNS
};
forward first; # 先转发,失败再自己递归
dnssec-enable yes;
dnssec-validation yes;
dnssec-lookaside auto;
bindkeys-file "/etc/named.iscdlv.key";
managed-keys-directory "/var/named/dynamic";
pid-file "/run/named/named.pid";
session-keyfile "/run/named/session.key";
};
logging {
channel default_debug {
file "data/named.run";
severity dynamic;
};
channel query_log {
file "data/query.log" versions 3 size 50m;
severity info;
print-time yes;
print-category yes;
};
category queries { query_log; };
category lame-servers { null; }; # 忽略 lame server 噪音日志
};
include "/etc/named.rfc1912.zones";
include "/etc/named.root.key";
⚠️ 避坑 #1:
allow-query一定要限制为可信网段。设为any会被公网滥用做递归放大攻击(开放解析器是 DDoS 放大攻击的常见源)。
Zone 声明与数据文件
正向 zone 声明
编辑 /etc/named.rfc1912.zones:
zone "internal.example.com" IN {
type master;
file "internal.example.com.zone";
allow-update { none; }; # 动态更新关闭(推荐)
allow-transfer { 192.168.1.11; }; # 允许从服务器 zone 传输
};
zone "1.168.192.in-addr.arpa" IN { # 反向解析 zone
type master;
file "192.168.1.zone";
allow-update { none; };
};
正向 zone 数据文件
创建 /var/named/internal.example.com.zone:
$TTL 86400
@ IN SOA ns1.internal.example.com. admin.internal.example.com. (
2026071701 ; Serial(YYYYMMDDNN 格式,每次修改必须 +1)
3600 ; Refresh(从服务器检查间隔)
1800 ; Retry(失败重试)
604800 ; Expire(从服务器数据过期时间)
86400 ; Minimum TTL(NXDOMAIN 缓存时间)
)
IN NS ns1.internal.example.com.
IN NS ns2.internal.example.com.
IN MX 10 mail.internal.example.com.
IN MX 20 mail2.internal.example.com.
ns1 IN A 192.168.1.10
ns2 IN A 192.168.1.11
mail IN A 192.168.1.20
mail2 IN A 192.168.1.21
www IN A 192.168.1.100
api IN A 192.168.1.101
db IN A 192.168.1.50
; 别名记录
web IN CNAME www.internal.example.com.
docs IN CNAME web.internal.example.com.
; TXT 记录(SPF 等)
@ IN TXT "v=spf1 mx -all"
反向 zone 数据文件
创建 /var/named/192.168.1.zone:
$TTL 86400
@ IN SOA ns1.internal.example.com. admin.internal.example.com. (
2026071701
3600
1800
604800
86400
)
IN NS ns1.internal.example.com.
IN NS ns2.internal.example.com.
10 IN PTR ns1.internal.example.com.
11 IN PTR ns2.internal.example.com.
20 IN PTR mail.internal.example.com.
100 IN PTR www.internal.example.com.
101 IN PTR api.internal.example.com.
⚠️ 避坑 #2:反向 zone 名称是反序 IP +
.in-addr.arpa。192.168.1.0/24 →1.168.192.in-addr.arpa。写反了查不到 PTR,会导致很多服务(邮件、SSH 反查、NFS)出问题。
配置语法检查与加载
每次改完配置必须检查语法再 reload:
# 检查主配置
named-checkconf /etc/named.conf
# 检查 zone 数据文件
named-checkzone internal.example.com /var/named/internal.example.com.zone
named-checkzone 1.168.192.in-addr.arpa /var/named/192.168.1.zone
# 全部 OK 后 reload(不中断服务)
rndc reload
# 或 systemctl reload named
# 查看 zone 是否加载成功
rndc status
dig @127.0.0.1 www.internal.example.com
dig @127.0.0.1 -x 192.168.1.100
主从复制
生产 DNS 必须主从双机,单机故障 = 整个内网域名瘫痪。
从服务器配置
zone "internal.example.com" IN {
type slave;
masters { 192.168.1.10; }; # 主服务器 IP
file "slaves/internal.example.com.zone";
allow-notify { 192.168.1.10; }; # 允许主服务器 notify
};
zone "1.168.192.in-addr.arpa" IN {
type slave;
masters { 192.168.1.10; };
file "slaves/192.168.1.zone";
};
⚠️ 避坑 #3:从服务器的 zone 文件目录(
/var/named/slaves/)必须让named用户可写,否则 AXFR 传输后无法落盘。chown named:named /var/named/slaves/。
主服务器修改 zone 后递增 Serial,执行 rndc reload,主服务器自动发送 NOTIFY 给从服务器触发增量传输。
Zone 传输安全加固
# 主服务器只允许指定从服务器拉 zone
allow-transfer { 192.168.1.11; };
# 更严格:用 TSIG 密钥认证
tsig-keygen -a HMAC-SHA256 transfer-key > /etc/transfer.key
chown root:named /etc/transfer.key
chmod 640 /etc/transfer.key
# 主配置引入
include "/etc/transfer.key";
zone "internal.example.com" IN {
type master;
file "internal.example.com.zone";
allow-transfer { key "transfer-key"; };
also-notify { 192.168.1.11 key "transfer-key"; };
};
# 从服务器配置对应密钥
include "/etc/transfer.key";
server 192.168.1.10 { keys { "transfer-key"; }; };
Split-Horizon(分区解析)
同一域名,内网解析到内网 IP,外网解析到公网 IP:
# 用 view 实现
acl "internal" { 192.168.0.0/16; 10.0.0.0/8; 127.0.0.1; };
view "internal_view" {
match-clients { "internal"; };
zone "example.com" IN {
type master;
file "internal/example.com.zone"; # 内网版本
};
zone "." IN {
type hint;
file "/var/named/named.ca";
};
};
view "external_view" {
match-clients { any; }; # 其余所有
zone "example.com" IN {
type master;
file "external/example.com.zone"; # 公网版本
};
recursion no; # 外部不允许递归
};
⚠️ 避坑 #4:使用
view后,所有 zone 必须在 view 内声明,不能再放到 view 外。否则配置报错。
DNSSEC 签名
DNSSEC 给 zone 数据做数字签名,防止 DNS 响应被篡改(缓存投毒):
# 1. 生成 KSK(密钥签名密钥)和 ZSK(zone 签名密钥)
cd /var/named
dnssec-keygen -a ECDSAP256SHA256 -b 256 -n ZONE internal.example.com
dnssec-keygen -a ECDSAP256SHA256 -b 384 -f KSK -n ZONE internal.example.com
# 2. 在 zone 文件中引入公钥
$INCLUDE Kinternal.example.com.+013+12345.key
$INCLUDE Kinternal.example.com.+013+67890.key
# 3. 签名 zone
dnssec-signzone -o internal.example.com -N increment \
-k Kinternal.example.com.+013+67890.key \
internal.example.com.zone \
Kinternal.example.com.+013+12345.key
# 4. 修改 named.conf 指向签名后的文件
zone "internal.example.com" IN {
type master;
file "internal.example.com.zone.signed"; # 注意 .signed 后缀
dnssec-enable yes;
inline-signing yes;
};
DNSSEC 的坑:
- 签名后 zone 文件变成二进制
.signed,不能直接编辑。改原始.zone后重新签名。 - KSK 轮换时必须把 DS 记录注册到上级 zone(公网域名要找注册商)。
- 时间必须同步(NTP),否则签名验证失败。
性能调优
# 系统层面(关键:提高 UDP 缓冲区)
sysctl -w net.core.rmem_max=4194304
sysctl -w net.core.rmem_default=4194304
sysctl -w net.core.netdev_max_backlog=2500
# named.conf 调优
options {
max-cache-size 512m; # 缓存上限
max-cache-ttl 604800; # 最大缓存 TTL(1 周)
max-ncache-ttl 10800; # NXDOMAIN 缓存(3 小时)
cleaning-interval 120; # 清理周期
lame-ttl 600; # lame server 缓存
# 多线程
tcp-clients 100; # TCP 连接上限
recursive-clients 1000; # 递归客户端上限
clients-per-query 10; # 单查询并发上限
};
高并发场景考虑用 unbound(更轻量的纯递归缓存服务器),BIND 适合权威 DNS + 中小规模递归。
监控与日志
# 查询日志(已在 named.conf 配置)
tail -f /var/named/data/query.log
# 统计信息
rndc stats
cat /var/named/data/named_stats.txt
# 关键监控指标
# - QPS(每秒查询数)
# - 缓存命中率(>90% 为健康)
# - 递归延迟(<50ms 为优)
# - zone 传输状态(主从一致)
# - DNSSEC 验证失败数(应 = 0)
Prometheus + bind_exporter 监控模板:
# bind_exporter 暴露 9119 端口
# 关键告警规则
- alert: DnsHighQueryLatency
expr: histogram_quantile(0.95, bind_resolver_query_duration_seconds_bucket) > 0.5
for: 5m
labels: { severity: warning }
- alert: DnsZoneTransferFailed
expr: increase(bind_zone_transfer_failure_total[10m]) > 0
labels: { severity: critical }
- alert: DnsServiceDown
expr: up{job="bind"} == 0
for: 1m
labels: { severity: critical }
常见故障排查
| 故障 | 诊断 | 解决 |
|---|---|---|
| 解析超时 | dig @127.0.0.1 domain +trace |
检查上游 forwarder 可达性、防火墙 53 端口 |
| zone 不生效 | rndc reload 后 dig @127.0.0.1 domain |
检查 Serial 是否递增、named-checkzone 语法 |
| 反向解析失败 | dig -x 192.168.1.100 |
反向 zone 名是否反序、PTR 记录是否齐全 |
| 从服务器数据不更新 | rndc retransfer 手动触发 |
检查 allow-transfer、Serial 是否大于从服务器 |
| DNSSEC 验证失败 | dig +dnssec domain 看 AD 标志 |
检查时间同步、KSK/ZSK 是否过期、DS 是否注册上级 |
| 缓存投毒嫌疑 | rndc flushname domain 清缓存 |
开启 DNSSEC 验证、限制 allow-query |
| 日志爆炸 | 查 query.log 大小 |
配置 versions 3 size 50m 滚动 |
| SERVFAIL 响应 | dig +trace domain 逐级排查 |
上游故障 / DNSSEC 签名失效 / zone 配置错 |
⚠️ 避坑 #5:
rndc flush会清空整个缓存,生产环境高峰期慎用。用rndc flushname domain只清指定域名的缓存。
十条避坑清单
- allow-query 必须限网段:开放递归器会被滥用做 DDoS 放大攻击,
any是灾难 - Serial 改了必须 +1:不递增从服务器不更新;推荐
YYYYMMDDNN格式好维护 - 反向 zone 名反序写:192.168.1.0/24 →
1.168.192.in-addr.arpa,写反了 PTR 全失效 - FQDN 结尾加点:zone 文件里
ns1.internal.example.com.(末尾点)才是绝对域名,少了点会拼接当前 zone 名 - 从服务器目录权限:
/var/named/slaves/必须 named 用户可写,否则 AXFR 无法落盘 - view 内必须包所有 zone:用了 view 就不能再有 view 外的 zone 声明
- DNSSEC 时间必须同步:签名依赖时间戳,NTP 不同步 = 验证全部失败
- forward first vs forward only:
forward first失败回退自递归(更可靠);forward only全依赖上游(上游挂了就全挂) - rndc reload 不中断服务:改配置用
rndc reload,不要systemctl restart(会断开所有连接) - TTL 别设太短:内网 zone TTL 86400(1天)够用;TTL 太短 = 缓存频繁失效 = 上游压力 + 解析变慢
总结
BIND DNS 运维核心要点:
- allow-query 限网段是安全底线——开放递归器是公害
- 主从双机 + TSIG 认证——单机 DNS 不可接受
- Serial 递增 + rndc reload——改 zone 数据的标准流程
- 反向 zone 与正向配对——很多服务依赖 PTR 反查
- DNSSEC 看场景上——公网权威 zone 推荐,内网可不上
掌握 zone 声明、数据文件编写、主从复制、split-horizon 这四块,BIND DNS 运维就能覆盖企业内网绝大多数需求。