YAPC::Kyoto 2023、JANOG51 MeetingではDNSへの水責めの攻撃とその対策について話をさせていただきました。その中で DNS攻撃をフィルタリングするために利用している dnsdist についてチューニングにより大きくパフォーマンス向上できることがわかってきたので紹介します。
YAPCの記事 kazeburo.hatenablog.com
JANOG51 Meetingについての記事 knowledge.sakura.ad.jp
Linuxのネットワークパラメータのチューニング
Linuxのチューニングでよくある設定ですが、sysctl.conf で以下のカーネルパラメータをチューニングをします。
net.core.somaxconn = 65535 net.core.netdev_max_backlog = 16384 net.core.rmem_max = 134217728 net.core.wmem_max = 134217728
ここでは rmem_max
、wmem_max
だけを設定しています。 net.core.rmem_default
、 net.core.wmem_default
については後述します。
dnsdistのチューニング
listenするスレッド数を増やす
dnsdistでは addLocal
を増やすことでlistenerスレッドの数を増やせます。
addLocal("0.0.0.0:53", {reusePort=true}) addLocal("0.0.0.0:53", {reusePort=true}) addLocal("0.0.0.0:53", {reusePort=true}) addLocal("0.0.0.0:53", {reusePort=true})
この際に reusePort
を有効にするとそれぞれのスレッドにListen Queueが分散されるので高速化が見込めます。
また、CPU affinityを固定するのも良いでしょう
addLocal("0.0.0.0:53", {reusePort=true,cpus={0}}) addLocal("0.0.0.0:53", {reusePort=true,cpus={1}}) addLocal("0.0.0.0:53", {reusePort=true,cpus={2}}) addLocal("0.0.0.0:53", {reusePort=true,cpus={3}})
さらにTCPの接続が多い場合、キューのサイズを増やしておくと良いかもしれません
addLocal("0.0.0.0:53", {reusePort=true,cpus={0},tcpListenQueueSize=4096}) addLocal("0.0.0.0:53", {reusePort=true,cpus={1},tcpListenQueueSize=4096}) addLocal("0.0.0.0:53", {reusePort=true,cpus={2},tcpListenQueueSize=4096}) addLocal("0.0.0.0:53", {reusePort=true,cpus={3},tcpListenQueueSize=4096})
UDPのバッファサイズの調整
UDPの読み書きのバッファのサイズを大きくします。デフォルトはカーネルパラメータの net.core.rmem_default
、net.core.wmem_default
になります。なのでsysctlで設定することもできます。
setUDPSocketBufferSizes(8388608,8388608)
カーネルパラメータの net.core.rmem_max
、net.core.wmem_max
が設定できる最大値となります。
複数のUDPによるクエリを1度のシステムコールで読み込む
recvmmsg(2)
を使うことで1度のシステムコールで複数のUDPによるクエリを読み込むことができます。 setUDPMultipleMessagesVectorSize
はその最大個数を指定します。
setUDPMultipleMessagesVectorSize(100)
デフォルトは1で、通常の recvmsg(2)
を使います。
ベンチマークの効果
さくらのクラウドの8コアの仮想サーバにて自作のDNS水責めベンチマークツールを使い検証を行いました。
dnsdistのみの検証のため、設定に
addAction( AllRule(), RCodeAction(DNSRCode.REFUSED) )
を設定し、すべてのクエリに即時 REFUSED
を返すように設定してあります。
初期状態
Listenerスレッドが1個の状態です。
# GOGC=500 ./prsd-bench4 -P 53 -H 192.168.10.50 --max-workers 1000 --max-length 8 --label 1 --zone example.com 2> /dev/null 2023-05-24 16:09:52.842209747 +0900 JST m=+10.002875416 resolved: 0.000000 query/sec, refused 231067.300000 query/sec, failed 72.200000 query/sec 2023-05-24 16:10:02.842144106 +0900 JST m=+20.002809775 resolved: 0.000000 query/sec, refused 220423.400000 query/sec, failed 144.400000 query/sec 2023-05-24 16:10:12.842143685 +0900 JST m=+30.002809354 resolved: 0.000000 query/sec, refused 230264.600000 query/sec, failed 144.400000 query/sec 2023-05-24 16:10:22.84215706 +0900 JST m=+40.002822729 resolved: 0.000000 query/sec, refused 220053.900000 query/sec, failed 144.400000 query/sec 2023-05-24 16:10:32.842160142 +0900 JST m=+50.002825810 resolved: 0.000000 query/sec, refused 227706.300000 query/sec, failed 144.400000 query/sec 2023-05-24 16:10:42.840047568 +0900 JST m=+60.000713237 resolved: 0.000000 query/sec, refused 232174.600000 query/sec, failed 144.400000 query/sec
20万qps以上は出ていますが、エラーもちらほらあります。
チューニング後
listenerスレッドを仮想コア数と同じ8個として、紹介したチューニングを全て入れている状態です。
# GOGC=500 ./prsd-bench4 -P 53 -H 192.168.10.50 --max-workers 1000 --max-length 8 --label 1 --zone example.com 2> /dev/null 2023-05-24 16:21:29.396835468 +0900 JST m=+10.001673734 resolved: 0.000000 query/sec, refused 417461.100000 query/sec, failed 0.000000 query/sec 2023-05-24 16:21:39.396746836 +0900 JST m=+20.001585106 resolved: 0.000000 query/sec, refused 445043.800000 query/sec, failed 0.000000 query/sec 2023-05-24 16:21:49.396666734 +0900 JST m=+30.001505003 resolved: 0.000000 query/sec, refused 431644.600000 query/sec, failed 0.000000 query/sec 2023-05-24 16:21:59.396818157 +0900 JST m=+40.001656423 resolved: 0.000000 query/sec, refused 438897.200000 query/sec, failed 0.000000 query/sec 2023-05-24 16:22:09.396665403 +0900 JST m=+50.001503669 resolved: 0.000000 query/sec, refused 440108.300000 query/sec, failed 0.000000 query/sec 2023-05-24 16:22:19.395664578 +0900 JST m=+60.000502848 resolved: 0.000000 query/sec, refused 425201.300000 query/sec, failed 0.000000 query/sec
エラーなく40万qps以上処理できるようになりました。
まとめ
権威DNSサーバであるNSDでは、UDPのバッファサイズ、recvmmsgの利用などはデフォルトで行われており、dnsdistパフォーマンスの調査をするにあたり参考にしました。
この記事がDNSサーバのパフォーマンスで困った際にお役に立てれば幸いです。