在 CentOS 7 上搭建一个高可用的 BIND9 集群通常涉及以下几种关键技术和策略的组合:主从复制 (Master-Slave Replication)、负载均衡 (Load Balancing) 以及可能的浮动 IP (Floating IP) 或 Anycast。
我们将主要关注主从复制和负载均衡的实现,这是构成高可用 DNS 集群的核心。
场景假设:
- 域名:
mycluster.local
- 主 DNS 服务器 (Master):
- IP:
192.168.1.10
- Hostname:
dns-master.mycluster.local
- IP:
- 从 DNS 服务器 1 (Slave 1):
- IP:
192.168.1.11
- Hostname:
dns-slave1.mycluster.local
- IP:
- 从 DNS 服务器 2 (Slave 2):
- IP:
192.168.1.12
- Hostname:
dns-slave2.mycluster.local
- IP:
- 负载均衡器/虚拟 IP (VIP):
- IP:
192.168.1.100
(客户端将使用此 IP 作为 DNS 服务器)
- IP:
核心组件:
- BIND9 主从复制:
- 主服务器 (Master) 维护权威的区域数据文件。
- 从服务器 (Slaves) 定期从主服务器同步区域数据。
- 当主服务器上的区域数据更新时,它会通知从服务器进行更新。
- 负载均衡器:
- 将客户端的 DNS 请求分发到后端的多个 BIND 从服务器(或包括主服务器)。
- 可以使用硬件负载均衡器 (如 F5, Citrix ADC) 或软件负载均衡器 (如 HAProxy, Nginx, LVS)。
- 对于简单的 DNS 负载均衡,也可以使用 DNS 轮询 (Round Robin DNS),但这不提供故障检测和自动切换。
- (可选) Keepalived 实现 VIP 和健康检查:
- Keepalived 可以管理一个虚拟 IP (VIP),并在主负载均衡器节点故障时将其漂移到备用节点。
- 它可以对后端 BIND 服务器进行健康检查,如果某个 BIND 服务器故障,则将其从负载均衡池中移除。
步骤一:在所有节点上安装 BIND9
在 dns-master
, dns-slave1
, dns-slave2
上执行:
sudo yum update -y
sudo yum install -y bind bind-utils
步骤二:配置主 DNS 服务器 (dns-master
)
编辑
/etc/named.conf
(options
部分):sudo vi /etc/named.conf
options { listen-on port 53 { 127.0.0.1; 192.168.1.10; }; // 监听自己的IP 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"; allow-query { localhost; 192.168.1.0/24; }; // 允许内网查询 recursion no; // 作为权威服务器 allow-recursion { none; }; // 允许从服务器进行区域传送 allow-transfer { 192.168.1.11; 192.168.1.12; }; dnssec-enable yes; dnssec-validation yes; // 或 no,如果纯权威且不解析外部 pid-file "/run/named/named.pid"; // ... 其他默认选项 ... }; logging { channel default_debug { file "data/named.run"; severity dynamic; }; }; include "/etc/named.rfc1912.zones"; include "/etc/named.root.key";
编辑
/etc/named.rfc1912.zones
(定义区域):sudo vi /etc/named.rfc1912.zones
在文件末尾添加:
zone "mycluster.local" IN { type master; file "db.mycluster.local"; // 区域数据文件名 allow-update { none; }; // 主动通知从服务器有更新 also-notify { 192.168.1.11; 192.168.1.12; }; }; // 示例反向区域 zone "1.168.192.in-addr.arpa" IN { type master; file "db.192.168.1"; allow-update { none; }; also-notify { 192.168.1.11; 192.168.1.12; }; };
创建区域文件 (例如
/var/named/db.mycluster.local
):sudo vi /var/named/db.mycluster.local
$TTL 86400 @ IN SOA dns-master.mycluster.local. admin.mycluster.local. ( 2023072101 ; Serial (YYYYMMDDNN) 3600 ; Refresh 1800 ; Retry 604800 ; Expire 86400 ) ; Minimum TTL ; Name Servers for the zone (these will be the VIP or individual server IPs clients might use) ; For HA, clients should point to the VIP. These NS records are for delegation. @ IN NS dns-vip.mycluster.local. ; 或者直接写 VIP IP 的 PTR 记录对应的主机名 ; @ IN NS dns-master.mycluster.local. ; 也可以列出所有服务器 ; @ IN NS dns-slave1.mycluster.local. ; @ IN NS dns-slave2.mycluster.local. ; A Records for Name Servers (actual IPs) dns-master IN A 192.168.1.10 dns-slave1 IN A 192.168.1.11 dns-slave2 IN A 192.168.1.12 dns-vip IN A 192.168.1.100 ; VIP ; Other records server1 IN A 192.168.1.50 web IN CNAME server1.mycluster.local.
重要:
SOA
记录中的主NS应为dns-master.mycluster.local.
。NS
记录应指向客户端实际用于查询的DNS服务器名称。如果使用VIP,则指向VIP对应的主机名。如果客户端可能直接查询各个节点,则列出所有节点。- 每次修改区域文件后,务必增加 Serial 号码。
创建反向区域文件 (例如
/var/named/db.192.168.1
):
(内容类似,包含 PTR 记录,SOA 和 NS 记录与正向区域类似)设置区域文件权限:
sudo chown root:named /var/named/db.mycluster.local sudo chown root:named /var/named/db.192.168.1 sudo chmod 640 /var/named/db.mycluster.local sudo chmod 640 /var/named/db.192.168.1
检查配置并启动服务:
sudo named-checkconf /etc/named.conf sudo named-checkzone mycluster.local /var/named/db.mycluster.local sudo named-checkzone 1.168.192.in-addr.arpa /var/named/db.192.168.1 sudo systemctl start named sudo systemctl enable named sudo firewall-cmd --permanent --add-service=dns sudo firewall-cmd --reload
步骤三:配置从 DNS 服务器 (dns-slave1
和 dns-slave2
)
在 dns-slave1
和 dns-slave2
上执行以下操作 (配置相似,只需注意 IP 地址)。
编辑
/etc/named.conf
(options
部分):sudo vi /etc/named.conf
options { listen-on port 53 { 127.0.0.1; <slave_server_ip>; }; // 例如 192.168.1.11 for dns-slave1 listen-on-v6 port 53 { ::1; }; directory "/var/named"; // BIND 会将从主服务器同步的区域文件存放在这里 (通常在 slaves/ 子目录) 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"; allow-query { localhost; 192.168.1.0/24; }; recursion no; allow-recursion { none; }; // 从服务器不需要 allow-transfer,除非它也是其他从服务器的主 // allow-transfer { none; }; dnssec-enable yes; dnssec-validation yes; // 或 no pid-file "/run/named/named.pid"; // ... 其他默认选项 ... }; logging { channel default_debug { file "data/named.run"; severity dynamic; }; // 建议为从服务器添加 xfer (transfer) 日志 channel xfer_log { file "data/xfer.log" versions 3 size 5m; print-time yes; severity info; }; category xfer-in { xfer_log; }; category xfer-out { xfer_log; }; category notify { xfer_log; }; }; include "/etc/named.rfc1912.zones"; include "/etc/named.root.key";
编辑
/etc/named.rfc1912.zones
(定义区域为 slave):sudo vi /etc/named.rfc1912.zones
在文件末尾添加:
zone "mycluster.local" IN { type slave; file "slaves/db.mycluster.local"; // BIND 会自动创建此文件 masters { 192.168.1.10; }; // 指定主服务器的 IP 地址 // 可选:如果主服务器的 also-notify 可能被防火墙阻止,可以配置 allow-notify // allow-notify { 192.168.1.10; }; }; zone "1.168.192.in-addr.arpa" IN { type slave; file "slaves/db.192.168.1"; masters { 192.168.1.10; }; };
注意:
file
指令指定了从主服务器同步下来的区域数据副本的存储位置。BIND 会自动在directory
(即/var/named/
) 下创建slaves
子目录(如果不存在)并存储这些文件。检查配置并启动服务:
sudo named-checkconf /etc/named.conf sudo systemctl start named sudo systemctl enable named sudo firewall-cmd --permanent --add-service=dns sudo firewall-cmd --reload
验证区域传送:
- 在从服务器启动
named
后,稍等片刻。 - 查看从服务器的日志 (
sudo journalctl -u named -f
或配置的xfer.log
),应该能看到区域传送成功的消息,类似:
zone mycluster.local/IN: transferred serial 2023072101
transfer of 'mycluster.local/IN' from 192.168.1.10#53: Transfer completed
- 检查
/var/named/slaves/
目录下是否生成了对应的区域文件。
- 在从服务器启动
步骤四:配置负载均衡 (以 HAProxy 为例)
这里我们使用 HAProxy 作为软件负载均衡器。可以在一台独立的服务器上安装 HAProxy,或者在其中一台 DNS 服务器上安装(但不推荐用于生产环境的关键服务)。
假设 HAProxy 安装在 IP 为 192.168.1.20
的服务器上。
安装 HAProxy:
sudo yum install -y haproxy
配置 HAProxy (
/etc/haproxy/haproxy.cfg
):sudo vi /etc/haproxy/haproxy.cfg
添加或修改以下内容:
global log /dev/log local0 chroot /var/lib/haproxy pidfile /var/run/haproxy.pid maxconn 4000 user haproxy group haproxy daemon stats socket /var/lib/haproxy/stats defaults mode http # 对于DNS,通常用tcp模式,但http模式的健康检查更灵活 log global option httplog # 如果是tcp模式,用tcplog option dontlognull option http-server-close # 如果是tcp模式,用clitcpka 和 srvtcpka # option forwardfor except 127.0.0.0/8 # 如果需要传递客户端IP option redispatch retries 3 timeout http-request 10s timeout queue 1m timeout connect 10s timeout client 1m timeout server 1m timeout http-keep-alive 10s timeout check 10s maxconn 3000 # DNS UDP Frontend and Backend frontend dns_udp_frontend bind 192.168.1.100:53 proto udp # VIP 和 UDP 端口 mode udp default_backend dns_udp_backend backend dns_udp_backend mode udp balance roundrobin # 或 leastconn # 健康检查对UDP比较困难,通常依赖TCP的健康检查或外部脚本 # HAProxy 对 UDP 的健康检查支持有限,通常依赖于后端服务器是否响应 server dns_master 192.168.1.10:53 check # 'check' 对UDP可能无效或行为不同 server dns_slave1 192.168.1.11:53 check server dns_slave2 192.168.1.12:53 check # DNS TCP Frontend and Backend (DNS也使用TCP,例如区域传送或大型响应) frontend dns_tcp_frontend bind 192.168.1.100:53 proto tcp # VIP 和 TCP 端口 mode tcp default_backend dns_tcp_backend backend dns_tcp_backend mode tcp balance roundrobin option tcp-check # 使用TCP健康检查 # TCP健康检查:尝试连接到服务器的53端口 # 可以更复杂,例如发送一个简单的DNS查询并期望特定响应 # default-server check port 53 inter 2s fall 3 rise 2 server dns_master 192.168.1.10:53 check port 53 inter 2s fall 3 rise 2 server dns_slave1 192.168.1.11:53 check port 53 inter 2s fall 3 rise 2 server dns_slave2 192.168.1.12:53 check port 53 inter 2s fall 3 rise 2 # HAProxy Stats Page (可选) listen stats bind *:8404 mode http stats enable stats uri /stats stats realm Haproxy\ Statistics stats auth admin:password # 设置用户名和密码
关于 HAProxy UDP 健康检查的说明:
HAProxy 对 UDP 服务的健康检查能力有限。check
关键字在mode udp
下的行为可能不如 TCP。更可靠的 UDP 健康检查通常需要:- 依赖于 TCP 端口的健康检查(如果服务同时监听 TCP)。
- 使用外部脚本通过
option external-check
执行自定义的 UDP 健康检查。 - 对于 DNS,一个简单的 TCP 连接检查到端口 53 通常可以作为指示。
允许 HAProxy 绑定到非本地 IP (VIP):
sudo sysctl -w net.ipv4.ip_nonlocal_bind=1 # 持久化 echo "net.ipv4.ip_nonlocal_bind=1" | sudo tee /etc/sysctl.d/90-haproxy.conf
启动 HAProxy 并设置开机自启:
sudo systemctl start haproxy sudo systemctl enable haproxy sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="0.0.0.0/0" port port="53" protocol="udp" accept' sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="0.0.0.0/0" port port="53" protocol="tcp" accept' sudo firewall-cmd --permanent --add-port=8404/tcp # 如果启用了stats页面 sudo firewall-cmd --reload
步骤五:配置客户端
将客户端的 DNS 服务器设置为负载均衡器的 VIP:192.168.1.100
。
步骤六:(可选) 使用 Keepalived 实现 VIP 高可用和负载均衡器冗余
如果 HAProxy 本身成为单点故障,可以使用 Keepalived 来管理 VIP 并在多个 HAProxy 节点之间进行故障转移。
假设你有两台 HAProxy 服务器 (haproxy1: 192.168.1.20
, haproxy2: 192.168.1.21
)。
在两台 HAProxy 服务器上安装 Keepalived:
sudo yum install -y keepalived
配置 Keepalived (
/etc/keepalived/keepalived.conf
):在 haproxy1 (MASTER):
! Configuration File for keepalived global_defs { router_id HAPROXY_DNS_01 } # 脚本用于检查HAProxy进程是否在运行 vrrp_script chk_haproxy { script "killall -0 haproxy" # 检查haproxy进程是否存在 interval 2 # 每2秒检查一次 weight 2 # 如果成功,权重加2 } vrrp_instance VI_DNS { state MASTER interface eth0 # 根据你的网卡名称修改 virtual_router_id 51 # 必须在所有Keepalived节点上相同 priority 101 # MASTER 优先级更高 advert_int 1 authentication { auth_type PASS auth_pass yoursecret # 密码,所有节点相同 } virtual_ipaddress { 192.168.1.100/24 dev eth0 label eth0:vip1 # VIP } track_script { chk_haproxy } }
在 haproxy2 (BACKUP):
! Configuration File for keepalived global_defs { router_id HAPROXY_DNS_02 } vrrp_script chk_haproxy { script "killall -0 haproxy" interval 2 weight 2 } vrrp_instance VI_DNS { state BACKUP interface eth0 virtual_router_id 51 priority 100 # BACKUP 优先级较低 advert_int 1 authentication { auth_type PASS auth_pass yoursecret } virtual_ipaddress { 192.168.1.100/24 dev eth0 label eth0:vip1 } track_script { chk_haproxy } }
启动 Keepalived 并设置开机自启 (在两台 HAProxy 服务器上):
sudo systemctl start keepalived sudo systemctl enable keepalived sudo firewall-cmd --permanent --add-protocol=vrrp # 允许VRRP协议 sudo firewall-cmd --reload
现在,
192.168.1.100
这个 VIP 会由 Keepalived 管理。如果haproxy1
上的 HAProxy 进程挂掉或服务器宕机,VIP 会自动漂移到haproxy2
。
测试高可用性:
主从同步测试:
- 在
dns-master
上修改区域文件 (例如,添加一条 A 记录),并增加 SOA 序列号。 - 执行
sudo rndc reload mycluster.local
(或sudo systemctl reload named
)。 - 在从服务器上查看日志,确认区域已成功传送新的序列号。
- 在从服务器上使用
dig @localhost new_record.mycluster.local
查询新记录。
- 在
负载均衡和故障转移测试 (HAProxy + BIND 节点):
- 从客户端
dig @192.168.1.100 existing_record.mycluster.local
,多次查询,观察 HAProxy 是否将请求分发到不同的后端 BIND 服务器 (可以在 BIND 服务器上开启查询日志来确认)。 - 停止其中一个从 BIND 服务器 (
sudo systemctl stop named
ondns-slave1
)。 - 再次从客户端查询,请求应该仍然成功,并由其他健康的 BIND 服务器响应。HAProxy 的健康检查应该会将故障节点标记为 down。
- 恢复
dns-slave1
上的named
服务,它应该会自动重新加入到负载均衡池中。
- 从客户端
VIP 故障转移测试 (Keepalived + HAProxy 节点):
- 在当前持有 VIP 的 HAProxy 服务器 (MASTER Keepalived) 上停止
keepalived
服务或haproxy
服务 (如果track_script
配置正确)。 - 观察 VIP (
192.168.1.100
) 是否成功漂移到另一台 HAProxy 服务器 (BACKUP Keepalived)。可以使用ip addr show
查看。 - 从客户端继续查询
dig @192.168.1.100 existing_record.mycluster.local
,应该仍然成功。
- 在当前持有 VIP 的 HAProxy 服务器 (MASTER Keepalived) 上停止
注意事项和改进:
- 安全性:
- 严格配置防火墙,只允许必要的端口和源 IP。
- 保护
rndc.key
文件。 - 考虑使用 TSIG (Transaction Signatures) 来保护区域传送。
- 定期更新 BIND 和其他系统组件。
- 日志和监控:
- 配置详细的日志记录,并使用集中式日志管理系统。
- 使用监控系统 (Nagios, Zabbix, Prometheus) 监控 BIND 服务、HAProxy、Keepalived 的状态以及 DNS 解析的健康状况。
- DNSSEC: 如果你的区域需要 DNSSEC 签名,确保主服务器正确签名区域,从服务器能够处理已签名的区域。
- 扩展性: 可以根据需要增加更多的从服务器和 HAProxy 节点。
- Anycast: 对于更大规模或地理分布的 DNS 集群,可以考虑使用 Anycast IP 地址,这需要网络设备的支持。
- 配置管理: 使用 Ansible, Puppet, Chef 等工具自动化部署和管理配置。
这是一个相对完整的搭建高可用 BIND9 集群的方案。根据你的具体需求和环境,可能需要进行调整。