さくらのVPS(v3) セットアップ備忘録(3) - httpdとTomcatの連携設定変更

httpdTomcatを連携させて運用していると、数十時間〜数日後にTomcatが応答しなくなる(503 Service Temporarily Unavailable になる)症状が発生。どうもajp通信のどちら側かがスタックしたままコネクションの上限数を使い尽くしてしまっているようで、発症の都度Tomcatを再起動させなければなりませんでした。

ajp通信をタイムアウトさせれば良いかと思い、Tomcatのserver.xmlにて

となっている部分に
connectionTimeout="300000"
を追加してみたところ、ひとまず上記の症状はなくなりました。

ところが今度は、httpd側が開くコネクションが(Tomcat側がタイムアウトして切断されても)完全には解放されずにCLOSE_WAIT状態で200個以上溜まってしまうようになりました。今のところ実害は出ていませんが、気持ち悪いし、ほうっておいたら問題の温床にはなりそうなので、調査続行。

いろいろ彷徨った後、たどり着いた情報はこのあたり。

httpd側のコネクションにも、Tomcatに設定したのと同等のタイムアウトを設定しなきゃならない(勝手にチェックして閉じてはくれない)様子。で、mod_jkの場合については示唆が見つかるのだけど、mod_proxy_ajpの場合の情報がなかなか見つからない。ただ、本家のドキュメントを見るかぎり、ProxyPassディレクティブにパラメタttlを設定すれば良さそうです。

が、しかーし。今度はこんな情報が。

うちはデフォルトの prefork MPM のまま使ってたので、これを worker MPM に変更しなきゃならなさげ。phpを動かすには prefork が無難みたいですが、もう仕方ないので諦めて。

を参考にして、


$yum install php-zts
$sudo vi /etc/sysconfig/httpd
 
#HTTPD=/usr/sbin/httpd.worker

HTTPD=/usr/sbin/httpd.worker
 
$/etc/init.d/httpd restart
$apachectl -V

プロセス名も httpd だったのが httpd.worker になって、数も減っているのを確認。

以上やった結果は・・・・・

・・・駄目だぁ。前よりCLOSE_WAIT状態のコネクションの数が減りはしたけど、やっぱり残っちゃう。
さらに継続調査ですね。。。


[ 6/21追記 ]
ProxyPassディレクティブのパラメタにkeepalive=onをつけると上手くいきました。


<Location /app>
  ProxyPass ajp://localhost:8009/app keepalive=on
  Order allow,deny
  Allow from all
</Location>

『... このフラグは OS に指示して、KEEP_ALIVE メッセージを非活動状態の コネクションでも送るようにします。これによってファイアウォールによってコネクションが 落とされることを防げます。keepalive を有効にするには、このプロパティを On にしてください。
初期およびその後の TCP キープアライブの間隔は OS のグローバル設定に依存しますが ...』

つまりkeepalive=onによって、コネクションにKEEP_ALIVEメッセージが流れるようになります。で、その動作はOSの tcp_keepalive_time, tcp_keepalive_probes, tcp_keepalive_intvl に従うわけですが、man tcpを見ると

  • tcp_keepalive_intvl (integer; default: 75; Linux 2.4 以降)
    • TCP keep-alive のプローブを送る間隔 (秒単位)。
  • tcp_keepalive_probes (integer; default: 9; Linux 2.2 以降)
    • TCP keep-alive プローブの最大回数。この回数だけ試しても接続先から反応が得られない場合は、あきらめて接続を切断する。
  • tcp_keepalive_time (integer; default: 7200; Linux 2.2 以降)
    • 接続がアイドル状態になってから、keep-alive プローブを送信するまでの時間を秒単位で指定する。SO_KEEPALIVE ソケットオプションが有効になっていないと keep-alive は送られない。デフォルト値は 7200秒 (2時間)。 keep-alive が有効になっている場合、さらにおよそ 11 分 (75 秒間隔の 9 プローブ分) 経過するとアイドル状態の接続は終了させられる。

      下層にある接続追跡機構やアプリケーションでのタイムアウトは、もっとずっと短いかもしれない。

結果として、TCP keep-alive プローブが一定回数だけ失敗すると接続が切断されるようになったようです。
切断までの間隔を短くするには、こちらを参考に

一時的でよければ


echo 240 > /proc/sys/net/ipv4/tcp_keepalive_time
echo 6 > /proc/sys/net/ipv4/tcp_keepalive_probes
echo 10 > /proc/sys/net/ipv4/tcp_keepalive_intvl
恒久的には

# cat >> /etc/sysctl.conf

net.ipv4.tcp_keepalive_time = 240
net.ipv4.tcp_keepalive_probes = 6
net.ipv4.tcp_keepalive_intvl = 10
# sysctl -w (反映)
# sysctl -p (確認)

Tomcat側は <Connector port="8009" protocol="AJP/1.3" connectionTimeout="3600000" に変えて様子見中。

結論は、
「Apache2.2+mod_proxy_ajpは、使い終わってプールに戻したコネクションを監視しないので、切断されたコネクションがプールに溜まるのを防ぐには、ProxyPassディレクティブのパラメタkeepaliveにより、OSにTCP keep-aliveをさせる必要がある。」