NLBのヘルスチェックが原因でMySQLが接続ホストをブロックしてしまった話


事象

以下の構成でMySQLを起動しておりました。

NLB経由でMySQLに対してリクエストを投げると以下のエラーが...

Host 'host_name' is blocked because of many connection errors;
unblock with 'mysqladmin flush-hosts'

以下によると
https://dev.mysql.com/doc/refman/5.6/ja/blocked-host.html

mysqld が途中で中断された多数の接続要求を特定のホストから受け取ったことを意味する。
接続が成功せずに max_connect_errorsに設定されている回数要求が失敗すると、
mysqld は何らかの問題があると見なし、FLUSH HOSTS ステートメントを発行するか、
mysqladmin flush-hosts コマンドを実行するまでそのホストが接続できないようにします。

と記載がありました。

NLBのモニタリングのクライアントのリセット回数を確認するとたしかに大量のRSTパケットが送信されておりました。

原因

まず前提として、NLBには以下の仕様があります。

  • ヘルスチェックのために作成された3way handshakeの後にターゲットから応答があるとターゲットに対してRSTパケットを送信する
  • NLB ではコンセンサスメカニズムを使用してヘルスチェックを行っていることから、設定しているヘルスチェック数よりも多くのヘルスチェックが行われる

また、tcpdumpで確認したころ、MySQLは3way handshake後にPSHのパケットを送信しております。
※192.168.2.14 -> NLB , 192-168-2-45 -> EC2(MySQL)

# tcpdump host 192.168.2.14 and port 3306
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 262144 bytes
00:44:53.081285 IP ip-192-168-2-14.ap-northeast-1.compute.internal.54412 > ip-192-168-2-45.ap-northeast-1.compute.internal.mysql: Flags [S], seq 2207875670, win 26883, options [mss 8961,sackOK,TS val 2151484336 ecr 0,nop,wscale 7], length 0
00:44:53.081324 IP ip-192-168-2-45.ap-northeast-1.compute.internal.mysql > ip-192-168-2-14.ap-northeast-1.compute.internal.54412: Flags [S.], seq 169259182, ack 2207875671, win 26847, options [mss 8961,sackOK,TS val 957708083 ecr 2151484336,nop,wscale 7], length 0
00:44:53.081684 IP ip-192-168-2-14.ap-northeast-1.compute.internal.54412 > ip-192-168-2-45.ap-northeast-1.compute.internal.mysql: Flags [.], ack 1, win 211, options [nop,nop,TS val 2151484336 ecr 957708083], length 0
00:44:53.081939 IP ip-192-168-2-45.ap-northeast-1.compute.internal.mysql > ip-192-168-2-14.ap-northeast-1.compute.internal.54412: Flags [P.], seq 1:79, ack 1, win 210, options [nop,nop,TS val 957708083 ecr 2151484336], length 78
00:44:53.082337 IP ip-192-168-2-14.ap-northeast-1.compute.internal.54412 > ip-192-168-2-45.ap-northeast-1.compute.internal.mysql: Flags [.], ack 79, win 211, options [nop,nop,TS val 2151484337 ecr 957708083], length 0
00:45:03.087133 IP ip-192-168-2-45.ap-northeast-1.compute.internal.mysql > ip-192-168-2-14.ap-northeast-1.compute.internal.54412: Flags [F.], seq 79, ack 1, win 210, options [nop,nop,TS val 957718088 ecr 2151484337], length 0
00:45:03.128518 IP ip-192-168-2-14.ap-northeast-1.compute.internal.54412 > ip-192-168-2-45.ap-northeast-1.compute.internal.mysql: Flags [.], ack 80, win 211, options [nop,nop,TS val 2151494383 ecr 957718088], length 0

上記のことから、
ヘルスチェックの3way handshake後にMySQLがNLBに対してPSHパケットを送信したことにより、NLBはRSTパケットをMySQLに対して送信

そのヘルスチェックが上記の仕様より設定回数以上実行されていたため結果として大量のRSTパケットがMySQLに送信される

max_connect_errorsの設定値以上接続失敗

MySQL接続ホストをブロック

ということでした。

回避策

以下のいずれかの方法で回避可能です。

  • ヘルスチェックのポートをMySQL以外のポートに変更
  • max_connect_errorsの値をmax値に設定する(根本的な解決にはなりませんが)