2011年7月28日木曜日

nginx+PHP-FPMでどこまでチューニングできるか

このサイトもnginx+PHP-FPM+MySQLで動作しているのですがphpをfastcgiで動かしている例は多くあったのですがPHP-FPMで書いている記事があまりない。

yumなどは使わずにソースから全てインストールします。MySQLは事前にインストールされている物とします。


nginx

nginxユーザを追加します

adduser -u 500 nginx


使わないオプションは全てOFFにします。


./configure \
--with-http_stub_status_module \
--without-http_upstream_ip_hash_module \
--without-http_gzip_module \
--without-http_autoindex_module \
--without-http_geo_module \
--without-http_map_module \
--without-http_split_clients_module \
--without-http_referer_module \
--without-http_proxy_module \
--without-http_uwsgi_module \
--without-http_limit_zone_module \
--without-http_limit_req_module \
--without-http_browser_module \



特に--with-http_stub_status_module のオプションはサーバのステータスを見てチューニングするのには必須なので必ずいれます。


nginx設定ファイル


user nginx;
worker_processes 2;

error_log /var/log/nginx/blog/error.log;
pid /var/run/nginx.pid;

events {
worker_connections 1024;
}


http {
include /usr/local/nginx/conf/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';

access_log /var/log/nginx/blog/access.log main;

sendfile on;
keepalive_timeout 0;
server_tokens off;

server {
listen 80;
server_name blog.kubox.info;
root /home/blog/public_html;
index index.php;
error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}


location ~*\.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME /home/blog/public_html$fastcgi_script_name;
}

location = /status {
include /usr/local/nginx/conf/fastcgi_params;
fastcgi_param SCRIPT_FILENAME /status;
fastcgi_pass 127.0.0.1:9000;
access_log off;
}

location /nginx_status {
stub_status on;
access_log off;
allow all;
deny all;
}

location ~ /\.ht {
deny all;
}

}
}



コンピュータはCPUの数を超えて並列処理はできないのでworker_processesはCPUの数に合わせます。2Coreなので2します。

worker_connectionsはworker_processesが同時に処理できる接続数です。


以下の行を追加するとhttp://URL/statusでPHP-FPMのステータス、http://URL/nginx_statusでnginxのステータスを見ることができます。
deny allになっているので許可するIPをAllowしてください。


location = /status {
include /usr/local/nginx/conf/fastcgi_params;
fastcgi_param SCRIPT_FILENAME /status;
fastcgi_pass 127.0.0.1:9000;
access_log off;
}

location /nginx_status {
stub_status on;
access_log off;
allow all;
deny all;
}



PHP-FPM
PHP-FPMはPHP5.3.3から組み込まれており、オプションを追加するだけでPHP-FPMが利用できます。

fastcgiは別途lighttpdをmakeして抜き出したりする手間がありましたがその必要がなくなりより高負荷サイト用に機能が追加されました。

  • 緩やかな (graceful) 停止/起動 機能を含む高度なプロセス管理



  • 異なる uid/gid/chroot/environment でのワーカーの開始、 異なるポートでのリスン、異なる php.ini の使用 (safe_mode の代替)



  • 標準出力および標準エラー出力へのログ出力



  • opcode キャッシュが壊れた場合の緊急再起動



  • 高速なアップロードのサポート



  • "slowlog" - 非常に低速に動作するログ出力スクリプト (名前から想像される内容だけでなく、ptrace やそれと同等の仕組みを使ってリモートプロセスの execute_data からの PHP バックトレースも出力します)



  • fastcgi_finish_request() - リクエストを終わらせてすべてのデータを出力した後で 何か時間のかかる処理 (動画の変換や統計情報の処理など) をさせるための特殊な関数



  • 動的/静的 な子プロセスの起動



  • 基本的な SAPI の動作状況 (Apache の mod_status と同等)



  • php.ini ベースの設定ファイル



http://www.php.net/manual/ja/install.fpm.php

configureでenable-fpmオプションを追加してください。


'./configure' \
'--with-mysql=/usr/local/mysql' \
'--enable-zend-multibyte' \
'--with-pdo-mysql' \
'--with-zlib' \
'--with-xmlrpc' \
'--with-gd' \
'--with-curl' \
'--with-jpeg-dir=/usr/local' \
'--with-png-dir=/usr/local' \
'--enable-mbstring' \
'--enable-fpm' \
'--with-fpm-user=nginx' \
'--with-fpm-group=nginx' \
'--without-SQLite' \


PHP-FPM設定ファイル


[global]
pid = /var/run/php-fpm.pid
error_log = /var/log/php/php-fpm.log
log_level = notice
emergency_restart_threshold = 0
emergency_restart_interval = 0
process_control_timeout = 0
daemonize = yes

[www]
listen = 127.0.0.1:9000
listen.backlog = -1
listen.allowed_clients = 127.0.0.1

listen.owner = nginx
listen.group = nginx
listen.mode = 0666

user = nginx
group = nginx

pm = dynamic
pm.max_children = 10
pm.start_servers = 5
pm.min_spare_servers = 3
pm.max_spare_servers = 8
pm.max_requests = 500
pm.status_path = /status

request_slowlog_timeout = 2
request_terminate_timeout= 5
slowlog = /var/log/php/$pool.log.slow



注目するところはpmから始まる設定です。

PHP-FPMはWebサーバ(nginx)がユーザから受け取ったリクエストをPHPでればPHP-FPMに処理を投げ処理をするので、nginxとPHP-FPMが同程度処理できるのが理想です。

ひとまず負荷試験を行うまでどこにボトルネックがあるかわからないので適用な値にしておきます。


メモリ
nginxとPHP-FPMが無事に起動できたら一旦メモリの状態を見てみます。


# free -m
total used free shared buffers cached
Mem: 497 385 111 0 75 154
-/+ buffers/cache: 156 341
Swap: 1027 0 1027



予想以上にメモリを使っていたのでちょっとpsしてみます。


# ps axu | grep php
root 32717 0.0 0.7 124672 3708 ? Ss 13:45 0:00 php-fpm: master process (/usr/local/etc/php-fpm.conf)
nginx 32718 0.2 9.0 139960 46368 ? S 13:45 0:00 php-fpm: pool www
nginx 32719 0.0 5.7 132708 29424 ? S 13:45 0:00 php-fpm: pool www
nginx 32720 0.1 4.6 129440 23700 ? S 13:45 0:00 php-fpm: pool www
nginx 32721 0.1 5.0 129928 25940 ? S 13:45 0:00 php-fpm: pool www
nginx 32722 0.0 5.2 130212 26900 ? S 13:45 0:00 php-fpm: pool www



PHP-FPMが結構メモリを使っているみたいです。


pmapコマンドでプロセスのメモリ利用状況をさらに詳しくみてみます。


# pmap 32717
32717: php-fpm: master process (/usr/local/etc/php-fpm.conf)
0000000000400000 8396K r-x-- /usr/local/sbin/php-fpm
0000000000e33000 464K rw--- /usr/local/sbin/php-fpm
0000000000ea7000 120K rw--- [ anon ]
0000000010021000 2288K rw--- [ anon ]
0000000040456000 4K ----- [ anon ]
0000000040457000 10240K rw--- [ anon ]
00000030f0c00000 112K r-x-- /lib64/ld-2.5.so
00000030f0e1c000 4K r---- /lib64/ld-2.5.so
00000030f0e1d000 4K rw--- /lib64/ld-2.5.so
00000030f1000000 1336K r-x-- /lib64/libc-2.5.so
00000030f114e000 2048K ----- /lib64/libc-2.5.so
00000030f134e000 16K r---- /lib64/libc-2.5.so
00000030f1352000 4K rw--- /lib64/libc-2.5.so
00000030f1353000 20K rw--- [ anon ]
00000030f1400000 8K r-x-- /lib64/libdl-2.5.so
00000030f1402000 2048K ----- /lib64/libdl-2.5.so
00000030f1602000 4K r---- /lib64/libdl-2.5.so
00000030f1603000 4K rw--- /lib64/libdl-2.5.so
00000030f1800000 520K r-x-- /lib64/libm-2.5.so
00000030f1882000 2044K ----- /lib64/libm-2.5.so
00000030f1a81000 4K r---- /lib64/libm-2.5.so
00000030f1a82000 4K rw--- /lib64/libm-2.5.so
00000030f1c00000 88K r-x-- /lib64/libpthread-2.5.so
00000030f1c16000 2044K ----- /lib64/libpthread-2.5.so
00000030f1e15000 4K r---- /lib64/libpthread-2.5.so
00000030f1e16000 4K rw--- /lib64/libpthread-2.5.so
00000030f1e17000 16K rw--- [ anon ]
00000030f2000000 80K r-x-- /usr/lib64/libz.so.1.2.3
00000030f2014000 2044K ----- /usr/lib64/libz.so.1.2.3
00000030f2213000 4K rw--- /usr/lib64/libz.so.1.2.3
00000030f2400000 236K r-x-- /lib64/libsepol.so.1
00000030f243b000 2048K ----- /lib64/libsepol.so.1
00000030f263b000 4K rw--- /lib64/libsepol.so.1
00000030f263c000 40K rw--- [ anon ]
00000030f2800000 84K r-x-- /lib64/libselinux.so.1
00000030f2815000 2048K ----- /lib64/libselinux.so.1
00000030f2a15000 8K rw--- /lib64/libselinux.so.1
00000030f2a17000 4K rw--- [ anon ]
00000030f2c00000 28K r-x-- /lib64/librt-2.5.so
00000030f2c07000 2048K ----- /lib64/librt-2.5.so
00000030f2e07000 4K r---- /lib64/librt-2.5.so
00000030f2e08000 4K rw--- /lib64/librt-2.5.so
00000030f3000000 52K r-x-- /lib64/libgcc_s-4.1.2-20080825.so.1
00000030f300d000 2048K ----- /lib64/libgcc_s-4.1.2-20080825.so.1
00000030f320d000 4K rw--- /lib64/libgcc_s-4.1.2-20080825.so.1
00000030f3800000 36K r-x-- /lib64/libcrypt-2.5.so
00000030f3809000 2044K ----- /lib64/libcrypt-2.5.so
00000030f3a08000 4K r---- /lib64/libcrypt-2.5.so
00000030f3a09000 4K rw--- /lib64/libcrypt-2.5.so
00000030f3a0a000 184K rw--- [ anon ]
00000030f4c00000 1204K r-x-- /lib64/libcrypto.so.0.9.8e
00000030f4d2d000 2044K ----- /lib64/libcrypto.so.0.9.8e
00000030f4f2c000 132K rw--- /lib64/libcrypto.so.0.9.8e
00000030f4f4d000 16K rw--- [ anon ]
00000030f5000000 84K r-x-- /lib64/libnsl-2.5.so
00000030f5015000 2044K ----- /lib64/libnsl-2.5.so
00000030f5214000 4K r---- /lib64/libnsl-2.5.so
00000030f5215000 4K rw--- /lib64/libnsl-2.5.so
00000030f5216000 8K rw--- [ anon ]
00000030f6000000 68K r-x-- /lib64/libresolv-2.5.so
00000030f6011000 2048K ----- /lib64/libresolv-2.5.so
00000030f6211000 4K r---- /lib64/libresolv-2.5.so
00000030f6212000 4K rw--- /lib64/libresolv-2.5.so
00000030f6213000 8K rw--- [ anon ]
00000030f6800000 8K r-x-- /lib64/libcom_err.so.2.1
00000030f6802000 2044K ----- /lib64/libcom_err.so.2.1
00000030f6a01000 4K rw--- /lib64/libcom_err.so.2.1
00000030f6c00000 8K r-x-- /lib64/libkeyutils-1.2.so
00000030f6c02000 2044K ----- /lib64/libkeyutils-1.2.so
00000030f6e01000 4K rw--- /lib64/libkeyutils-1.2.so
00000030f7c00000 1228K r-x-- /usr/lib64/libxml2.so.2.6.26
00000030f7d33000 2048K ----- /usr/lib64/libxml2.so.2.6.26
00000030f7f33000 36K rw--- /usr/lib64/libxml2.so.2.6.26
00000030f7f3c000 4K rw--- [ anon ]
00000030f8400000 144K r-x-- /usr/lib64/libk5crypto.so.3.1
00000030f8424000 2044K ----- /usr/lib64/libk5crypto.so.3.1
00000030f8623000 8K rw--- /usr/lib64/libk5crypto.so.3.1
00000030f8800000 32K r-x-- /usr/lib64/libkrb5support.so.0.1
00000030f8808000 2044K ----- /usr/lib64/libkrb5support.so.0.1
00000030f8a07000 4K rw--- /usr/lib64/libkrb5support.so.0.1
00000030f8c00000 584K r-x-- /usr/lib64/libkrb5.so.3.3
00000030f8c92000 2044K ----- /usr/lib64/libkrb5.so.3.3
00000030f8e91000 16K rw--- /usr/lib64/libkrb5.so.3.3
00000030f9400000 176K r-x-- /usr/lib64/libgssapi_krb5.so.2.2
00000030f942c000 2048K ----- /usr/lib64/libgssapi_krb5.so.2.2
00000030f962c000 8K rw--- /usr/lib64/libgssapi_krb5.so.2.2
00000030fa400000 280K r-x-- /lib64/libssl.so.0.9.8e
00000030fa446000 2048K ----- /lib64/libssl.so.0.9.8e
00000030fa646000 24K rw--- /lib64/libssl.so.0.9.8e
0000003100a00000 140K r-x-- /usr/lib64/libpng12.so.0.10.0
0000003100a23000 2048K ----- /usr/lib64/libpng12.so.0.10.0
0000003100c23000 4K rw--- /usr/lib64/libpng12.so.0.10.0
00002aaaaaac4000 40K r-x-- /lib64/libnss_files-2.5.so
00002aaaaaace000 2044K ----- /lib64/libnss_files-2.5.so
00002aaaaaccd000 4K r---- /lib64/libnss_files-2.5.so
00002aaaaacce000 4K rw--- /lib64/libnss_files-2.5.so
00002aaaaaccf000 32768K rw-s- /dev/zero (deleted)
00002aaaacccf000 4K rw-s- /dev/zero (deleted)
00002b9f39d31000 8K rw--- [ anon ]
00002b9f39d33000 4K rw-s- /dev/zero (deleted)
00002b9f39d3f000 4K rw--- [ anon ]
00002b9f39d40000 2904K r-x-- /usr/local/mysql/lib/libmysqlclient.so.18.0.0
00002b9f3a016000 1020K ----- /usr/local/mysql/lib/libmysqlclient.so.18.0.0
00002b9f3a115000 524K rw--- /usr/local/mysql/lib/libmysqlclient.so.18.0.0
00002b9f3a198000 24K rw--- [ anon ]
00002b9f3a19e000 132K r-x-- /usr/lib64/libjpeg.so.62.0.0
00002b9f3a1bf000 2044K ----- /usr/lib64/libjpeg.so.62.0.0
00002b9f3a3be000 4K rw--- /usr/lib64/libjpeg.so.62.0.0
00002b9f3a3bf000 4K rw--- [ anon ]
00002b9f3a3c0000 324K r-x-- /usr/local/lib/libcurl.so.4.2.0
00002b9f3a411000 2044K ----- /usr/local/lib/libcurl.so.4.2.0
00002b9f3a610000 12K rw--- /usr/local/lib/libcurl.so.4.2.0
00002b9f3a613000 4K rw--- [ anon ]
00002b9f3a614000 196K r-x-- /usr/lib64/libidn.so.11.5.19
00002b9f3a645000 2044K ----- /usr/lib64/libidn.so.11.5.19
00002b9f3a844000 4K rw--- /usr/lib64/libidn.so.11.5.19
00002b9f3a845000 28K rw--- [ anon ]
00002b9f3a88d000 316K rw--- [ anon ]
00002b9f3a8dc000 128K r-x-- /usr/local/lib/php/extensions/no-debug-non-zts-20090626/apc.so
00002b9f3a8fc000 2048K ----- /usr/local/lib/php/extensions/no-debug-non-zts-20090626/apc.so
00002b9f3aafc000 12K rw--- /usr/local/lib/php/extensions/no-debug-non-zts-20090626/apc.so
00002b9f3aaff000 36K rw--- [ anon ]
00002b9f3ab08000 76K r-x-- /usr/local/lib/php/extensions/no-debug-non-zts-20090626/oauth.so
00002b9f3ab1b000 2048K ----- /usr/local/lib/php/extensions/no-debug-non-zts-20090626/oauth.so
00002b9f3ad1b000 8K rw--- /usr/local/lib/php/extensions/no-debug-non-zts-20090626/oauth.so
00007fff4a564000 84K rw--- [ stack ]
00007fff4a5fc000 16K r-x-- [ anon ]
ffffffffff600000 8192K ----- [ anon ]
total 132860K



apc.soはAPCを使う上で必要なのでしょうがないですがoauth.soはそんなに頻繁にOAuthしないので一旦php.iniでコメントアウトしておきます。

PHP-FPMを再起動後、再度pmapしてみます。

pmap 11238
11238: php-fpm: master process (/usr/local/etc/php-fpm.conf)
0000000000400000 8396K r-x-- /usr/local/sbin/php-fpm
0000000000e33000 464K rw--- /usr/local/sbin/php-fpm
0000000000ea7000 120K rw--- [ anon ]
000000001a798000 2252K rw--- [ anon ]
00000000406da000 4K ----- [ anon ]
00000000406db000 10240K rw--- [ anon ]
00000030f0c00000 112K r-x-- /lib64/ld-2.5.so
00000030f0e1c000 4K r---- /lib64/ld-2.5.so
00000030f0e1d000 4K rw--- /lib64/ld-2.5.so
00000030f1000000 1336K r-x-- /lib64/libc-2.5.so
00000030f114e000 2048K ----- /lib64/libc-2.5.so
00000030f134e000 16K r---- /lib64/libc-2.5.so
00000030f1352000 4K rw--- /lib64/libc-2.5.so
00000030f1353000 20K rw--- [ anon ]
00000030f1400000 8K r-x-- /lib64/libdl-2.5.so
00000030f1402000 2048K ----- /lib64/libdl-2.5.so
00000030f1602000 4K r---- /lib64/libdl-2.5.so
00000030f1603000 4K rw--- /lib64/libdl-2.5.so
00000030f1800000 520K r-x-- /lib64/libm-2.5.so
00000030f1882000 2044K ----- /lib64/libm-2.5.so
00000030f1a81000 4K r---- /lib64/libm-2.5.so
00000030f1a82000 4K rw--- /lib64/libm-2.5.so
00000030f1c00000 88K r-x-- /lib64/libpthread-2.5.so
00000030f1c16000 2044K ----- /lib64/libpthread-2.5.so
00000030f1e15000 4K r---- /lib64/libpthread-2.5.so
00000030f1e16000 4K rw--- /lib64/libpthread-2.5.so
00000030f1e17000 16K rw--- [ anon ]
00000030f2000000 80K r-x-- /usr/lib64/libz.so.1.2.3
00000030f2014000 2044K ----- /usr/lib64/libz.so.1.2.3
00000030f2213000 4K rw--- /usr/lib64/libz.so.1.2.3
00000030f2400000 236K r-x-- /lib64/libsepol.so.1
00000030f243b000 2048K ----- /lib64/libsepol.so.1
00000030f263b000 4K rw--- /lib64/libsepol.so.1
00000030f263c000 40K rw--- [ anon ]
00000030f2800000 84K r-x-- /lib64/libselinux.so.1
00000030f2815000 2048K ----- /lib64/libselinux.so.1
00000030f2a15000 8K rw--- /lib64/libselinux.so.1
00000030f2a17000 4K rw--- [ anon ]
00000030f2c00000 28K r-x-- /lib64/librt-2.5.so
00000030f2c07000 2048K ----- /lib64/librt-2.5.so
00000030f2e07000 4K r---- /lib64/librt-2.5.so
00000030f2e08000 4K rw--- /lib64/librt-2.5.so
00000030f3000000 52K r-x-- /lib64/libgcc_s-4.1.2-20080825.so.1
00000030f300d000 2048K ----- /lib64/libgcc_s-4.1.2-20080825.so.1
00000030f320d000 4K rw--- /lib64/libgcc_s-4.1.2-20080825.so.1
00000030f3800000 36K r-x-- /lib64/libcrypt-2.5.so
00000030f3809000 2044K ----- /lib64/libcrypt-2.5.so
00000030f3a08000 4K r---- /lib64/libcrypt-2.5.so
00000030f3a09000 4K rw--- /lib64/libcrypt-2.5.so
00000030f3a0a000 184K rw--- [ anon ]
00000030f4c00000 1204K r-x-- /lib64/libcrypto.so.0.9.8e
00000030f4d2d000 2044K ----- /lib64/libcrypto.so.0.9.8e
00000030f4f2c000 132K rw--- /lib64/libcrypto.so.0.9.8e
00000030f4f4d000 16K rw--- [ anon ]
00000030f5000000 84K r-x-- /lib64/libnsl-2.5.so
00000030f5015000 2044K ----- /lib64/libnsl-2.5.so
00000030f5214000 4K r---- /lib64/libnsl-2.5.so
00000030f5215000 4K rw--- /lib64/libnsl-2.5.so
00000030f5216000 8K rw--- [ anon ]
00000030f6000000 68K r-x-- /lib64/libresolv-2.5.so
00000030f6011000 2048K ----- /lib64/libresolv-2.5.so
00000030f6211000 4K r---- /lib64/libresolv-2.5.so
00000030f6212000 4K rw--- /lib64/libresolv-2.5.so
00000030f6213000 8K rw--- [ anon ]
00000030f6800000 8K r-x-- /lib64/libcom_err.so.2.1
00000030f6802000 2044K ----- /lib64/libcom_err.so.2.1
00000030f6a01000 4K rw--- /lib64/libcom_err.so.2.1
00000030f6c00000 8K r-x-- /lib64/libkeyutils-1.2.so
00000030f6c02000 2044K ----- /lib64/libkeyutils-1.2.so
00000030f6e01000 4K rw--- /lib64/libkeyutils-1.2.so
00000030f7c00000 1228K r-x-- /usr/lib64/libxml2.so.2.6.26
00000030f7d33000 2048K ----- /usr/lib64/libxml2.so.2.6.26
00000030f7f33000 36K rw--- /usr/lib64/libxml2.so.2.6.26
00000030f7f3c000 4K rw--- [ anon ]
00000030f8400000 144K r-x-- /usr/lib64/libk5crypto.so.3.1
00000030f8424000 2044K ----- /usr/lib64/libk5crypto.so.3.1
00000030f8623000 8K rw--- /usr/lib64/libk5crypto.so.3.1
00000030f8800000 32K r-x-- /usr/lib64/libkrb5support.so.0.1
00000030f8808000 2044K ----- /usr/lib64/libkrb5support.so.0.1
00000030f8a07000 4K rw--- /usr/lib64/libkrb5support.so.0.1
00000030f8c00000 584K r-x-- /usr/lib64/libkrb5.so.3.3
00000030f8c92000 2044K ----- /usr/lib64/libkrb5.so.3.3
00000030f8e91000 16K rw--- /usr/lib64/libkrb5.so.3.3
00000030f9400000 176K r-x-- /usr/lib64/libgssapi_krb5.so.2.2
00000030f942c000 2048K ----- /usr/lib64/libgssapi_krb5.so.2.2
00000030f962c000 8K rw--- /usr/lib64/libgssapi_krb5.so.2.2
00000030fa400000 280K r-x-- /lib64/libssl.so.0.9.8e
00000030fa446000 2048K ----- /lib64/libssl.so.0.9.8e
00000030fa646000 24K rw--- /lib64/libssl.so.0.9.8e
0000003100a00000 140K r-x-- /usr/lib64/libpng12.so.0.10.0
0000003100a23000 2048K ----- /usr/lib64/libpng12.so.0.10.0
0000003100c23000 4K rw--- /usr/lib64/libpng12.so.0.10.0
00002aaaaaac4000 40K r-x-- /lib64/libnss_files-2.5.so
00002aaaaaace000 2044K ----- /lib64/libnss_files-2.5.so
00002aaaaaccd000 4K r---- /lib64/libnss_files-2.5.so
00002aaaaacce000 4K rw--- /lib64/libnss_files-2.5.so
00002aaaaaccf000 32768K rw-s- /dev/zero (deleted)
00002aaaacccf000 4K rw-s- /dev/zero (deleted)
00002aac041d0000 8K rw--- [ anon ]
00002aac041d2000 4K rw-s- /dev/zero (deleted)
00002aac041d3000 4K rw-s- /dev/zero (deleted)
00002aac041d4000 4K rw-s- /dev/zero (deleted)
00002aac041d5000 4K rw-s- /dev/zero (deleted)
00002aac041de000 4K rw--- [ anon ]
00002aac041df000 2904K r-x-- /usr/local/mysql/lib/libmysqlclient.so.18.0.0
00002aac044b5000 1020K ----- /usr/local/mysql/lib/libmysqlclient.so.18.0.0
00002aac045b4000 524K rw--- /usr/local/mysql/lib/libmysqlclient.so.18.0.0
00002aac04637000 24K rw--- [ anon ]
00002aac0463d000 132K r-x-- /usr/lib64/libjpeg.so.62.0.0
00002aac0465e000 2044K ----- /usr/lib64/libjpeg.so.62.0.0
00002aac0485d000 4K rw--- /usr/lib64/libjpeg.so.62.0.0
00002aac0485e000 4K rw--- [ anon ]
00002aac0485f000 324K r-x-- /usr/local/lib/libcurl.so.4.2.0
00002aac048b0000 2044K ----- /usr/local/lib/libcurl.so.4.2.0
00002aac04aaf000 12K rw--- /usr/local/lib/libcurl.so.4.2.0
00002aac04ab2000 4K rw--- [ anon ]
00002aac04ab3000 196K r-x-- /usr/lib64/libidn.so.11.5.19
00002aac04ae4000 2044K ----- /usr/lib64/libidn.so.11.5.19
00002aac04ce3000 4K rw--- /usr/lib64/libidn.so.11.5.19
00002aac04ce4000 28K rw--- [ anon ]
00002aac04d2c000 316K rw--- [ anon ]
00002aac04d7b000 128K r-x-- /usr/local/lib/php/extensions/no-debug-non-zts-20090626/apc.so
00002aac04d9b000 2048K ----- /usr/local/lib/php/extensions/no-debug-non-zts-20090626/apc.so
00002aac04f9b000 12K rw--- /usr/local/lib/php/extensions/no-debug-non-zts-20090626/apc.so
00002aac04f9e000 36K rw--- [ anon ]
00007fffbee97000 84K rw--- [ stack ]
00007fffbeffc000 16K r-x-- [ anon ]
ffffffffff600000 8192K ----- [ anon ]
total 130704K



2Mダイエットできました。


負荷試験
nginxとPHP-FPMを少しキャッシュなどで太らせたいので別のマシンからApacheBenchしてみます。


./ab -n 1000 -c 20 -t 10 http://blog.kubox.info/




# free -m
total used free shared buffers cached
Mem: 497 346 151 0 11 126
-/+ buffers/cache: 208 289
Swap: 1027 9 1017



結構メモリを消費したみたいです。

LoadAverageも結構あがりました。


top - 17:16:48 up 7 days, 2:56, 3 users, load average: 4.12, 1.24, 0.44
Tasks: 107 total, 23 running, 75 sleeping, 8 stopped, 1 zombie
Cpu(s): 89.5%us, 4.5%sy, 0.0%ni, 0.0%id, 0.0%wa, 2.5%hi, 3.5%si, 0.0%st
Mem: 509792k total, 299092k used, 210700k free, 12056k buffers
Swap: 1052248k total, 10232k used, 1042016k free, 102124k cached

PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
11823 nginx 16 0 124m 23m 15m R 63.8 4.7 0:02.69 php-fpm
11810 nginx 16 0 124m 23m 15m R 31.9 4.6 0:02.31 php-fpm
11805 nginx 16 0 124m 23m 15m R 25.9 4.6 0:03.59 php-fpm
11824 nginx 16 0 125m 23m 15m R 21.9 4.8 0:01.19 php-fpm
11806 nginx 16 0 124m 23m 15m R 19.9 4.6 0:03.96 php-fpm
11807 nginx 16 0 125m 23m 15m R 15.0 4.8 0:02.25 php-fpm
11825 nginx 16 0 125m 23m 15m R 15.0 4.8 0:01.55 php-fpm
10715 nginx 15 0 21728 2180 756 S 5.0 0.4 0:01.02 nginx
11123 mysql 15 0 452m 50m 5684 S 1.0 10.1 0:01.65 mysqld




エラーがでるまでApacheBenchのスレッドを上げていきます。

同時に先ほどnginxでstatusとnginx_statusを設定したページにアクセスしてプロセスの状態を見ます。


#nginx_status
Active connections: 19
server accepts handled requests
815 815 815
Reading: 0 Writing: 19 Waiting: 0


Readingはnginxがリクエストのヘッダーを読み込んでいる数、Writingはレスポンス用のリクエストを生成している数、WaitingはKeepAliveで待っている数です。

KeepAliveは0(無効)にしているので必ず0になります。KeepAliveはDoS攻撃などの耐性が低くなるのでできるだけ無効で運用します。


100スレッド目からエラーが出始めました。


Server Software: nginx
Server Hostname: blog.kubox.info
Server Port: 80

Document Path: /
Document Length: 119439 bytes

Concurrency Level: 100
Time taken for tests: 11.310 seconds
Complete requests: 100
Failed requests: 2
(Connect: 0, Receive: 0, Length: 2, Exceptions: 0)
Write errors: 0
Non-2xx responses: 2
Total transferred: 11742858 bytes
HTML transferred: 11705790 bytes
Requests per second: 8.84 [#/sec] (mean)
Time per request: 11309.898 [ms] (mean)
Time per request: 113.099 [ms] (mean, across all concurrent requests)
Transfer rate: 1013.95 [Kbytes/sec] received

Connection Times (ms)
min mean[+/-sd] median max
Connect: 10 21 6.5 20 34
Processing: 475 6205 3240.7 6199 11285
Waiting: 129 5687 3298.2 5759 11123
Total: 489 6227 3241.4 6226 11304

Percentage of the requests served within a certain time (ms)
50% 6226
66% 8179
75% 9103
80% 9653
90% 10643
95% 11059
98% 11106
99% 11304
100% 11304 (longest request)




クライアント側のログを見てみます。


[error] 5717#0: *5587 recv() failed (104: Connection reset by peer) while reading response header from upstream, client: IP, server: blog.kubox.info, request: "GET / HTTP/1.0", upstream: "fastcgi://127.0.0.1:9000", host: "hostname"


どうやらnginxが受け取ったリクエストをPHP-FPMに処理を投げたときにconnectionを切られているみたいです。

このエラーの場合はnginxはユーザからリクエストを受け取り、PHP-FPMに処理を投げているのでボトルネックはPHP-FPMだと分かります。

PHP-FPMの設定を見直すとrequest_terminate_timeout= 5が悪さをしているようでタイムアウトを少し長めします。

request_terminate_timeout= 10


150スレッド目で同様のエラーが出力されました。

PHP-FPMのステータスを負荷試験中に見るとmax children reached: 1となっているのでPHP-FPMのプロセスが常時最大に達しているのが確認できました。


pool: www
process manager: dynamic
accepted conn: 317
listen queue len: 0
max listen queue len: -1
idle processes: 0
active processes: 10
total processes: 10
max children reached: 1



PHP-FPMのプロセスを増やしたいと思います。

ApacheやnginxやPHP-FPMはforkしてプロセス間で負荷分散しつつメモリの消費を節約します。

サーバが落ちる時は大体この辺りの設定ミスです。

実際にどれぐらいforkさせるのかはサービスによって異なりますが物理リソースを無駄なく利用するにはforkで生じるメモリ使用量を把握する必要があります。

メモリのお話については割愛しますが今回のPHP-FPMでいうとforkされたプロセスが使うメモリはおおよそ7M程度です。

物理メモリ100Mは残して起きたいのでさらに20プロセス増やす事にしました。


pm = dynamic
pm.max_children = 30
pm.start_servers = 11
pm.min_spare_servers = 10
pm.max_spare_servers = 15
pm.max_requests = 500



PHP-FPMを再起動後メモリの状態を見てみます。
予想に反してあまりメモリを消費しなかったみたいです。


# free -m
total used free shared buffers cached
Mem: 497 400 97 0 52 140
-/+ buffers/cache: 207 290
Swap: 1027 9 1017




プロセス数を3倍にしたので論理的には3倍のアクセスを処理できることになります。

再度ApacheBenchで今度は450スレッドまで引き上げて負荷試験をしてみます。


Server Software: nginx
Server Hostname: blog.kubox.info
Server Port: 80

Document Path: /
Document Length: 119439 bytes

Concurrency Level: 350
Time taken for tests: 10.132 seconds
Complete requests: 82
Failed requests: 0
Write errors: 0
Total transferred: 10160997 bytes
HTML transferred: 10128261 bytes
Requests per second: 8.09 [#/sec] (mean)
Time per request: 43245.804 [ms] (mean)
Time per request: 123.559 [ms] (mean, across all concurrent requests)
Transfer rate: 979.37 [Kbytes/sec] received




心なしかレスポンスが良くなりました。

vmstatを見てみるとrがCPUコア数を超えているのでCPUの負荷がかなり高い状態になっています。
メモリはswapは少々発生していますがswap inもoutも発生していないのでとりあえず問題はありません。
procsのbとSWAPのsoが発生し始めると大幅に性能が劣化しますので注意が必要です。

nginxのエラーログとvmstatを見る限りエラーもなしでCPUのみが高負荷なので理想な負荷です。


# vmstat 1
procs -----------memory---------- ---swap-- -----io---- --system-- -----cpu------
r b swpd free buff cache si so bi bo in cs us sy id wa st
1 0 10144 73552 54424 147776 0 0 1 3 2 16 0 0 99 0 0
31 0 10144 59592 54424 147776 0 0 0 0 2099 858 57 10 33 0 0
31 0 10144 60948 54424 147792 0 0 0 0 1744 762 94 6 0 0 0
31 0 10144 58468 54424 147868 0 0 0 4 1897 681 94 6 0 0 0
31 0 10144 59212 54424 147876 0 0 0 0 1904 842 94 6 0 0 0
34 0 10144 59708 54428 147948 0 0 0 460 1777 828 94 6 0 0 0
32 0 10144 59832 54428 147944 0 0 0 0 1785 835 94 6 0 0 0
32 0 10144 59212 54428 148020 0 0 0 0 1971 763 93 7 0 0 0
31 0 10144 59832 54428 148024 0 0 0 0 1891 896 94 6 0 0 0
31 0 10144 59708 54428 148100 0 0 0 0 2010 769 94 6 0 0 0
32 0 10144 59832 54436 148080 0 0 0 480 1943 855 93 7 0 0 0
34 0 10144 70760 54436 148232 0 0 0 0 2099 1320 92 9 0 0 0
35 0 10144 69560 54436 148444 0 0 0 0 1020 1727 95 5 0 0 0


Too many open files

800スレッド目からnginxのエラーログにToo many open filesが出力され始めました。

nginxのファイルオープン数の制限に引っかかったようです。

設定ファイルにworker_rlimit_nofile 10240;を追加します。

設定を追加後もエラーが消えないのでどうやらLinuxのファイルディスクリプタに引っかかっているようです。


#vi /etc/security/limits.conf
nginx soft nofile 10240
nginx hard nofile 10240


これでエラーはでなくなりました。


最終的に1000スレッドまで達し24時間負荷をかけ続けましたが(これはJmeterで)レスポンスタイムは平均で3秒程度まで落ちたものの安定したアクセスでエラーも出力されていませんでした。

expireを使いユーザのブラウザにキャッシュさせる方法も画像が多いサイトには有効です。

1000スレッド目で負荷を与える側の限界に達したので今回はこれ以上行う事はできませんがApacheと比べて最初はメモリ使用量がやや多いですが、高負荷時は一定のペースを保つので負荷の高いサイトにnginxはオススメです。


その他やったこと
libjpeg-turboに差し替え

libjpeg-truboはlibraryを差し替えるだけで簡単にjpeg系の処理を高速化できます。
http://sourceforge.jp/projects/freshmeat_libjpeg-turbo/


tar -zxvf nasm-2.09.02.tar.gz
cd nasm-2.09.02
./configure
make
make install

tar -zxvf libjpeg-turbo-1.0.90.tar.gz
cd libjpeg-turbo-1.0.90
./configure
make
make install


/opt/libjpeg-turbo/ にインストールされるので、一旦 /usr/lib/libjpeg* をtmpなどに待避させてから入れ替えます。


mkdir /tmp/libjpeg-backup/
mv /usr/lib/libjpeg* /tmp/libjpeg-backup/
cp -aL /opt/libjpeg-turbo/lib/lib* /usr/lib/
ldconfig


WordPressは接続の度にDBを叩くのでクエリーキャッシュを多めに設定しています。

skip-name-resolve
key_buffer_size = 20M
max_allowed_packet = 1M
table_open_cache = 1024
sort_buffer_size = 1M
read_buffer_size = 1M
read_rnd_buffer_size = 4M
myisam_sort_buffer_size = 8M
thread_concurrency = 8
character-set-server = utf8
skip-character-set-client-handshake
max_connect_errors=10000
max_connections=300
table_cache=256
thread_cache_size = 30
query_cache_size = 32M
wait_timeout = 60


innodb_data_file_path = ibdata1:1024M;ibdata2:2048M:autoextend
innodb_buffer_pool_size = 128M
innodb_log_file_size = 64M
innodb_flush_log_at_trx_commit = 1
innodb_flush_method=O_DIRECT



負荷試験中のMySQLの状態を見てみます。

この3つの値が増え始めるとMySQLの負荷が高くなってくるので上記の設定で予めキャッシュさせておきます。


mysql> SHOW GLOBAL STATUS;
Max_used_connections | 2
Opened_tables | 45
Opened_files | 121

6 件のコメント:

  1. [...] チューニングは、nginx+PHP-FPMでどこまでチューニングできるか at 改訂版 新卒ネットワークエンジニア を参考に。同一サーバのabだけど、こんな感じ。Wordpress + W3TC(Memcache)です。 [...]

    返信削除
  2. [...] http://blog.kubox.info/?p=175 http://blog.iss.ms/2011/01/08/130929 http://docs.komagata.org/4809 [...]

    返信削除
  3. [...] nginx+PHP-FPMでどこまでチューニングできるか at 改訂版 新卒ネットワークエンジニア [...]

    返信削除
  4. 勉強になりました。

    返信削除
  5. [...] 출처 : http://blog.kubox.info/?p=175 이 사이트도 nginx+PHP-FPM+MySQL로 동작하고 있습니다. php를fastcgi로 동작시키는 예는 많이 있었지만 PHP-FPM에서 쓰고 있다는 기사는 별로 없었습니다. yum등은 사용하 [...]

    返信削除
  6. [...] » nginx+PHP-FPMでどこまでチューニングできるか Network is unreachable http://blog.kubox.info/?p=175 [...]

    返信削除