Apache Prefork環境での子プロセス数チューニングの勘どころ

vector-wrench-and-screwdriver-xxl-icon_sizeXS.jpg

チューニングされていない Apache をそのまま使っていると、環境によってはトラフィック集中時に子プロセスが山ほど立ち上がってメモリが不足し Swap が多発、最悪、OOM Killer によりあらゆるプロセスが殺されまくる事態となり、サーバーダウンを引き起こします。

ということで、Apache Prefork 環境でこういった事態を避けるには、子プロセス絡みのパラメータをどうチューニングすれば良いのか。その勘どころをメモしておきます。

まず、サーバで以下のコマンドを実行し、キャッシュなどを除いたメモリの余裕をチェックします。

$ free

具体的には、「-/+ buffers/cache」の free の値を確認します。

その後、次のコマンドのRSS列を見て、httpd子プロセスが利用する平均実メモリ容量を算出します。

$ ps aux

この値と先の free の値を元に、httpd 子プロセスをいくつ追加できるかを見積もる。というのが、各所でよく紹介されているチューニング方法です。

が、ここに以下2点も加えて考慮できれば、より良い結果を得られると思います。

  1. メモリの余力を丸ごと Apache の子プロセスに割り振らない
  2. Copy on Write(CoW)による実メモリ消費量の効率化を意識する

「1」が言わんとするのは、メモリをある程度 buffers や cached に振らないとI/Oパフォーマンスに悪影響がある。ということです。もちろん、そこには slab キャッシュも考慮に入れる必要があります。

小さめのクラウドサーバ上でCMSを運用する場合だと、メモリの20~30%を buffers, cache に割り当てるのが典型のような。ただし、具体的な設定値は環境によって大きく異なります。見積もりできない場合は、計測と勉強をしながら勘どころを掴む感じになるでしょう。

「2」は、CoWにより、子プロセスの実メモリのフットプリントが節約されている分を考慮に入れてもよい。ということです。

実際にチューニングしてみると分かりますが、Apache の子プロセスを増やしても、free コマンドで確認できる空きメモリ容量は、ps コマンドで確認できる Apache 子プロセス1つあたりの物理メモリ量ほどには減りません。

これはつまり、CoWにより実メモリの使用量が削減されている。ということです。

プロセスがどの程度メモリを他プロセスと共有しているのか、を調べるには、topコマンドのRESとSHRを見るのが手軽と思います。

ただ、ある程度正確に捉えるには、次の記事の方法も検討すると良いと思います。

以下、スクリプトを引用させていただきます。

#!/bin/sh

echo -e "PID\tVSZ\tRSS\tShared"
for pid in $@; do
    smaps="/proc/$pid/smaps"
    vsz=$(grep -E "^Size" $smaps | awk 'BEGIN{ num = 0 } { num += $2 } END{ print num }')
    rss=$(grep -E "^Rss" $smaps | awk 'BEGIN{ num = 0 } { num += $2 } END{ print num }')
    shared=$(grep -E "^Shared" $smaps | awk 'BEGIN{ num = 0 } { num += $2 } END{ print num }')
    percent=$(echo "scale=2; ($shared / $rss) * 100" | bc | cut -d "." -f 1)
    echo -e "$pid\t${vsz}KB\t${rss}KB\t${shared}KB(${percent}%)"
done

実行時はこんな感じになると思います。

$ ./smaps.sh $(pgrep httpd)

ここで忘れてはいけないのは、Apache 子プロセスの実メモリのフットプリントは最初は小さいが、運用を続けるうちに段々実メモリを喰うようになり、最終的にある値に落ち着く傾向がある。つまり、CoWの効果は時間経過と共に変化する。という点です。

そこも踏まえた上で、Apache 子プロセス1つあたりどの程度の実メモリを消費するのか、を見積もる必要がある。ということです。

サーバのメモリをどう活用するか。はチューニングの重要ポイントです。I/Oの速さに自信があるならキャッシュ少なめで同時接続数を稼ぐ。とか、同時接続数を絞ってメモリをキャッシュに潤沢に振ってさらに速度を追求する。とか、そういうポリシーを持ってチューニングする形になります。

こういった判断をするには当然、アプリケーション特性を押さえている必要があり、例えばそれがI/Oインテンシブなものなのか、メモリインテンシブなのか、CPU喰らいなのか、といった部分の認識が必要になってくる。ということであります。

そういう色々を踏まえた上で、prefork のプロセス数を許容限界近くに固定してしまう。具体的には、StartServer, MinSpareServers, MaxSpareServers, ServerLimit, MaxClients を全て同じ値にしておく。というのが、僕の個人的な好みの設定にはなります。

Apache 2.4 からは worker がメインになりそうな感じなので、細かい部分は多少、違いは出てくるとは思いますが、基本的な考え方は割と流用できる部分もあるんじゃ?とは思ったり。

チューニングの確認項目

何をどこまで確認するか、という部分ではありますが、チューニング前後で最低でも以下くらいは見ておいた方が良いとは思います。

  • Disk IOs / latency
  • Apache のエラーコード付きレスポンスの比率の変化
  • メモリ / cache / Swap
  • Interrupts / Context switches
  • Load average

参考情報:

Hatena Pocket Line
last update 2015年3月10日

コメントを記入