2011年12月1日木曜日

TCP keepalive設定でハーフコネクションを解消


2台のサーバ間がTCPハーフコネクション状態になる事象が発生した。
TCP keepalive(キープアライブ)の設定でその状態を解消するための手段をメモしておく。


構成
サーバAとサーバB


アプリケーション仕様
サーバAがサーバB上のファイルを定期的に取得する。
両サーバ間はTCPコネクションを張り続けている。通信ごとに接続する仕様ではない。


トラブル
サーバBに障害が発生し、サーバBが突然ダウンした。
そのためサーバBからサーバAへのTCPのクローズ処理が行われず、
サーバAだけにTCPの状態がESTABLISHEDで残るハーフコネクションとなってしまった
LANケーブルが抜けた場合なども同じ状態になるだろうか。

ハーフコネクションの有無は、サーバA、サーバBでそれぞれ
"lsof -n"、"netstat -an" などのコマンドを打ち、状態がESTABLISHEDになっている行で
対になっていないものがあるかどうかで確認できる。

サーバAはコネクションが残っていると思い込んでいるため、その経路を使おうとする。
しかしサーバB側では既にコネクションは維持されていないので接続できない。


TCPのオープン、クローズ処理に関しては下が参考になるだろう。
TCPのオープンとクローズ処理
TCPの状態遷移図



疑問
サーバAをサーバごと再起動(もしくはネットワークの再起動)すればハーフコネクションの状態からは復旧するだろう。しかし商用のサーバではそうそう再起動などかけられない。サーバは稼働し続けているという条件の元で、異常なハーフコネクションはいつまで続くのだろうか。



現在の設定値の確認
TCPキープアライブの設定周りを確認すればよいだろう。

まずはキープアライブプローブが送信される時間を確認する(単位は秒)。
これは接続がアイドル状態になってからである。つまり接続がアイドル状態でなければキープアライブプローブは送信されない。
# sysctl -a | grep tcp_keepalive_time
7200

相手からキープアライブ応答がなかった場合のキープアライブプローブの間隔を確認する(単位は秒)。
# sysctl -a | grep tcp_keepalive_intvl
75

キープアライブプローブの最大回数を確認する。
この回数だけ試しても接続先から反応が得られない場合に接続は切断される(単位は回)。
# sysctl -a | grep tcp_keepalive_probes
9

ディフォルト設定値では7200(秒)+75(秒)×9(回)の間はハーフコネクションの状態が続いてしまう。2時間以上というのは長すぎるだろう。

補足
sysctlコマンドで確認したものはサーバ起動時に読み込まれる設定であるため起動後に変更をしていれば現在の設定と相違がある可能性がある。grepした文言名で/proc/sys/net/配下あたりのファイルを見た方が正確だろう。



恒久的な設定変更
OS再起動時にも設定を有効にさせる。
# cp /etc/sysctl.conf /etc/sysctl.conf.YYYYMMDD

# vi /etc/sysctl.conf
net.ipv4.tcp_keepalive_time = 60
net.ipv4.tcp_keepalive_intvl = 3
net.ipv4.tcp_keepalive_probes = 3

変更を反映させる。
# sysctl -w

変更を確認する。
# sysctl -a



確認
発生した障害と同じ条件を作り、設定した時間でサーバAのハーフコネクションが切断されることを確認する。しかし、意図した時間が経ってもコネクションは切断されず、残り続けてしまった。

このページのUsing TCP keepalive under Linux の章を読み原因が分かった。

『Remember that keepalive support, even if configured in the kernel, is not the default behavior in Linux.
Programs must request keepalive control for their sockets using the setsockopt interface.
There are relatively few programs implementing keepalive,
but you can easily add keepalive support for most of them following the instructions explained later in this document. 』

つまり、アプリケーション側からカーネルのTCPキープアライブの設定をコントロールすることができるわけである。

こちらで使っているアプリケーションではキープアライブの設定を有効にするかどうかを
決めるコンフィグのキーが存在した。アプリケーションに依存する部分なのでどのキーかは記載しない。アプリケーションごとに仕様を確かめてほしい。

キープアライブを制御するようなコンフィグがなければ、ソースを見るしかないだろう。
setsockopt関数あたりで制御しているようである。



(参考)
カーネルパラメータのチューニング