embomaコラム

【LAMPサイトのチューニング入門(1) -WWWサーバ編】

2008/05/26 (月)

株式会社えむぼま CTO 高見禎成

■swapさせずに最大プロセス数にせよ!
 WWWサーバが最大パフォーマンスを出すには
1) SWAPさせないで
2) 最大プロセス数に設定する
 ことが最大の条件です。
 他にapacheのhttpd.confでHostNameLookups offにしてログ記録時の名前解決をなくす等細かい積み重ねはいくつかありますが、最大のポイントは上記2条件でしょう。


■最大プロセス数は揃えよ!
 SWAPさせない最大プロセス数をいくつにするかはこの後見ていきますが、どの値にするにせよapacheとtomcatとDBの最大プロセス数は揃えましょう。
 apacheのMaxClietnsは標準で255、tomcatのMaxThreadsは標準で150、MySQLのmax_connectionsは標準で100です。
 apacheが2台でMySQL 1台に接続しているなら、MySQLのmax_connectionsはapacheのMaxClients x2以上に揃えましょう。
 apacheとtomcatを1台に同居させてapache→tomcat接続させているなら、tomcatのMaxThreadsはapacheのMaxClientsと揃えましょう。

 

■事前準備 - sarの勧め
 本来監視しておくべきですが、サーバが遅くなる日は突然やってきます。
 対応するのはピークをすぎてからのことも多いでしょう。
 ピーク時の稼働状況を後から調べられるようにするためにsar(sysstat)をインストールしておくことをお勧めします。
 SWAPの有無、システムの稼働プロセス数、DISK I/O、ネットワークトラフィック等sarがあればほぼチューニングの基礎データはそろいます。

 

※付録1 -sar
○sarのオプション
-q     loadとともにシステムの最大プロセス数がわかる。apacheやmysqlが最大プロセス数に達したか調べられる。
-W     swap状況がわかる -rのメモリ状況と合わせて使う。
-b     I/O数と転送量からDISK I/Oを調べられる
-n DEV NICの転送パケット数と転送料からトラフィックが調べられる

 

○sarの実行間隔
 debianの標準では10分単位の集計になっている。変更の際は下記を変更する

/etc/cron.d/sysstat
---
# Activity reports every 10 minutes everyday
5-55/10 * * * * root [ -x /usr/lib/sysstat/sa1 ] && { [ -r "$DEFAULT" ] && . "$DEFAULT" ; [ "$ENABLED" = "true" ] && exec /usr/lib/sysstat/sa1; }

 

○過去のsar情報
 debianでは/var/log/sysstat(RHは/var/log/sa)に過去分がある。
saDDでDDが日付の部分

 

cd /var/log/sysstat

 

sa01  sa03  sa05  sa28  sa30  sar01  sar03  sar2
/var/log/sysstat# ls7  sar29  sar31
sa02  sa04  sa27  sa29  sa31  sar02  sar04  sar28  sar30

 

# sar -f sa01 -u
Linux 2.4.27-2-686-smp     02/01/07

 

00:05:01          CPU     %user     %nice   %system   %iowait     %idle
00:15:01          all      1.53      0.00      3.98      0.00     94.48
00:25:01          all      1.57      0.00      4.07      0.00     94.36

 

○sarの世代数
/usr/lib/sysstat/sa2
---

find /var/log/sysstat \( -name 'sar??' -o -name 'sa??' \) -mtime +7 -exec rm -f {} \;
                                                                 ^^ここで7日分で削除している


■システムの負荷状況に見方
○Linuxの稼働メモリ
 Linuxは物理メモリを最大限キャッシュに利用します。
 そのためfree,top等のメモリ使用量は常にほぼ100%です。
 実際に利用されているメモリの調べ方です。

 

$ free -m
             total       used       free     shared    buffers     cached
Mem:           503        490         12          0        100        135
-/+ buffers/cache:        254        248
Swap:          509        192        317

 

 used - buffers - cached = 実使用量
 上記の例だと 490 - 100 - 135 = 255MBが実使用量。空きは最大248MBになります。
 apacheやtomcat、msyql、postgresを止めて上記を調べるとカーネルを含む最低起動メモリ量がわかります。

 

○Linuxの過去のメモリ状況の調べ方
 sar -rを使用する

 

$ sar -r|more
Linux 2.4.27-2-686-smp     02/05/07

 

00:05:01    kbmemfree kbmemused  %memused kbbuffers  kbcached kbswpfree kbswpused  %swpused  kbswpcad
00:15:01        30264    485108     94.13     33980    263084    461368     60864     11.65     10308
00:25:01        27816    487556     94.60     34616    265260    461368     60864     11.65     10308
00:35:01        28544    486828     94.46     35004    265092    461368     60864     11.65     10308
00:45:01        28484    486888     94.47     35340    265064    461368     60864     11.65     10308
00:55:01        28344    487028     94.50     35644    264520    461368     60864     11.65     10308

 freeの読み方と同じです。swpusedが100%になるとシステムが停止します。

 

○Linuxの過去のメモリ不足(swap)の調べ方
 sar -Wを使用する

 

$ sar -W
Linux 2.4.27-2-686-smp     02/05/07

 

00:05:01     pswpin/s pswpout/s
00:15:01         0.19      0.00

 

 pswpin,pswpoutの値が高くなっているとswapが発生しています。
 swapが発生するとloadも高くなりシステムは極端な遅延になります。
 swapを消費しつくすとシステム停止になるので要注意です。


○Linuxの過去のCPU稼働率の調べ方
 sar -uを使用する

 

$ sar -u|more
Linux 2.4.27-2-686-smp     02/05/07

 

00:05:01          CPU     %user     %nice   %system   %iowait     %idle
00:15:01          all      1.50      0.00      4.01      0.00     94.49
00:25:01          all      1.55      0.00      4.07      0.00     94.38
00:35:01          all      1.50      0.00      3.99      0.00     94.51
00:45:01          all      1.52      0.00      4.01      0.00     94.47
00:55:01          all      1.50      0.00      3.69      0.00     94.81
01:05:02          all      2.15      0.00      5.90      0.00     91.95
01:15:01          all      1.55      0.00      3.91      0.00     94.54

 

 CPU稼働率が高く100%になっても「システムは極端な遅延にならない!」のでCPU稼働100%は問題の優先順位が低いです。
 CPU稼働が100%になった原因=SWAPによるDISK I/Oを特定することが重要です。

 

 PHPの場合最大稼働CPU時間が設定されているので無限ループは発生しにくいですが、PerlのCGI等で無限ループが発生するとsar -qでプロセス数もloadも増えていないのにCPU稼働率が上がったりします。
 筆者はsar -uはほぼ無限ループプロセスがいないか探すためだけに使っています。
 繰り返しますがCPUの稼働率が高いことは「システム遅延の直接の原因ではない!!」と考えています。


○Linuxの過去の稼働プロセス数の調べ方
 apacheが最大プロセス数に達したことは/var/log/apache/error_logに

 

[error] server reached MaxClients setting, consider raising the MaxClient s setting

 

 と表示されます。grep -i max error_log 等で時間を調べることができますが、最大に達しないと出力されません。

 

 Linuxで稼働するプロセス数を調べるにはsar -qを使用します。

 

$ sar -q
Linux 2.4.27-2-686-smp     02/05/07

 

00:05:01      runq-sz  plist-sz   ldavg-1   ldavg-5  ldavg-15
00:15:01            2        51      0.01      0.10      0.14
00:25:01            2        49      0.01      0.10      0.13
00:35:01            2        48      0.00      0.05      0.08
00:45:01            3        50      0.01      0.07      0.08
00:55:01            2        49      0.00      0.04      0.06
01:05:02            1        48      0.05      0.38      0.26
 plist -szがシステムの稼働プロセス数です。この値が急に増えている場合、apacheやMySQLのプロセス数が増えている場合が多いです。 ちなみに ldavg-1は1分毎のloadです。loadとは待ちプロセス数の数でCPU稼働率よりシステムの負荷を表す基準に使います。loadがあがる典型例として以下の2つがあります。

 

1) DISK I/Oが増えて待ち行列が増えた
2) apacheのMaxClients等アプリの最大プロセス制限で待ちプロセス数が増えた

 

 関係情報として、loadが5以上になるとsendmailはSMTPをrejectすることを覚えておくとよいでしょう。。


○Linuxの過去のDISK I/O調査
 sar -bを使う

 

# sar -b
Linux 2.4.27-2-686-smp     02/05/07

 

00:05:01          tps      rtps      wtps   bread/s   bwrtn/s
00:15:01        15.75      0.65     15.10     10.50     58.24
00:25:01        15.21      0.23     14.98      2.72     57.80
00:35:01        19.74      0.03     19.71      0.17     67.56
00:45:01        15.32      0.19     15.13      1.65     58.76
00:55:01        14.95      0.03     14.92      0.29     58.11

 

 SWAPも発生しておらず、プロセスも増えておらず、CPU稼働率も低いのに遅い場合
例えばDBで全文検索が頻繁に発生したり、distictで一時テーブルが頻繁に作成されている
場合にdisk I/Oが増える。
 他のマシンとも比較して高い値になっていないかを調べる。

 

■apacheの最大プロセス数を割り出す方法
○プロセスモデルとスレッドモデル
 まずプロセスについて解説します。

 

 アプリケーションにはプロセスモデルとスレッドモデルがあります。
 UNIXの伝統的なモデルはプロセスモデルで、apache1.xが同時複数アクセスを処理するためにapacheは子プロセスを生成しています。プロセスモデルはメモリ空間をコピーするため、プロセス起動は負荷が高いです。またapacheはプロセス毎にメモリ使用量が異なります。
 プロセスの重さを解決するためにスレッドモデルが作られました。MySQLはスレッドモデルです。スレッドモデルはメモリ空間を共有するため起動の負荷が少ないのです。
 apache2.0 よりプロセスモデル(prefork)とスレッドモデル(worker)を選べますが、スレッドモデルとして稼働するにはCGIではスレッドの意味がなく、PHP等スレッドモデルにする必要があります。PHPはスレッド未対応のため、apacheは原則preforkで稼働させています。

 

○プロセスのメモリ使用量
 ps auxwとtopで確認します

 

$ ps auxw
USER       PID %CPU %MEM   VSZ  RSS TTY      STAT START   TIME COMMAND
mysql      901  0.0  4.1 70716 21444 ?       S    Jan26   1:25 /usr/sbin/mysqld --basedir=/usr --datadir=/var/lib/mysql --user=mysql
mysql      902  0.0  4.1 70716 21444 ?       S    Jan26   0:13 /usr/sbin/mysqld --basedir=/usr --datadir=/var/lib/mysql --user=mysql
www-data 32233  0.0  1.8 29200 9712 ?        S    06:25   0:05 /usr/sbin/apache
www-data 32234  0.0  2.6 29200 13620 ?       S    06:25   0:05 /usr/sbin/apache
www-data 32235  0.0  2.5 29208 13280 ?       S    06:25   0:05 /usr/sbin/apache
www-data 32236  0.0  2.5 29224 13072 ?       S    06:25   0:05 /usr/sbin/apache

 

 VSZがメモリサイズ(KB)、RSSはReSident Sizeで実メモリサイズ(KB)
 mysqlはスレッドモデルのため、全てのMYSQLで約21MB使用しています。
 apacheはプロセスモデルのためRSSは個別に違います

 

 ただしプロセス単位のメモリはtopを利用する方がわかりやすいように思います。

 

top - 17:09:09 up 9 days, 23:49,  2 users,  load average: 0.08, 0.18, 0.16
Tasks:  49 total,   1 running,  48 sleeping,   0 stopped,   0 zombie
Cpu(s):   0.2% user,   5.4% system,   0.0% nice,  94.4% idle
Mem:    515372k total,   485780k used,    29592k free,    36088k buffers
Swap:   522232k total,    63892k used,   458340k free,   230688k cached

 

 PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
  901 mysql      9   0 53224  20m  18m S  0.0  4.2   1:25.56 mysqld
  902 mysql      9   0 53224  20m  18m S  0.0  4.2   0:13.38 mysqld
  903 mysql      9   0 53224  20m  18m S  0.0  4.2   0:00.00 mysqld
32233 www-data   9   0 12176 9712 5412 S  0.0  1.9   0:05.67 apache
32234 www-data   9   0 15672  13m 9332 S  0.0  2.6   0:06.08 apache
32235 www-data   9   0 15372  12m 8972 S  0.0  2.6   0:05.39 apache
32236 www-data   9   0 15368  12m 8764 S  0.0  2.5   0:06.06 apache

 

 VIRTがメモリサイズ(KB)、RESがResidentで実メモリ使用量、SHRは共有メモリ量
 スレッドモデルのMySQLの場合はRESをみれば実使用メモリがわかります。
 この場合20MB

 

 プロセスモデルのapacheの場合プロセス毎に上記では13MB、12MB,12MB使用しています
 ただしSHR分は共有しているので、8〜9MBは共有です。
 よって上記の場合、1プロセス当たりに必要なメモリは約5〜3MBとなります

 

■チューニング方法
 apacheの最大プロセスに達した時何MB必要か、それは現在の空きメモリに足りるかを
 算出して最大プロセス数の設定を行います。

 

 freeコマンドで調べた used - buffer - cachedで使用量を求め空き容量がわかります。
 この稿の例では248MBです。
 MySQLのメモリ使用量ははプロセス数にあまり比例しないのでここでは考えません。
 このapacheは5〜3MBなので、1プロセスあたり4MBとすると、248 / 4 = 62
 この時点の起動apacheのプロセス数(ps auxw|grep apache|wc -lで求める)を足すと
 最大プロセス数になります。

 

 最大プロセス数が現在のMaxClientsより大きいなら増やしましょう。
 最大プロセス数が現在のMaxClientsより小さいならswapするよりは小さくした方がよいですが、
 いずれにしても性能が下がってしまいます。

 

a) 物理メモリを増やす
b) apacheのメモリ使用量を減らす
 PHPで配列変数で大きなメモリを使っていたりしたら減らしましょう。
 httpd.confのLoadしている不要なモジュールを減らすことである程度は減らせます。
 一義的には物理メモリを増やすことをお勧めします。


※付録2 -ログの集計
 WWWサーバのチューニングには1時間や1分単位のヒット数の集計ができると便利です。
 awstatやwebalizerは1分単位の集計はできませんが、awkとsortコマンドを組み合わせるとログの集計が可能です。
 grepを組み合わせることで障害時、チューニング時の原因究明が行いやすくなります。
 下記はapacheのaccess_logの時間毎の集計例です。

 

※access_log
$ cat access.log|awk '{print $4}'|awk -F: '{print $1,$2}'|sort|uniq -c|sed "s/\[//"
      2 05/Feb/2007 10
      3 05/Feb/2007 11
    369 05/Feb/2007 12
     68 05/Feb/2007 15
    936 05/Feb/2007 16
    921 05/Feb/2007 17
    225 05/Feb/2007 18


分単位の集計にしたい場合
$ cat access.log|awk '{print $4}'|awk -F: '{print $1,$2,$3}'|sort|uniq -c|sed "s/\[//"
     56 05/Feb/2007 15 55
      9 05/Feb/2007 15 56
     23 05/Feb/2007 16 01
     23 05/Feb/2007 16 06
     23 05/Feb/2007 16 11