クラスタ構成のサーバでは、障害発生後にクライアントがすぐに復旧しない場合があります。サーバ側がフェイルオーバした後にクライアント側が再接続するまでの時間を短くする方法を紹介します。
クライアントからサーバに接続するとソケットはESTABLISHEDになります。もしESTABLISHEDになったソケットで正しくパケットが送信されなかった場合、OSは再送を試みます。再送に失敗してソケットをクローズするまでの時間はOSの設定によります。
OSがTCP接続の異常を検知してからクローズするまでの時間を短くするには3つの方法があります。
- パケットの再送回数を少なくする。
- TCPレイヤでKeep Aliveパケットを送信する。この方法はTCP Keep Aliveに対応しているアプリのみ可能。
- アプリケーションレイヤでKeep Aliveパケットを送信する。この方法はNullパケットを投げる等に対応しているアプリのみ可能。
例えばSSHでは上記3つの設定が可能ですが、Keep Aliveに対応していないアプリもあります。ここでは、1を適用した場合のTCPの挙動について考察します。
検証
SSHクライアントとSSHサーバの間のパケットが欠落した場合の挙動を確認してみます。障害試験は、SSH接続中にファイアウォールでパケットをドロップすることで実施します。
SSHクライアント | ファイアウォール | SSHサーバ |
---|---|---|
172.16.35.1 | 172.16.35.254 - 172.16.36.254 | 172.16.36.1 |
何も設定していない場合
約15分でソケットがクローズされる結果となりました。
時刻 | 作業手順 | SSHクライアントの ソケットステータス |
SSHサーバの ソケットステータス |
---|---|---|---|
02:48:10 | SSHクライアントからSSHサーバに接続し、1秒毎に日時を表示する。 | ESTABLISHED | ESTABLISHED |
02:50:30 | ファイアウォールでSSHをドロップするようポリシーを変更する。 | ESTABLISHED | ESTABLISHED |
03:05:56 | - | ESTABLISHED | CLOSED |
03:06:20 | SSHクライアントでEnterキーを押す | ESTABLISHED | CLOSED |
03:21:48 | SSHクライアントが異常終了 | CLOSED | CLOSED |
パケットログを見てみましょう。再送パケットを時系列に並べてみました。ちなみに初回はパケットログを取り忘れたのでもう一回やってます。
# | 時刻 | 経過秒 |
---|---|---|
0 | 03:53:10 | 0 |
1 | 03:53:11 | 1 |
2 | 03:53:11 | 1 |
3 | 03:53:13 | 3 |
4 | 03:53:16 | 6 |
5 | 03:53:23 | 13 |
6 | 03:53:36 | 26 |
7 | 03:54:01 | 51 |
8 | 03:54:53 | 103 |
9 | 03:56:36 | 206 |
10 | 03:58:36 | 326 |
11 | 04:00:36 | 446 |
12 | 04:02:36 | 566 |
13 | 04:04:36 | 686 |
14 | 04:06:36 | 806 |
15 | 04:08:36 | 926 |
CLOSED | 04:10:36 | 1046(17分26秒) |
間隔を2倍ずつ大きくして再送していることが分かります。Linuxカーネルでは再送回数のデフォルト値が15回に設定されています。これは /proc/sys/net/ipv4/tcp_retries2 で確認できます。
TCP再送回数を少なくする
再送回数を7回に設定してみましょう。/etc/sysctl.confに以下を追加してsysctl -pを実行します。
# /etc/sysctl.conf
net.ipv4.tcp_retries2 = 7
すると、約50秒でソケットがクローズされる結果となりました。
時刻 | 作業手順 | SSHクライアントの ソケットステータス |
SSHサーバの ソケットステータス |
---|---|---|---|
04:21:00 | SSHクライアントからSSHサーバに接続し、1秒毎に日時を表示する。 | ESTABLISHED | ESTABLISHED |
04:21:20 | ファイアウォールでSSHをドロップするようポリシーを変更する。 | ESTABLISHED | ESTABLISHED |
04:22:11 | - | ESTABLISHED | CLOSED |
パケットログを見ても、7回の再送でタイムアウトしていることが分かります。
# | 時刻 | 経過秒 |
---|---|---|
0 | 04:21:19 | 0 |
1 | 04:21:19 | 0 |
2 | 04:21:19 | 0 |
3 | 04:21:20 | 1 |
4 | 04:21:22 | 3 |
5 | 04:21:25 | 6 |
6 | 04:21:31 | 12 |
7 | 04:21:44 | 25 |
CLOSED | 04:22:11 | 52 |
再送回数をあまりにも少なくすると、ネットワークが瞬断しただけでTCP接続が切れてしまいます。ファイアウォールのフェイルオーバ時間より長い時間であれば問題ないでしょう。
NFSのようにTCP Keep Aliveを設定できないプロトコルでは、再送回数をデフォルトより少なくすることで障害復旧時の立ち直りが早くなるはずです。他ミドルへの影響を検討してからお試しください。
おまけ
1秒毎にソケットステータスをログるスクリプトです。
#!/bin/bash PORT=22 while true; do netstat -an --inet 2> /dev/null | grep ":$PORT" | sed -e "s,^,$(date +%Y-%m-%dT%H:%M:%S)\t,g" sleep 1 done