公众号旧文
VPS 被清退后,DNS 服务如何迁移?
前几天,我给域名 851004721.xyz 搭了一套自建权威 DNS。
当时写了这篇:无需申请,即可使用:免费二级域名正式开放
这套 DNS 的主要作用不是普通解析,而是支持一些自定义规则。
比如:
a.1-1-1-1.851004721.xyz -> 1.1.1.1
a.8-8-8-8.851004721.xyz -> 8.8.8.8
也就是说,DNS 服务会根据子域名里的内容动态返回对应的 IP。
之前的架构是这样的:
ns1 = jp-pro
ns2 = us-sta
其中:
ns1.851004721.xyz -> 45.8.173.18
ns2.851004721.xyz -> 144.225.254.193
这两个节点分别承担权威 DNS 的角色。
正常情况下,外部递归 DNS 查询 851004721.xyz 时。
会从注册局父区拿到 ns1 和 ns2,然后再去问其中任意一个权威服务器。
但是今天 us-sta 被清退了,原来的 ns2 已经无法访问。
我在其他机器上测试旧 ns2:
dig @144.225.254.193 a.1-1-1-1.851004721.xyz A
结果是:
communications error to 144.225.254.193#53: timed out
no servers could be reached
这说明旧的 ns2 已经彻底不可用了。
而 ns1 仍然正常:
dig @45.8.173.18 a.1-1-1-1.851004721.xyz A
返回:
a.1-1-1-1.851004721.xyz. 60 IN A 1.1.1.1
并且返回里有 aa 标志,说明这是权威回答。
一、为什么不直接用 jp-pro2 做 ns2?
我手里还有一台 jp-pro2,看起来可以很方便地接替 ns2。
但问题是,jp-pro 和 jp-pro2 大概率在同一个故障域里。
它们是同一个平台、同一类节点,甚至可能就在同一台母机上。
如果把 DNS 做成:
ns1 = jp-pro
ns2 = jp-pro2
表面上看是两个 NS,实际上意义不大。
因为它只能防这种问题:
jp-pro 系统配置错了
jp-pro 上的服务挂了
jp-pro 文件误删了
但防不了真正重要的问题:
同一母机故障
同一平台网络故障
同一商家清退
同一机房断网
权威 DNS 的 ns1/ns2,核心价值不是有两个 IP,而是故障域隔离。
所以最后我没有选择 jp-pro2,而是选择了 jp-sb2。
新的目标架构是:
ns1 = jp-pro
ns2 = jp-sb2
对应公网 IP:
ns1.851004721.xyz -> 45.8.173.18
ns2.851004721.xyz -> 45.192.199.139
二、把 pzbkdns 复制到 jp-sb2
我先确认 jp-sb2 的公网 IP:
curl -4 ip.sb
得到:
45.192.199.139
然后从 jp-pro 把 /opt/pzbkdns 复制到 jp-sb2:
scp -r /opt/pzbkdns root@100.64.0.5:/opt/
同时复制 systemd 服务文件:
scp /etc/systemd/system/pzbkdns.service root@100.64.0.5:/etc/systemd/system/pzbkdns.service
100.64.0.5 是 jp-sb2 的 Tailscale 内网 IP。
复制过程中遇到一个小问题:
scp: local "/opt/pzbkdns/venv/lib64" is not a regular file
这是因为 Python 虚拟环境里的 lib64 是软链接,scp -r 处理软链接时不如 rsync -a 稳。
不过问题不大,我没有继续纠结复制过来的 venv,而是直接在 jp-sb2 上重建虚拟环境。
三、在 jp-sb2 上重建 Python 虚拟环境
进入目录:
cd /opt/pzbkdns
删除旧的虚拟环境:
rm -rf venv
然后重新创建虚拟环境:
python3 -m venv venv
安装依赖:
./venv/bin/pip install -U pip
./venv/bin/pip install dnslib
之后启动服务:
systemctl daemon-reload
systemctl restart pzbkdns
systemctl status pzbkdns --no-pager
看到:
Active: active (running)
说明 jp-sb2 上的 pzbkdns 已经跑起来了。
四、检查 53 端口
接着检查 UDP/TCP 53 是否监听:
ss -lntup | grep ':53'
输出:
udp UNCONN 0 0 0.0.0.0:53 0.0.0.0:* users:(("python",pid=5655,fd=3))
tcp LISTEN 0 5 0.0.0.0:53 0.0.0.0:* users:(("python",pid=5655,fd=4))
这说明 DNS 服务已经同时监听了 UDP 53 和 TCP 53。
权威 DNS 一般不能只开 UDP 53,TCP 53 也应该支持,因为部分 DNS 响应、重试或特殊场景会用到 TCP。
五、测试新 ns2 是否可用
先测本机:
dig @127.0.0.1 a.1-1-1-1.851004721.xyz A
返回:
a.1-1-1-1.851004721.xyz. 60 IN A 1.1.1.1
再测 Tailscale 内网 IP:
dig @100.64.0.5 a.1-1-1-1.851004721.xyz A
也正常返回:
1.1.1.1
最后测试公网 IP:
dig @45.192.199.139 a.1-1-1-1.851004721.xyz A
返回:
a.1-1-1-1.851004721.xyz. 60 IN A 1.1.1.1
并且有:
flags: qr aa
这说明 jp-sb2 已经可以作为新的权威 DNS 节点对外服务了。
六、为什么后台改了 ns2 IP,代码里还要改?
注册商后台里改的是 glue record,也就是父区 .xyz 用来找到权威服务器的入口记录。
比如:
ns1.851004721.xyz -> 45.8.173.18
ns2.851004721.xyz -> 45.192.199.139
因为 ns1.851004721.xyz 和 ns2.851004721.xyz 本身就在 851004721.xyz 这个域名下面,所以父区必须提供它们的 IP。
否则递归解析器会陷入我要先找到 ns1/ns2,才能查询 851004721.xyz;但 ns1/ns2 又在 851004721.xyz 里面的死循环。
这就是 glue record 的作用。
但我的 pzbkdns 程序自己也会回答:
ns1.851004721.xyz A
ns2.851004721.xyz A
也就是说,除了注册商后台,程序配置里也有一份固定记录。
我在代码里找到旧 IP,把它改成新的 ns2 IP,然后重启:
systemctl restart pzbkdns
测试:
dig @127.0.0.1 ns2.851004721.xyz A
dig @45.192.199.139 ns2.851004721.xyz A
返回:
ns2.851004721.xyz. 60 IN A 45.192.199.139
同样,jp-pro 上也要改同一个配置。
因为 ns1 也会回答 ns2.851004721.xyz 的 A 记录。
所以这次需要改两个地方:
注册商后台:
给 .xyz 父区用,决定 glue record。
pzbkdns/config.py:
给自己的权威 DNS 程序用,决定它自己怎么回答 ns2.851004721.xyz。
这两个地方最好保持一致。
七、最终验证
我在外部机器上测试两个权威 DNS:
dig @45.8.173.18 ns2.851004721.xyz A
dig @45.192.199.139 ns2.851004721.xyz A
dig @45.8.173.18 a.8-8-8-8.851004721.xyz A
dig @45.192.199.139 a.8-8-8-8.851004721.xyz A
结果两台都能正确返回:
ns2.851004721.xyz -> 45.192.199.139
a.8-8-8-8.851004721.xyz -> 8.8.8.8
并且都有 aa 标志。
这说明两个权威 DNS 服务本身已经完全正常。
八、现在还在等什么?
最后一步是等待注册局父区的 glue record 生效。
我直接查询 .xyz 父区:
dig @x.nic.xyz 851004721.xyz NS +norecurse +additional
dig @y.nic.xyz 851004721.xyz NS +norecurse +additional
dig @z.nic.xyz 851004721.xyz NS +norecurse +additional
一开始父区仍然返回旧 IP:
ns1.851004721.xyz. 3600 IN A 45.8.173.18
ns2.851004721.xyz. 3600 IN A 144.225.254.193
说明注册商页面虽然已经保存了:
ns2 -> 45.192.199.139
但 .xyz 父区还没有同步完成。
这时不需要再改程序,因为权威服务器已经正常了。
只需要等待父区 glue 更新。
等它变成:
ns1.851004721.xyz. 3600 IN A 45.8.173.18
ns2.851004721.xyz. 3600 IN A 45.192.199.139
才算真正完成。
九、这次迁移的总结
这次 ns2 迁移,大致分成几步:
1. 确认旧 ns2 已经不可用
2. 选择新的故障域机器 jp-sb2
3. 复制 pzbkdns 项目和 systemd 服务
4. 在新机器上重建 venv
5. 启动 pzbkdns
6. 检查 UDP/TCP 53
7. 测试新 ns2 公网解析
8. 修改 pzbkdns 里的 SECONDARY_NS_IP
9. 修改注册商后台 Personal nameserver
10. 等待注册局 glue record 生效
这次也再次说明了一个问题:
自建权威 DNS 的 ns1/ns2,不是随便找两台机器就行。
如果两台机器在同一个母机、同一个平台、同一个网络里。
那么它们并不能提供真正的高可用。
更好的方式是让 ns1/ns2 分布在不同故障域里:
不同机器
不同母机
不同机房
不同网络
最好不同商家
这样当一个节点被清退、断网或故障时。
另一个节点还能继续提供解析服务。