Nginx plus 的 UDP 反向代理主动健康检测

万能的 Nginx 在今年支持了 UDP 的负载均衡,依旧是由于某些原因我打算试一试这个 UDP 反代功能,实际上 Linux 平台能做 UDP 的代理的工具很少,Nginx 几乎是唯一的选择,然后它自带的主动健康检测和 dashboard 监控是 Nginx-plus 才有的(实际上自己用插件写的话应该也不是很困难,但是这个我确实不会写)Nginx-plus 的安装也很有意思,从官网下载一个 key 和一个 crt,然后添加到 apt,连接 nginx.com 的仓库的时候会进行双向的 TLS 认证(一般的 HTTPS 只有客户端验证服务器,这个模式更类似网上银行和支付宝的客户端证书)

Nginx的四层反代功能是由 ngx_stream_xxx_module 提供,使用一个 stream 区块代替 http 区块,其余配置和做 Web Server 的时候差不多,来看个例子,这个例子是 dnsmasq 监听53,nginx 从10086端口反代到53:

是不是很简单呢,本来就是嘛,Nginx stream module 尤其是 UDP 方面功能本来就很原始,可配置的参数啊文档啊都很少,如果不是 Nginx-plus 这里还有一半的参数是不可用的,反代这部分文档在http://nginx.org/en/docs/stream/ngx_stream_upstream_module.html,有兴趣的可以自己去看。

关于他的主动健康检测,Nginx 官方博客(https://www.nginx.com/resources/admin-guide/tcp-load-balancing/)写的是:

实际上原理就是 Nginx 假装自己是客户端隔几秒给后端发一个请求,返回结果符合预期结果就当他是正常的(TCP 可以有很多办法检测,比如尝试握手有响应就可以认为存活,UDP 是无连接的相对比较难确定到底问题出在哪里),然而这个写法是有问题的,这个实际上是查询 stl.umsl.edu(send 里的数据实际上就是十六进制的 DNS 请求的 payload),然而这玩意不知道是时间久远还是 UMSL 改了什么 DNS 设置,总之现在这么写健康检测并不会 pass 的。

实际上我想了很久一段时间这个健康检测到底是怎么查看结果的,如果要看日志的话是不是太不优雅了一点···后来才看到https://nginx.org/en/docs/http/ngx_http_status_module.html#status 这么个玩意是有监控面板的

QQ20160813-0@2x

在右上角那个 upstream 里就能看到健康检测进行的次数、失败成功的次数等等,如果我们使用官网那个样例的话,就会一直提示 failed,让我们来看看这里出了什么问题, tcpdump -i lo udp -vnnX 查看 lo 网卡接口上的 UDP 包(其中 -v 是查看详细参数 -n 是以 IP 显示 -X 是同时输出 HEX dump 和 ASCI 解码后的内容),发现 dnsmasq 返回的结果是正常的,那么八成就是 expect 的内容出现了偏差。

我们来看看 DNS 长啥样,这个玩意是 RFC 里抄的:

看不懂是吧,我其实也看不懂,其实要自己手写一个 DNS 的 payload 还是很麻烦的,于是我决定打开 wireshark (tcpdump 其实是可以配合 wireshark 远程抓包的,但是由于防火墙比较麻烦我没有配)抓个包看看:

QQ20160813-2@2x

好的清楚多了,wireshark 还可以对应各个分段标识出十六进制或者二进制表示,文字部分是 ASCI 编码。注意从 wireshark 复制出来的 HEX dump 是空格分开的, sed s/' '/'\\x'/g 可以替换成 Nginx 需要的格式。在 wireshark 里把从 transcation ID 到 Class IN (0x0001) 对应的二进制抓出来丢进 nginx 配置文件的 send 区块,再找到对应的 DNS 响应,找一个合适的部分丢进 expect 区块,reload nginx 然后盯着 dashboard 和 tcpdump 看有没有成功。

QQ20160813-3@2x

后来大部分响应是 pass 的,还是有时候会出现失败,持续抓包之后发现 dnsmasq 发送出去的 payload 会变,有一个 flag 改变之后变成了递归查询,请求变成递归的之后就会一直追到权威 DNS 那边去,响应就会变得很庞大,Nginx 也抓不到位置在很后面的我设置的那个响应字段,自然就会被当做失败。这个现象比较有规律,基本十分钟出现一次(实际上就是我域名的 TTL 时间),但是我现在还想不通这里为什么会变,nginx 的 send 区块里已经包括了这个标志位,为什么 dnsmasq 向外查询的时候就会变,感觉上还是和 dnsmasq 本身的设置有关,TTL 过期之后没有本地缓存内容(实际上我在服务端执行一次 nslookup 之后,所有的请求就会立刻正常,不管他的话,过一段时间也会正常,实在是没有心思跟着抓半天的包)。workaround 也有,就是分析一下这两个响应有什么一样的部分取出来做 expect 内容。QQ20160812-3@2x


UPDATE 2017.7.27

实际上这个问题是当时的我不理解 DNS 查询的机制导致的,当时的场景是我在本地跑了一个 dnsmasq,查询发往127.0.0.1:53,而 dnsmasq 是一个缓存 DNS 服务器(或者说是递归服务器),在一次查询之后会把解析结果在本地缓存 TTL 秒,经过这个时间之后 dnsmasq 认为缓存失效,就会向权威查询(进行一次迭代查询)

发表评论

电子邮件地址不会被公开。 必填项已用*标注