我在负载均衡器后面运行 BIND 9.8.2(RHEL6)的 DNS 解析器上遇到了随机问题。
负载均衡器正在使用 DNS 查询主动探测服务器以确定它是否处于活动状态,并且时不时地会在端口 53 上收到“连接被拒绝”(ICMP),这会导致服务器在负载均衡器服务器池中短暂无法提供服务。
这种情况会间歇性地发生(LB 每 10 秒探测一次,我们通常会看到 1 或 2 次失败,然后成功),每次持续几分钟,然后停止,它发生在高峰负载时间,但它是在流量最低时检测到的。
问题发生时,BIND 已启动并正在运行,这会使“连接被拒绝”消息令人困惑,我原以为这是来自没有端口 53 监听服务的服务器,但事实并非如此。即使探测记录的 DNS 解析失败,答案也将是 NXDOMAIN 或 SERVFAIL,而不是完全的 UDP 拒绝。
查询日志没有显示失败的探测,这意味着 UDP 数据包在传送到 BIND 进行处理之前被拒绝。
我最好的猜测是,这是由某种资源耗尽引起的,但我搞不清楚具体是什么。我们正在监控打开的文件描述符、net.netfilter.nf_conntrack_count
CPU、内存recursive-clients
等,当问题发生时,这些指标都没有接近极限。
当问题发生时,所有日志文件都没有任何相关的错误消息。
所以,我的问题是:我应该研究哪些网络指标/参数?
配置
/etc/sysconfig/命名
OPTIONS="-4 -n10 -S 8096 "
命名配置文件
options {
directory "/var/named";
pid-file "/var/run/named/named.pid";
statistics-file "/var/named/named.stats";
dump-file "/var/named/named_dump.db";
zone-statistics yes;
version "Not disclosed";
listen-on-v6 { any; };
allow-query { clients; privatenets; };
recursion yes; // default
allow-recursion { clients; privatenets; };
allow-query-cache { clients; privatenets; };
recursive-clients 10000;
resolver-query-timeout 5;
dnssec-validation no;
//querylog no;
allow-transfer { xfer; };
transfer-format many-answers;
max-transfer-time-in 10;
notify yes; // default
blackhole { bogusnets; };
// avoid reserved ports
avoid-v4-udp-ports { 1935; 2605; 4321; 6514; range 8610 8614; };
avoid-v6-udp-ports { 1935; 2605; 4321; 6514; range 8610 8614; };
max-cache-ttl 10800; // 3h
response-policy {
zone "rpz";
zone "rpz2";
zone "static";
};
rate-limit {
window 2; // seconds rolling window
ipv4-prefix-length 32;
nxdomains-per-second 5;
nodata-per-second 3;
errors-per-second 3;
};
};
编辑:UDP 接收错误
$ netstat -su
IcmpMsg:
InType3: 1031554
InType8: 115696
InType11: 178078
OutType0: 115696
OutType3: 502911
OutType11: 3
Udp:
27777696664 packets received
498707 packets to unknown port received.
262343581 packet receive errors
53292481120 packets sent
RcvbufErrors: 273483
SndbufErrors: 3266
UdpLite:
IpExt:
InMcastPkts: 6
InOctets: 2371795573882
OutOctets: 13360262499718
InMcastOctets: 216
编辑 2:网络内存大小
$ cat /proc/sys/net/core/rmem_max
67108864
$ cat /proc/sys/net/ipv4/udp_mem
752928 1003904 1505856
编辑 3:UdpInErrors 没有问题
发生了探测失败事件,但 UDP 数据包接收错误并没有相应增加,因此这似乎被排除了原因。
编辑 4:这里可能有 2 个问题,有些故障实例有相应的 UdpInErrors 增加,有些则没有
我发现了一个与 UDP 接收错误问题相关的事件:
我已经将内核内存值增加到:
# cat /proc/sys/net/core/wmem_max
67108864
# cat /proc/sys/net/core/rmem_max
67108864
# cat /proc/sys/net/ipv4/udp_rmem_min
8192
# cat /proc/sys/net/ipv4/udp_wmem_min
8192
这似乎与负载无关,具有相同甚至更大工作负载的类似服务器没有出现任何问题,而同时,同一负载均衡器后面的另一台服务器在该时间段内表现出完全相同的行为。
编辑 5:在 BIND 的统计通道数据中发现一些 TCP 问题的证据
发现 UDP 数据包接收错误率高与BIND 的指标TCP4ConnFail
之间存在相关性TCP4SendErr
统计渠道数据。
尽管这种增加的规模与 UDPInErrors 不同,但它与 UDPInErrors 密切相关,因为这种影响在其他时间并不存在。
编辑 6:情节变得更加复杂... 系统日志似乎是一个促成因素
受影响的 DNS 服务器配置为将查询日志记录到 sysloglocal0
设备,然后通过 UDP 将其转发到远程服务器。这在过去是造成性能问题的原因,事实上,出于这个原因,我们没有在最繁忙的 DNS 服务器上启用它。
我尝试禁用查询日志,似乎udpInErrors
问题消失了,所以我做了一个实验。我们有 2 台服务器位于同一个负载均衡器后面,我让服务器 A 保持查询日志处于活动状态作为控制,并禁用服务器 B 上的查询日志(以及 syslog 的转发)。问题不再发生在两台服务器上在同一时间。
然后我在 serverB 上重新启用了查询日志记录,问题再次出现在两台服务器上!
我们曾两次尝试过此操作,一次是在轻负载下,一次是在最繁忙的时候,但只在繁忙的时候才会出现这种情况,因此似乎在一定程度上与负载有关。
下图显示了网络流量和 UDP 接收错误之间的关系。
在编辑5中提到的TCP指标中也可以发现同样的增加效果。
因此,根本原因似乎与网络有关,网络会级联并产生所有显示的症状。棘手的部分是这些服务器(实际上是虚拟机)在单独的基础设施上运行(有时是不同的机架,甚至是房间),因此它们之间不应该有任何影响传播。
它似乎与查询日志本身无关,否则其影响就不会在服务器之间传播。
我想到的一件事是,接收 syslog 服务器没有路由回这些服务器,所以我们从来没有收到任何回复数据包(如果有的话),所以 syslog 采取了尽力而为的“发射后不管”的方式。这可能是原因吗?
答案1
Udp:
27777696664 packets received
...
262343581 packet receive errors
每 1 个 UDP 接收错误接收 105.88 个数据报,DNS 数据包丢失率极高,这表明您的软件存在瓶颈。如果内核尝试交出数据包时与 UDP 套接字关联的接收队列已满,则数据包丢失,此计数器将递增。这意味着您丢失了超过两亿个数据包自上次重启该服务器以来就一直存在这种现象。
接收队列已满意味着软件无法足够快地从内核中删除数据包,从而造成数据包积压,最终超过缓冲区的大小。下一步应该是确定为什么队列太长了。作为例子,我们的一位用户发现由于磁盘日志记录过多,系统已饱和 iowait。除了建议您查看所有 SNMP 性能指标并寻找相关性之外,我无法提供全面的指南来识别根本原因。如果您找不到任何相关性,则可能是您的硬件+软件堆栈已满负荷运行,最好通过投入更多服务器来解决问题。
顺便提一下,可以增加接收队列的大小,但这最好被视为处理突发流量的能力。如果突发流量不是为什么会出现队列溢出,较大的队列大小会导致处理您所需的流量的时间更长做accept,这可能是不期望的。BIND 将使用最大 32K 的接收队列深度,但限制为 指定的值/proc/sys/net/rmem_max
。可以通过使用 重新编译 BIND 来进一步增加此数字--with-tuning=large 选项,这将潜在最大值从 32K 增加到 16M。