今月に入ってお客さんのサイトでapacheがハングアップして応答が無くなり、mysql,apacheの再起動を余儀なくされる状況が1日おき位に続いていた。対応が結構しんどかったので、何とか原因を見つけようとした作業記録です。
1. サーバ環境
webサーバ:1台:Redhat Linux 2.4.21-40(かなり古いです) mem=1G, cpu=1
DBサーバ:1台:Redhat Linux 2.6.9-42 mem=8G, cpu=4
2.LAMP構成
webサーバ:apache 2.2.6, php4.3.11
DBサーバ:mysql 4.1.22
3.現象
稼動しているとあるタイミング(主にアクセスが多い時間帯)にhttpdが無反応になる。apacheをリスタートしても状況が変わらず、apache停止→mysqlリスタート→apacheスタートの順で回復する。
4.やったこと
(1) mrtgでのプロセス監視
原因が良くわからないので、mrtgで各プロセスを監視することにした。測定内容はapacheのプロセス数、mysqlの接続数、各サーバの負荷など。
(2) apacheの設定変更
MaxClients 20
MaxKeepAliveRequests 5
KeepAlive On
KeepAliveTimeout 1
Timeout 30
とかなり控えめなプロセス数を設定。→ハングアップ周期は長くなるものの、改善に至らず。
(3) mysqlの設定変更
long_query_time=3
log-slow-queries=/var/log/slow.log
my-medium.cnfとほぼ同じ値を設定。(これもマシンスペックに対してはかなり控えめ)→ハングアップ周期は長くなるものの、改善に至らず。上記設定を追加して、実行速度の遅いクエリーを追跡することにした。
(4) php.iniの設定変更
mysql.max_links = 7
mysql.connect_timeout = 8
mysql.trace_mode = On
と接続控えめなプロセス数を設定。→ハングアップ周期は長くなるものの、改善に至らず。
trace_modeをOnにして、mysql接続時のphpエラーを監視することにした。
※追記
mysql.allow_persistent =Off
を追加して様子を見ています。持続的接続関数のmysql_pconnectを使ってないので、関係無いと思うけど設定してみました。
5. 解決策
まず、mysqlの実行速度の遅いクエリーをつぶすことにした。(上記(3)の設定で)
LEFT JOINを使うSQLが特に問題となっていたので、プログラム内でsqlを2つに分けたりしてLEFT JOINを使わない形に変更した。これでハングアップ周期は伸びたが、まだ完全には解決しなかった。
そこでサーバがハングアップした際、web,dbサーバのそれぞれでnetstatコマンドを実行してみると、3306ポートで大量のTIME_WAITが滞留していることに気づいた。このTIME_WAIT状態でネットワークリソースが枯渇すると考え、この状況を改善する方法を調査したところ、sysctlでカーネルの設定値が変更することが分かった。各サーバで下記コマンドを実行。
# /sbin/sysctl -w net.ipv4.tcp_tw_recycle=1
/etc/sysctl.confにも
net.ipv4.tcp_tw_recycle=1
net.ipv4.tcp_fin_timeout=10
net.ipv4.ip_no_pmtu_disc=1
を追加。
この設定により上記状況は改善された。(たぶん。。)