场景分析
家用DNS的场景免不了天天折腾,比不上企业服务器24x7不关机。
很多在用PaoPaoDNS+PaoPaoGateway的网友经常问关于DNS故障转移的问题。让我们来先看一个常用的简单FakeIP拓扑:
在这个场景里面,DHCP下发客户端的DNS是PaoPaoDNS,“DNS故障"可以有几种情况:
- PaoPaoGateway炸了,不返回FakeIP。但这在常用的拓扑下,通常只影响国外域名查询。
- PaoPaoDNS的递归组件炸了,但PaoPaoDNS内部可以回落到其他查询结果
- PaoPaoDNS所在的宿主机炸了,这回就真的是没网了。<-最需要故障转移高可用的一集
如果给客户端下发备用DNS有用吗?没有用,多个DNS下发到客户端的实际行为不确定,大多数时候都是随机查询并不是故障转移。既然PaoPaoDNS所在的宿主机炸了,那么自然也要跳出宿主机去解决问题——比如在一些稳定不关机的嵌入式设备,或者可执行二进制的硬路由器(经典openwrt硬路由)上跑一个简单的DNS转发器,他的任务很简单,当PaoPaoDNS可用的时候使用PaoPaoDNS,当PaoPaoDNS不可用的时候回落到运营商或者其他公共DNS————由于给客户端下发的DNS是路由器的DNS,因此你就算把PaoPaoDNS的宿主机砸了也不会断网。理想的拓扑简化如下:
实现平滑的DNS故障转移
很多开源DNS服务端项目,比如openwrt里面自带的dnsmasq就有DNS故障转移的功能————比如按顺序查询(strict-order参数)。但实际用起来一点也不平滑,表现为:
- 当DNS故障的时候,查询结果明显巨大的延迟,上网就感觉到卡。因为触发故障转移的条件往往非常苛刻,比如直到遇到SERVFAIL甚至5秒的查询超时后才切换备用查询服务器。也就是你搭建的DNS炸了之后,每次查询都要忍受巨大的延迟————有些应用程序甚至等不了这么久就马上报错。
- 无有效结果的响应不会被视作DNS查询失败。这个很好理解,就像你打开网页返回了404被认为是正常的。比如当PaoPaoGateway炸了的时候,CUSTOM_FORWARD仍会返回失败的消息。
- 当搭建的DNS服务器恢复正常了不能及时切回来。这个不仅是DNS服务端缓存,也是相对于客户端而言的,就FakeIP场景而言,假设在故障的时候没有返回FakeIP而是其他IP,DNS或者其他服务恢复之后,之前的IP结果在客户端就会有通常最多10分钟左右的缓存,这就造成换回来也很不平滑。
那么要做到平滑的DNS故障转移也很简单:先查询主DNS,在一个较小的查询阈值之内如果没有匹配查询请求的DNS结果,那么就转移到备用DNS上查询,如果有查询结果,把DNS应答的ttl设置为1,让缓存快速过期,待主DNS恢复后,平滑切换回来。实际上用mosdns写配置也不复杂,但为了更便捷的配置和专注于DNS故障转移的用途,方便给下次遇到同样需求的问题直接甩一个程序链接,基于PaoPaoDNS的修改版本的mosdns的基础上写了一个故障转移专用的DNS转发器————mini-ppdns。
mini-ppdns
https://github.com/kkkgo/mini-ppdns
专注于 DNS 故障转移的迷你DNS转发器。
mini-ppdns 是从PaoPaoDNS项目精简修改而来的纯粹转发器,致力于提供极致轻量化且高效的平滑 DNS 故障转移体验。
快速启动
假设你的本地自建DNS是10.10.10.8,你的运营商DNS或者需要故障转移的DNS是223.5.5.5, 那么最简单的命令行启动:
| |
可以指定DNS端口和多个上游:
| |
参数详解
-listen可以指定监听地址和端口,默认是监听所有可监听的私有地址(跳过公网地址):mini-ppdns -dns 10.10.10.8 -fall 223.5.5.5 -listen 127.0.0.1:53-aaaa可以指定是否开启IPv6的aaaa记录(默认为no,屏蔽aaaa):mini-ppdns -dns 10.10.10.8 -fall 223.5.5.5 -aaaa=yes-force_fall可以指定某些IP段总是走运营商/故障转移的DNS。:mini-ppdns -dns 10.10.10.8 -fall 223.5.5.5 -force_fall=192.168.1.10,192.168.2.0/24
注:FakeIP场景可以利用这个功能,间接实现某些设备不走代理-qtime可以指定故障转移的延迟阈值(默认为250ms,一般不需要调整):mini-ppdns -dns 10.10.10.8 -fall 223.5.5.5 -qtime=250-debug输出详细的调试日志。-d可以在后台运行。-config可以指定加载配置文件,可以配置mini-ppdns.ini如下:
| |
在openwrt路由器上部署
在openwrt上部署说起来简单也复杂,因为很多openwrt里面有各种神神秘秘的插件互相干扰。其中不少会劫持DNS,此处部署过程仅包含一些常见的坑和注意事项。当然部分过程也适用于其他linux系统。
- 去release下载适合你的硬件架构的二进制文件。如果不清楚自己的硬件是什么架构,可以在终端输入
uname -m。其中release名字带UPX的是为了给一些储存空间紧张的设备用的,如果你的设备空间充足下正常版本即可。 - 把
mini-ppdns上传到你的设备,为了方便你可以上传到/usr/sbin/mini-ppdns,加执行权限chmod +x /usr/sbin/mini-ppdns。将你的配置文件储存在/etc/mini-ppdns.ini,然后执行mini-ppdns -config /etc/mini-ppdns.ini看看是否输出正常(比如提示某个端口已经被监听)。 - 添加自启动脚本。在openwrt上最简单的是编辑
/etc/rc.local,在exit 0之前添加你的启动命令,带上-d参数,程序会自动到后台,不会阻塞启动。当你修改了局域网的IP段或者配置,你需要重新启动mini-ppdns。
或者写一个守护脚本加计划任务或者修改服务,此处提供了一个参考脚本:
https://github.com/kkkgo/mini-ppdns/blob/main/mini-ppdns.sh
使用方法,把脚本上传到/usr/sbin重命名为/usr/sbin/mini-ppdns加执行权限,crontab -e编辑计划任务:* * * * * /usr/sbin/mini-ppdns.sh即可。脚本启动之前检测是否已经存在mini-ppdns进程,如果存在就直接退出,因此可以直接让计划任务每分钟执行来作为守护。执行mini-ppdns.sh restart可以重载配置。
- 普通linux到这里已经弄完了,但一些linux安装过程中会自带DNS服务器导致占用监听端口,比如Ubuntu,可以禁用自带的DNS解析器:
当然,openwrt自带dnsmasq,我们需要把他停用。
编辑 /etc/dnsmasq.conf 或在 OpenWrt 管理界面 (网络 -> DHCP/DNS -> 高级设置) 中:
- 某些dnsmasq的禁用DNS解析会导致DHCP不下发DNS。所以我们还需要手动下发路由器的DNS。
点击网络-接口-LAN-DHCP服务器-高级设置,在DHCP选项里面,手动设置DHCP的附加选项。下发DNS的选项是6,比如你的路由器IP是10.10.10.1,那么填入6,10.10.10.1。当然,在FakeIP场景下,你也可以顺便填入option 121。 - 某些修改的openwrt版本会有DNS重定向的劫持选项需要手动关闭。【参考1】 【参考2】。
- 在IPv6环境下,需要关闭路由器的DNS的IPv6 DNS下发。
在docker上部署
尽管在这个场景下使用docker部署不太常见,但仍有很多没有开放终端的设备只能跑docker。
以下是非常简单的docker compose的示例配置,可以根据自己的实际环境调整,或者复制给AI转换成你实际的容器环境Cli。
把mini-ppdns.ini和对应你设备架构的mini-ppdns二进制放在docker-compose.yml同一目录。
此处定义网络模式是host(直接使用宿主机网络),因此不需要映射端口,可根据自己需要调整。