サーバーのメモリと向き合ってみる 〜MaxClientsの設定〜

この記事はだいぶ前に書かれたものなので情報が古いかもしれません
無駄遣いはしてないんだけどね

この記事を三行にまとめると

MaxClientsの設定を変更してプロセスの数を制御してみたい
同時接続数が16って決して少なくないのよ
よほど稼ぎがないとプロミスに頼っても賄えないですね
サーバーがダウンする理由はいろいろあると思いますが、その中の一つにメモリ不足というケースがあります。例えば瞬間的にやばいくらいにアクセスが集中してメモリの使用量が限界を超えてしまうとかですね。強化系なのにダブルを作ろうと頑張ってしまったカストロのように。

もしこんな感じ(↓)のエラーメッセージがログに出ていたら、メモリが足りなくなっている証拠です。メモリ不足によって新しいプロセスを立ち上げられなくなっている状態です。

Cannot allocate memory: fork: Unable to fork new process

これを防ぐには、例えばスワップ領域を適切に確保しておく方法などがあります。でもスワップと聞くとどうしても昔FXでやらかした苦い記憶が蘇ってくるので、今回は目を背けました。当時はポンドとかユーロのスワップポイントが良い感じだったから高いレバレッジでそこそこの金を突っ込んで小遣い稼ぎしてたんだけど、リーマンショックだかサブプライムローンだかの影響により一瞬で値ががくーっと下がってあっという間にロスカットされてしまったのさ。

まあそんな黒歴史は置いといて、今日はメモリが足りなくならないようにMaxClientsの設定を変更してプロセスの数を制御してみたいと思います。と言っても完璧には理解できてないので、間違ってる部分があったら突っ込んでください。FXに大金を突っ込むよりははるかに健全ですので、遠慮なくお願いしやす。

いつも通り前置きが長いので、読み飛ばすならhttpdプロセスの設定に関する項目か、いくつに設定すれば良いかにGo。



プロセスについて

本題に入る前に、サーバーのプロセスについて少々。

プロセスってのは、例えばFXに金を突っ込みすぎてロスカットされた結果、明日を生きるお金すらも失ってしまった時に、無理のない返済計画で当面の生活費を何とかしてくれる正義の味方です……って、そりゃプロセスじゃなくてプロミスやないかーい。

プロセスというのは、サーバー上で実行されるタスクのことです。一口にサーバーって言っても、ウェブサイトへのリクエストを処理したり、クーロンを実行したり、メールの送受信を行なったり、内部ではいろんな処理を行なっています。そういった処理たちを管理しているのがプロセスです。

どんなプロセスが実行中なのかは、topコマンドやpsコマンドで確認できます。

//topコマンド実行
# top

//実行結果(の一部)
top - 11:01:06 up 271 days, 7:58, 1 user, load average: 0.00, 0.01, 0.05
Tasks: 76 total, 1 running, 75 sleeping, 0 stopped, 0 zombie
Cpu(s): 0.0%us, 0.0%sy, 0.0%ni,100.0%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
Mem: 504064k total, 223636k used, 280428k free, 12556k buffers
Swap: 0k total, 0k used, 0k free, 56884k cached

  PID USER   PR NI  VIRT  RES  SHR S %CPU %MEM   TIME+  COMMAND
    1 root   20  0 19480  368   48 S  0.0  0.1  0:10.00 init
 2073 root   20  0 77440 1004  220 S  0.0  0.2  1:07.30 sshd 
25891 root   20  0  476m  15m 8520 S  0.0  3.2  0:00.11 httpd
25896 apache 20  0  589m  25m 5992 S  0.0  5.2  0:00.27 httpd
25901 apache 20  0  487m  23m 5388 S  0.0  4.8  0:00.15 httpd

これはtopコマンドを使用した場合。一つ一つ説明するとちょっと長くなっちゃうので、関係ありそうなとこだけど拾ってみます。

まず4行目の「Mem: 504064k total, 223636k used」ってのがメモリです。上記の例だと、サーバーのメモリは約500MBで、そのうち220MBほどが使用中という状態を表しています。

8行目から下が各プロセスです。いろいろ項目がありますが、注目したいのは「RES」と「%MEM」の二つ。RESってのは使用中のメモリサイズです。「368」とか「15m」とか表示されてますが、368はそのまま368バイトで、15mは15メガバイトです。「%MEM」はパーセンテージです。15mの場合、サーバーのメモリが500MBなので、15/500で全体の3%を使用している計算になる。上の結果でも3.2ってなってますね。

簡単に言えば、この各プロセスの使用しているメモリサイズ(RES)の合計がサーバーのメモリを超えてしまうと、不足した状態になるわけです。%MEMの方で言うと、合計が100%を超えるとアウト。だからウェブサイトへのアクセスが増えてきてメモリの使用量も上がってきた時は、そうならないように設定しておく必要があるってことですね。



MaxClientsについて

じゃあどんな設定すれば良いのかって話なのですが、ここで出てくるのが「MaxClients」というやつです。

MaxClientsはApacheの同時接続数になります。この場合の同時接続数とは、Apacheが管理する子プロセスの数って意味です。ウェブサイトへの同時アクセス数とイコールとは限りません。ほぼ同じと考えて良いかもですが、あとでちょっと自分なりの考察を書きますね。

Apacheが管理する子プロセスとは何ぞやって話ですが、先ほどのプロセス一覧のところで、「USER」が「apache」で「COMMAND」が「httpd」ってなってるのがありますよね。一覧の中の下の2行です。

  PID USER   PR NI  VIRT  RES  SHR S %CPU %MEM   TIME+  COMMAND
25896 apache 20  0  589m  25m 5992 S  0.0  5.2  0:00.27 httpd
25901 apache 20  0  487m  23m 5388 S  0.0  4.8  0:00.15 httpd

これはapacheがウェブに関するリクエストを処理しているプロセスになります。このプロセスの数を制御するのがMaxClientsです。正確には子プロセスって言い方をします。

プロセスには親プロセスと子プロセスがあります。上記の例で言うと、「USER」が「root」で「COMMAND」が「httpd」なのが親プロセスで、apacheの方は子プロセスになります。

//親プロセス
  PID USER   PR NI  VIRT  RES  SHR S %CPU %MEM   TIME+  COMMAND
25891 root   20  0  476m  15m 8520 S  0.0  3.2  0:00.11 httpd

//子プロセス
  PID USER   PR NI  VIRT  RES  SHR S %CPU %MEM   TIME+  COMMAND
25896 apache 20  0  589m  25m 5992 S  0.0  5.2  0:00.27 httpd
25901 apache 20  0  487m  23m 5388 S  0.0  4.8  0:00.15 httpd

実際にそのプロセスが親か子かを確かめるには、psコマンドを使います。

//コマンド実行
# ps auxf

//実行結果(の一部)
USER     PID %CPU %MEM    VSZ   RSS TTY STAT START  TIME COMMAND
root       1  0.0  0.0  19480   368 ?   Ss    2016  0:10 /sbin/init
root   25891  0.0  3.1 487888 15896 ?   Ss   10:16  0:00 /usr/sbin/httpd
apache 25896  0.0  5.1 603800 26204 ?   S    10:16  0:00  \_ /usr/sbin/httpd
apache 25901  0.0  4.8 498848 24408 ?   S    10:16  0:00  \_ /usr/sbin/httpd

psコマンドにfオプションをつけると親子関係が見られます。「COMMAND」のところを見ると先頭に「\_」となっているのがありますが、これが子プロセスを表しています。

ちなみに、httpdプロセスだけを見たい場合はgrepすればOKです。

# ps auxf | grep httpd

ウェブサイトへのアクセスが増えて来ると、たくさんのリクエストを同時に処理するためにapacheの子プロセスが増えて行きます。でもどこまでも無尽蔵に増え続けるわけではなく、MaxClientsで設定した数が上限になります。

逆に言うとMaxClientsまではプロセスを増やせることになるので、その結果メモリの使用量が100%を超えてしまう可能性もあるわけです。そうならないような数を設定しましょうってのが今回の本題です。



httpdプロセスの設定に関する項目

MaxClientsの設定と一言で言っちゃいましたが、実際にapacheのプロセスを制御するにはMaxClients以外にもいくつか設定項目があるので、ささっと見て行きましょう。

設定はhttpd.confで行います。

//httpd.conf
<IfModule prefork.c>
  StartServers 5
  MinSpareServers 5
  MaxSpareServers 10
  ServerLimit 256
  MaxClients 256
  MaxRequestsPerChild 10000
</IfModule>

「StartServers」はApacheを起動した時に生成されるプロセスの数です。デフォルト値は5。つまりapacheを起動した直後にtopやpsコマンドでプロセスを確認すると、apacheのhttpdプロセスが5つある状態になります。

「MinSpareServers」はアイドル状態のプロセスの最小数です。これもデフォルト値は5。ウェブサイトへのアクセスが全くなくても、常に5つのhttpdプロセスはtopコマンドで確認できる状態が維持されます。「MaxSpareServers」は反対にアイドル状態のプロセスの最大数です。デフォルト値は10。ウェブサイトへのアクセスが集中するとたくさんのプロセスが生成されますが、その後アクセスが落ち着いてきたらこの数を超えないようにプロセスがkillされる……ってことだと思う。

「ServerLimit」はMaxClientsに設定できる上限の数です。「ServerLimit >= MaxClients」となるように設定すれば数字は何でも良いみたいです。デフォルトは256。

そして「MaxClients」。プロセスは最大でこの数まで増える。これもデフォルトは256なので、初期設定のままならアクセス集中時などに256個までhttpdプロセスが生成されることになります。

「MaxRequestsPerChild」は各プロセスが処理できるリクエスト数です。たとえアクセスが集中したり落ち着いたりを繰り返さなくても、一つのhttpdプロセスはこの項目で設定した回数のリクエストを処理するとkillされます。デフォルトは10000(4000かもしれない)なので、10000回リクエストを処理したらkillされます。この数を0に設定すると無限にリクエストを処理できるそうですが、でも同じプロセスがリクエストを処理し続けるとメモリの使用量も増大し続けるので、どっかのタイミングでkillされる方が良いみたいです。

ちなみにApache2.4では「MaxClients」と「MaxRequestsPerChild」の項目名がそれぞれ「MaxRequestWorkers」と「MaxConnectionsPerChild」に変わるようです。変えなくても動作はするみたいですが、非推奨って感じなのかな。



いくつに設定すれば良いか

上記の項目をどんな数にすれば良いかは、サーバーのメモリ総量とhttpdプロセスのメモリ使用量を見て考える必要があります。

ここでもう一度、上記のhttpdプロセスを見てみましょう。

  PID USER   PR NI  VIRT  RES  SHR S %CPU %MEM   TIME+  COMMAND
25896 apache 20  0  589m  25m 5992 S  0.0  5.2  0:00.27 httpd
25901 apache 20  0  487m  23m 5388 S  0.0  4.8  0:00.15 httpd

メモリの使用量はそれぞれ25MBと23MBとなっています。実際は全部のhttpdプロセスを見るべきですが、この二つの平均を取ると、プロセスは一つ辺り24MB使用する計算になる。

ということはMaxClientsが256だと、マックスで使用されるメモリの量は24MB×256=6144MB。約6GBになります。今回はサーバーのメモリが500MBしかないですから、圧倒的に足りませんね。ざわざわしそうなほど足りない。500MBを超えないためにはもっと数を減らさないといけません。もっともサーバーはhttpd以外にもメモリを使用していますから、500MB全部を使用できる設定にしておくのは良くない。FXに貯金を全額注ぎ込んちゃうような無謀はマネはせず、多少余裕を持たせておくと良いでしょう。

ここでは8割の400MBを上限と考えてみましょう。一つ辺り24MBメモリを使用するなら、増やせる限界は400/24=16.67。つまりMaxClientsが16なら限界までプロセスが生成されてもメモリを使いきっちゃうことはなくなります。もちろんパーセンテージの方で考えても良いです。使用量の平均が5%なら、80/5=16となりますね。

算出した結果に基づいて少し設定をいじってみましょう。

//httpd.conf
<IfModule prefork.c>
  StartServers 5
  MinSpareServers 5
  MaxSpareServers 10
  ServerLimit 16
  MaxClients 16
  MaxRequestsPerChild 1000
</IfModule>

MaxRequestsPerChildも10000から1000にしました。設定を変更したらapacheを再起動しないと適用されないので注意です。

# service httpd stop
# service httpd start

プロセスがリクエストを処理し続けるとメモリの使用量が上がるってんなら、MaxRequestsPerChildはとことんまで低くしちゃえば良いような気もするのですが、たぶんだけど、あまりにもこの値が低すぎるとアクセスが集中した際にプロセスの生成が追いつかないみたいな現象が発生してしまうとか、そんなのがあるのかな……? すみません、そこはちゃんと検証できていません。

MinSpareServersやMaxSpareServersはあまり大きい値にしない方が良いと言われています。アイドリング状態でもメモリは消費するので、常時たくさんのhttpdプロセスがあるとそれだけでメモリを食ってしまう。でもその一方で、MaxClientsを適切な値にしていればその数のプロセスが常にあっても問題ないわけだから、MinSpareServersとMaxSpareServersとMaxClientsは同じ値にしておいて常に使えるプロセスをマックスに生成しておいた方がレスポンスが良いという説もある。どっちが良いかは……あなた次第?



おまけ

さっき同時接続数と同時アクセス数は必ずしもイコールじゃないって言いました。ここで言う同時アクセス数ってのは、ウェブサイトを同時に開いている人の人数だと思ってください。Googleアナリティクスで言うところの、リアルタイムのアクティブユーザー数です。

これはだいぶ憶測も混じった話になっちゃうんですが、MaxClientsを1に設定することは、アクティブユーザー数を二人以上にできなくするってことではないのです。

例えばAさんが今、とあるサイトにアクセスしたとします。ページが表示されるまでに5秒かかり、Aさんはそのページに10分滞在したとする。この時、httpdプロセスは10分間ずっと処理中なわけではなく、ページの読み込みに必要な5秒間が処理中の状態になります。だからAさんが滞在している10分間は他の誰もアクセスできないわけではなくて、ページの読み込みが終わっていればBさんもページを開ける。この場合、Googleアナリティクスを見たらアクティブユーザー数は2になっているでしょう。さらに少し遅れてCさんがアクセスしてきたら、アクティブユーザー数は3になる。

図にするとこんな感じかな。

A |--L--|-------------R-------------
B           |--L--|--------R--------
C                   |--L--|----R----

L・・・読み込み中
R・・・ページ閲覧中

MaxClientsが1でもアクセスがポツポツとしか来ないなら、こんな風に同時にページを閲覧することは可能なわけです。

MaxClientsを16にした場合もアクティブユーザー数の上限が16ってことではなくて、ページの読み込みに必要な5秒間に同時に16までのアクセスをさばけるということです。これは結構な数ですよ。仮に5秒間に16回のアクセスが続いた場合、一時間のアクセス数は11520、一ヶ月なら800万以上の計算になるので、とんでもないモンスターサイトでもない限りこんな状態にはならないでしょう。しかもこれはページの読み込みに5秒かかる場合の話ですからね。もっとウェブサイトをチューニングして1秒で読み込めるようにできれば、1秒間に16回のアクセスまで耐えられる計算になります。一時間で約58000アクセス。一ヶ月で4000万以上。高橋名人もビックリだぜ。

もちろんアクセスには波がありますから、月に数万PVでも瞬間的に16以上のアクセスがある可能性はゼロではないですけど、でも同時接続数が16って決して少なくないのよってお話。

こんな認識で合ってるのかしら?






正確にどれくらいの値にしておけば大丈夫なのかってのは、状況によって変わります。月間のPVはたいしたことないけど毎日決まった時間にだけ相当数のアクセスがあるのなら、それに耐えられるようにしておかなきゃいけないし、場合によってはMaxClientsの設定だけではダメで、サーバーの台数を増やさなきゃいけない場合もあるでしょう。その辺はケースバイケースってやつですね。

設定自体は難しくないと思うのですが、「いやいやこんな設定めんどくてやってられんわー」って場合は……どうしましょう。絶対に不足しないくらいハイスペックなメモリを搭載するとかですかね。1TBくらいメモリがあれば、まず足りなくなることはないでしょう。でもコストも半端ないと思う。AWSのEC2の場合、976GBのメモリを積んだサーバーがありますが、料金は約$10/hだから一ヶ月稼働すると80万円くらいになる。ロードバランサーで二台構成にしたら160万円。よほど稼ぎがないとプロミスに頼っても賄えないですね。

やはりまずは無理のない計画を立ててサーバーの設定を行うのが良いと思います。
 もしかしたら何か関連しているかも? 
 質問や感想などお気軽にコメントしてください