NGINX Plus R9新增了UDP流量反向代理和负载均衡功能,显著增强了NGINX Plus的第4层负载均衡能力。
本篇博文探讨在现代应用基础设施中运行DNS服务器所面临的挑战,并以此说明NGINX Open Source和NGINX Plus如何有效、高效地对UDP和TCP流量进行负载均衡。(应用[主动]健康检查是NGINX Plus的专属功能,但除此之外,本文内容同样适用于NGINX Open Source;为简洁起见,下文将统一使用NGINX Plus。)
[编者注:如需概览NGINX Plus R9的所有新功能,请参阅我们博客中的《NGINX Plus R9发布公告》。]
为什么要对UDP流量进行负载均衡?
与TCP不同,UDP在设计上不保证数据的端到端交付。这就像用信鸽传递消息:可以确定消息已发出,但无法确认是否送达。这种“无连接”方式有多项优势,其中最显著的是延迟低于TCP。这是因为UDP的单条消息较小,占用的带宽更少,而且建立连接时无需握手。UDP将检测超时及其他网络层问题的任务留给应用开发者。那么,这对DNS意味着什么?
与其他几种基于UDP的协议一样,DNS采用请求-响应数据流。例如,DNS客户端请求与某个主机名对应的IP地址,并接收响应。如果在指定的超时时间内未收到响应,DNS客户端会向“备用”DNS服务器发送相同的请求。但是,必须等到超时后才能重试请求,这可能会使通常以毫秒计的极快过程变成以秒计的缓慢过程。

使用NGINX Plus代理和负载均衡DNS流量,可减少客户端遇到超时的次数。当NGINX Plus负载均衡器后端部署了多台DNS服务器时,只有客户端与NGINX Plus之间发生网络分区,客户端才会遇到超时。NGINX Plus使用应用健康检查后,DNS服务器本身的问题不会影响客户端。NGINX Plus通过监控每台服务器的可用性和响应时间,避免将客户端请求发送到运行状况不佳的服务器。
DNS并非仅使用UDP协议
尽管绝大多数DNS流量都通过UDP传输,但一些常见的DNS操作会使用TCP。DNS使用UDP传输小型消息(最大512字节),而对于需要(或可能需要)传输较大消息的操作,则使用TCP。过去,TCP在DNS中仅用于将区域从权威主名称服务器传送到其辅助名称服务器。然而,随着容器和不可变基础设施日益普及,DNS越来越多地通过使用SRV记录充当主要的服务发现机制。
DNS SRV 记录最初是为使用 SIP 查找服务器的IP语音(VoIP)手持设备而引入的,但也可用于任何类型的服务。不过,SRV 记录包含的信息远多于大多数其他类型的DNS记录。因此,标准的512 字节UDP响应只能容纳约10 条SRV 记录,而A 记录则可容纳约30 条。当DNS响应超过512 字节的限制时,系统会返回前512 字节,但将响应标记为“已截断”。此时,DNS客户端可以尽可能处理截断的响应,也可以使用TCP重试同一请求。
这意味着,在现代网络基础设施中对DNS服务器进行负载均衡时,NGINX Plus可能会同时收到UDP和TCP流量。
微服务环境中的DNS
下图简要展示了一个包含两个负载均衡器的微服务环境。前端负载均衡器代理来自应用公共客户端的请求,选择合适的微服务实例,并执行许多其他功能,此处不作讨论。我们将重点介绍DNS负载均衡器。它位于微服务环境与DNS服务器之间,这些服务器为微服务提供服务发现信息。

配置基本DNS负载均衡
NGINX Plus在Stream模块中实现第4层负载均衡,因此UDP和TCP负载均衡需在stream块中配置,如以下代码片段所示。
警告:不能直接将此配置片段作为新文件添加到/etc/nginx/conf.d目录中。否则会出现验证错误(“此处不允许使用stream指令”),因为默认的NGINX Plusnginx.conf配置文件会将conf.d目录中文件的内容包含在http块中。最简单的解决方案是将完整的stream块直接包含在nginx.conf中。
stream { upstream dns_servers {
server 192.168.136.130:53;
server 192.168.136.131:53;
}
server {
listen 53 udp;
listen 53; #tcp
proxy_pass dns_servers;
error_log /var/log/nginx/dns.log info;
}
}首先,定义DNS服务器的上游组。server指令指定上游服务器监听的端口号,即53(DNS的知名端口)。
The server{}块定义NGINX Plus如何处理传入的DNS流量。两条listen指令让NGINX Plus在端口53上同时侦听UDP和TCP流量。TCP是Stream模块默认的第4层协议,因此无需像UDP那样将其显式指定为参数。
The proxy_pass指令用于指定NGINX Plus如何处理所侦听的流量。此处,我们将此类流量代理到dns_servers上游组。将客户端UDP请求转发到上游服务器时,NGINX Plus会自动使用UDP(客户端TCP请求则使用TCP),因此无需在上游组中明确指定第4层协议。
Stream模块中没有access_log指令,因为NGINX Plus不会像检查HTTP数据包那样检查TCP段或UDP数据报的有效负载。不过,可以在error_log指令中使用info参数,记录连接处理和代理事件。
[编者注:本博客发布后,Stream模块已在NGINX Open Source 1.11.4和NGINX Plus R11中启用访问日志记录。]
高可用性调优
为提高DNS服务器的可用性,可以再添加几条指令,并配置主动(应用)健康检查。
配置代理响应次数和超时时长
第一个附加指令是proxy_responses,用于指定NGINX Plus预期每个代理UDP请求返回多少个响应。在本例中,NGINX Plus收到一个响应后会立即停止等待后续响应,从而释放该会话使用的内存和套接字。
第二个附加指令proxy_timeout用于确定NGINX Plus等待服务器响应的时长(此处将默认的10分钟缩短为1秒)。如果NGINX Plus在此期间未收到响应,则会尝试上游组中的下一台服务器,并将无响应的上游服务器标记为在指定时段内不可用(默认为10秒),以免其他客户端在此期间因超时而出现延迟。
server { listen 53 udp;
listen 53; #tcp
proxy_pass dns_servers;
error_log /var/log/nginx/dns.log info;
proxy_responses 1;
proxy_timeout 1s;
}还可以通过在上游组的fail_timeout 指令中添加 server 选项,更改服务器被标记为不可用的时长。采用以下设置时,NGINX Plus会将发生故障的上游服务器标记为不可用,持续60秒:
upstream dns_servers { server 192.168.136.130:53 fail_timeout=60s;
server 192.168.136.131:53 fail_timeout=60s;
}这样,当某台DNS服务器发生故障时,我们便可控制客户端遇到的延迟时长。不过,如果尝试向发生故障的DNS服务器发送TCP请求,TCP内置的错误检查机制会让NGINX Plus自动将该服务器标记为不可用,从而避免后续向其发送TCP或UDP请求。
配置主动健康检查
NGINX Plus的主动健康检查功能是一项额外且非常实用的工具,有助于确保包括DNS在内的任何负载均衡服务实现高可用性。无需等到DNS客户端发出的实际TCP请求失败后才将DNS服务器标记为停机,我们可以在health_check指令中添加port=53参数,并将该指令包含在server{}块中,让NGINX Plus定期尝试连接端口53,以确定DNS服务器是否已启动并正常运行。(默认情况下,NGINX Plus会向listen指令指定的端口发送健康检查请求,在本例中为端口53。因此,此处使用该参数显式配置默认端口;如果同时修改DNS服务器,使其能够响应其他端口上的流量,也可以指定其他端口。)
使用UDP时,还可以进一步配置主动健康检查,对已知记录执行实际的DNS查询。例如,可以在区域文件中为微服务环境内用于服务发现的同一子域添加以下CNAME记录。
healthcheck IN CNAME healthy.svcs.example.com.由于UDP较为轻量,我们可以监控网络流量,并轻松提取表示DNS查询的字节字符串。然后创建一个match配置块,将该字符串用作send指令的参数。expect指令指定服务器必须返回的响应,只有返回该响应,服务器才会被视为运行正常。
match dns_lookup {
send x00x01x00x00x00x01x00x00x00x00x00x00x06x68x65x61 ...;
expect ~* "healthy.svcs.example.com.";
}这种深入的应用层运行状况检查有助于发现潜在问题:即使名称服务器已正常运行,对生产域名执行实际DNS查询,也能发现配置问题和数据损坏,避免这些问题影响下游环节。
The NGINX Plus支持团队可协助为DNS查询及其他协议配置UDP运行状况检查。
以下代码片段重点标出了主动健康检查所需的其他指令。
stream { upstream dns_servers {
zone dns_mem 64k;
server 192.168.136.130:53 fail_timeout=60s;
server 192.168.136.131:53 fail_timeout=60s;
}
match dns_lookup { send x00x01x00x00x00x01x00x00x00x00x00x00x06x68x65x61 ...; expect ~* "healthy.svcs.example.com."; }
server {
listen 53 udp;
listen 53; #tcp
health_check match=dns_lookup interval=20 fails=2 passes=2 udp;
health_check interval=20 fails=1 passes=2 port=53; #tcp
proxy_pass dns_servers;
error_log /var/log/nginx/dns.log debug;
proxy_responses 1;
proxy_timeout 1s;
}
}The zone指令定义了一个名为dns_mem的共享内存区,使所有NGINX Plus工作进程都能访问健康检查结果(及其他状态信息)。
上文刚刚介绍了match指令。
The health_check指令包含多个参数,可根据环境进行调整。此处分别为UDP和TCP定义单独的健康检查。由于UDP与TCP存在差异,UDP健康检查需要连续失败两次,才会将DNS服务器标记为不健康,而TCP只需失败一次。对于这两种协议,都需要连续两次收到成功响应,才会将服务器重新标记为健康,以免向不稳定、状态频繁切换的服务器发送请求。
为UDP和TCP流量定义同一个DNS服务器上游组的优势在于,只要任一协议的运行状况检查失败,系统就会将该服务器标记为运行状况不佳,并将其从负载均衡池中移除。
规模化调优
虽然仅部署两台后端服务器即可构成有效的高可用性解决方案,但借助NGINX Plus的负载均衡功能,可以在客户端无感知的情况下横向扩展后端服务器。
上述示例微服务环境不太可能需要扩展后端DNS服务器。不过,为所有订阅用户提供DNS服务的ISP会面临持续负载以及负载大幅激增的可能性,因此需要部署大量DNS服务器,并使用前端代理在这些服务器之间对流量进行负载均衡。
NGINX和NGINX Plus的所有负载均衡算法均适用于TCP、UDP和HTTP:
- 轮询(默认)
- 通用哈希及其一致性变体(ketama算法)
- IP 哈希
- 最少连接
- 最短时间(仅限NGINX Plus)
(还可以为所有算法配置权重,进一步提高其效率。有关详细讨论,请参阅博客文章《选择NGINX Plus负载均衡技术》中有关权重的部分。)
HTTP请求给后端服务器带来的负载和处理需求可能差异很大,而DNS请求通常产生相同的负载。因此,最少连接和最短时间算法与轮询相比不太可能带来优势。尤其是,最少连接算法会将NGINX Plus仍在等待上游服务器响应的所有UDP请求计入连接数。只要proxy_responses和proxy_timeout的设定条件尚未满足,NGINX Plus就仍会统计与上游服务器的连接,即使这些服务器可能已经完成处理。
如果客户端数量众多,并且所用协议需要在客户端与服务器之间进行大量“对话”,即交换多条消息(如RADIUS质询-响应流程),则使用源IP哈希可让这些对话始终由同一台后端服务器处理。换言之,这会建立会话持久性,即NGINX Plus会将来自同一客户端的所有请求定向到同一台服务器。以下示例为一对RADIUS身份验证服务器配置Hash负载均衡算法,并将源(客户端)IP地址(由$remote_addr变量捕获)用作键。
upstream radius_servers { hash $remote_addr; # Source-IP hash
server 192.168.136.201:1812;
server 192.168.136.202:1812;
}更多信息
如需详细了解UDP和TCP负载均衡,请参阅以下资源:
- TCP 和 UDP 负载均衡,位于《NGINX Plus 管理指南》中
- NGINX Stream模块和
upstream配置上下文 - 部署指南,用于对必须同时处理基于TCP和HTTP的流量的Microsoft® Exchange™服务器进行负载均衡
- 使用NGINX Plus和Galera Cluster实现MySQL高可用性<.htmla>(TCP负载均衡用例)
要了解NGINX Plus R9的其他出色功能,请参阅博客文章“NGINX Plus R9发布公告”,并观看点播网络研讨会“NGINX Plus R9新功能”。
要试用NGINX Plus,可立即开始30天免费试用,或联系我们,讨论您的使用场景。
关于作者

相关博客文章
NGINX Plus R26 正式发布
NGINX Plus R26通过缓存JSON Web密钥集加快JWT验证,并强化TLS握手,从而提升安全性。NGINX JavaScript的新功能包括增强对异步函数的支持,以及实现WebCrypto API。
使用NGINX App Protect DoS保护gRPC应用,抵御严重的DoS攻击
gRPC具备开发人员设计和部署现代应用所需的速度与灵活性,但其开放性也使其极易受到第7层DoS攻击。NGINX App Protect DoS可保护gRPC应用免受此类破坏性攻击。
Ingress Controller 选型指南(第 2 部分):风险与面向未来的规划
了解选择错误的Ingress控制器可能带来的风险,以及如何从关键方面着手,让您的选择更好地适应未来需求。
NGINX Plus R19 正式发布
NGINX Plus R19扩展了监控功能,不仅提供更多指标,还新增了指标分析方式;增加试运行模式,用于测试限速效果;增强键值存储,以支持IP地址范围;并使限速更加动态。
NGINX现已正式加入F5
NGINX现已正式成为F5的一部分。Gus Robertson现任F5 NGINX业务部门负责人。他阐述了两家公司在人才、文化和愿景方面融合的价值,以及双方将如何携手增强NGINX的开源产品和商业产品。
