公众号旧文

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 分布在不同故障域里:

不同机器
不同母机
不同机房
不同网络
最好不同商家

这样当一个节点被清退、断网或故障时。

另一个节点还能继续提供解析服务。