さくらVPSのWordPressをチューニングして30倍高速化した方法

今日はさくらVPSに載せているWordPressのパフォーマンスをチューニングして高速化に成功したので安心して眠れるという話をします。

2.5ページ/秒だったのが70ページ/秒と30倍高速化。

以前はDaily数千PVで重くなっていたサイトがDaily3.6万PVを余裕で捌けるようになりました。
#ちなみにproxy cacheという手法はwordpressでなくても動的コンテンツ全般に有効です。

▼サーバ気にしなくて良くなったので今週末新宿御苑に花見に行けました☆。枝ぶりがいいさくらが多くてほんといいところだと思うの。

WordPressチューニング高速化結果

http://hara19.jp/のサーバ環境と測定結果は以下のとおり。

WordPress稼働環境

さくらVPS 1GB/CentOS5.5/PHP5.3/MySQL5.5/WordPress3.1。
WEBサーバはチューニングにあたりApache2.2.3からnginx0.8.53に変更しています。

WordPressチューニング高速化方法

基本的に「WordPressを100倍速くする! MySQLの調整やnginx proxy cache」の内容をひと通り実施してみました。

同記事で書かれている通りnginx proxy cacheの効果が大きいですね。

WordPress高速化結果測定

改善結果はab -c 100 -n 100 http://hara19.jp/で計測。これは同時コネクション100個で100PVの負荷をかける設定です。

改善前が2.56 [#/sec] とあって一秒あたり2.56ページしかさばけていません。アクセス集中時に重くなるのは処理待ちで待たされるから、ということです。

改善後は86.09 [#/sec] ということで34倍。素晴らしい!何回かやってみて65-80くらいなので30倍高速化されました。

改善前:Requests per second: 2.56 [#/sec] (mean)

$ab -n 100 -c 100 http://hara19.jp/
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking hara19.jp (be patient).....done


Server Software:        Apache/2.2.3
Server Hostname:        hara19.jp
Server Port:            80

Document Path:          /
Document Length:        91243 bytes

Concurrency Level:      100
Time taken for tests:   39.135 seconds
Complete requests:      100
Failed requests:        0
Write errors:           0
Total transferred:      9156400 bytes
HTML transferred:       9124300 bytes
Requests per second:    2.56 [#/sec] (mean)
Time per request:       39135.269 [ms] (mean)
Time per request:       391.353 [ms] (mean, across all concurrent requests)
Transfer rate:          228.48 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:      104  354 564.8    112    1654
Processing:  3385 21488 10676.6  20430   37484
Waiting:      110 15415 10426.5  12855   32960
Total:       3490 21842 11008.5  20542   39134

改善後:Requests per second: 86.09 [#/sec] (mean)

$ab -c 100 -n 100 http://hara19.jp/This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking hara19.jp (be patient).....done


Server Software:        nginx/0.8.53
Server Hostname:        hara19.jp
Server Port:            80

Document Path:          /
Document Length:        90617 bytes

Concurrency Level:      100
Time taken for tests:   1.162 seconds
Complete requests:      100
Failed requests:        0
Write errors:           0
Total transferred:      9087900 bytes
HTML transferred:       9061700 bytes
Requests per second:    86.09 [#/sec] (mean)
Time per request:       1161.581 [ms] (mean)
Time per request:       11.616 [ms] (mean, across all concurrent requests)
Transfer rate:          7640.36 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:      131  183  31.9    183     239
Processing:   245  597 162.3    630     963
Waiting:       15  116 129.0     36     518
Total:        378  780 183.4    832    1159

Percentage of the requests served within a certain time (ms)
  50%    832
  66%    867
  75%    887
  80%    955
  90%   1053
  95%   1065
  98%   1097
  99%   1159
 100%   1159 (longest request)

▼新宿御苑は芝生で語り合う花見カップルが沢山いてのどかな感じ

アクセス集中時に重い!

そもそもの話。

hara19.jpは昨年8月スタートした原宿・表参道エリアのグルメ/イベントなどを扱う地域情報サイトですがここの所アクセスの急増で超重い日がありました。

例えばフォロワが多い人に紹介されると自分でも管理画面に辿りつけないオワタ\(^o^)/ってなってました。

PageViewベースでは通常時一日2000PVだったのが1月のピークが8600PV、3月に20147PV、そして直近4月には36444PVという日が!

で、そのたびに「速報:サーバが死亡しました(><)」とか騒いでいた訳ですがKRAYの@amachinが書いた「WordPressを100倍速くする! MySQLの調整やnginx proxy cache」を読んで100倍高速化されるならやってみるか、と根本的対策に乗り出した次第。

▼11万フォロワの佐々木俊尚さんや6万フォロワのpixiv公式twitterからtweetされると落ちてました。

実はすごく普通のことをしてるのにすぎない。でもこの普通のTweetをできない企業が圧倒的に多い日本の現実。 /ラフォーレ原宿の公式アカウントがtwitter活用事例として凄まじい件 http://t.co/dozv1Mbless than a minute ago via Tweet Button Favorite Retweet Reply

[原宿・表参道.jp] MS台湾がpixivでまさかの萌え絵キャンペーンを始めた件 http://p.tl/b_NPless than a minute ago via web Favorite Retweet Reply

WordPress高速化設定

WordPressを100倍速くする! MySQLの調整やnginx proxy cache」に従って設定していきます。既に実施済みだったものもあり。

  1. APCを導入してPHPを速くする(済)
  2. WP Super Cacheを使う(済)
  3. DBを最適化するプラグインを導入する
  4. nginx+FastCGIにする
  5. nginxのproxy cacheを使う

1.APCを導入してPHPを速くする(済)

APC(Alternative PHP Cache)はPHPの中間コードのキャッシュや最適化を行うモジュール。Remi リポジトリからインストール。Remiリポジトリについては省略。

yum install php-pecl-apc

2.WP Super Cacheを使う(済)

WP SuperCacheをインストールした。WordPressのキャッシュプラグインではメジャーなものの一つ。

APCではほとんど速度変わらなかったが、WP Super Cacheを入れると体感できるくらい速くなります。

WP SuperCacheの仕組みは毎回動的に生成していたページを静的ファイルを生成するもの。動的ページより静的ページの方が速いのは当然。

静的/動的の振り分けはPHPでもできるしapacheなら.htaccessでもできます。

http://wordpress.org/extend/plugins/wp-super-cache/

3.DBを最適化するプラグインを導入する

手作業でDBメンテするのは手間なのでDB最適化プラグインを導入しました。最適化以外にバックアップなどひと通りのDB操作が揃っているので便利。

http://wordpress.org/extend/plugins/wp-dbmanager/

5.nginx+FastCGIにする

nginxのproxy cacheを試すのが最終目標なのでWEBサーバをnginxにします。

さらにPHPについてはspawn-fcgiFastCGI化して4つのPHPが常時起動した状態にします。これで動的に毎回起動するより速くなります。

# yum install nginx
Installed:
  nginx.x86_64 0:0.8.53-1.el5                                                                                         
Dependency Installed:
  GeoIP.x86_64 0:1.4.7-0.1.20090931cvs.el5                        gd.x86_64 0:2.0.33-9.4.el5_4.2       
# yum --enablerepo=remi install fcgi spawn-fcgi
Installed:
  fcgi.i386 0:2.4.0-12.el5            fcgi.x86_64 0:2.4.0-12.el5            spawn-fcgi.x86_64 0:1.6.3-1.el5          
# chkconfig nginx on
# service nginx on
# chmod a+x /etc/init.d/php-fastcgi
# yum install spawn-fcgi
# vi /etc/init.d/php-fastcgi
# /sbin/chkconfig --add php-fastcgi
# service php-fastcgi start
# service nginx configtest
# service nginx start

cat /etc/init.d/php-fastcgi

#!/bin/sh
#
# php-cgi - php-fastcgi swaping via  spawn-fcgi
#
# chkconfig:   - 85 15
# description:  Run php-cgi as app server
# processname: php-cgi
# config:      /etc/sysconfig/phpfastcgi (defaults RH style)
# pidfile:     /var/run/php_cgi.pid
# Note: See how to use this script :
# http://www.cyberciti.biz/faq/rhel-fedora-install-configure-nginx-php5/
# Source function library.
. /etc/rc.d/init.d/functions
 
# Source networking configuration.
. /etc/sysconfig/network
 
# Check that networking is up.
[ "$NETWORKING" = "no" ] && exit 0

 
spawnfcgi="/usr/bin/spawn-fcgi"
php_cgi="/usr/bin/php-cgi"
prog=$(basename $php_cgi)
server_ip=127.0.0.1
server_port=9000
server_user=nginx
server_group=nginx
server_childs=4
pidfile="/var/run/php_cgi.pid"
 
# do not edit, put changes in /etc/sysconfig/phpfastcgi
[ -f /etc/sysconfig/phpfastcgi ] && . /etc/sysconfig/phpfastcgi
 
start() {
    [ -x $php_cgi ] || exit 1
    [ -x $spawnfcgi ] || exit 2
    echo -n $"Starting $prog: "
    daemon $spawnfcgi -a ${server_ip} -p ${server_port} -u ${server_user} -g ${server_group} -P ${pidfile} -C ${server_childs} -f ${php_cgi}
    retval=$?
    echo
    return $retval
}
 
stop() {
    echo -n $"Stopping $prog: "
    killproc -p ${pidfile} $prog -QUIT
    retval=$?
    echo
    [ -f ${pidfile} ] && /bin/rm -f ${pidfile}
    return $retval
}
 
restart(){
    stop
    sleep 2
    start
}
 
rh_status(){
    status -p ${pidfile} $prog
}
 
case "$1" in
    start)
        start;;
    stop)
        stop;;
    restart)
        restart;;
    status)
        rh_status;;
    *)
        echo $"Usage: $0 {start|stop|restart|status}"
        exit 3
esac

6.nginxのproxy cacheを使う

nginxのproxy cache機能を使うと動的コンテンツをメモリと静的ファイルにキャッシュします。メモリなら動的生成によるCPU負荷やファイルIOが発生しないので超高速になります。

構成としては80のフロントサーバと8001のバックエンドサーバになっていて、80側で処理できないものはバックエンド側から取ってくる形。

server {listen 80; とあるブロックがフロントサーバ、server { listen 8001;とあるのがバックエンドサーバの設定。

肝としては下記の部分。10MBのメモリキャッシュ、50MBのディスクキャッシュ、120分アクセスがなければ削除。

proxy_cache_path /var/www/nginx_cache levels=1:2 keys_zone=czone:10m max_size=50m inactive=120m;
proxy_temp_path  /var/www/nginx_tmp;

フロント側の設定では下記。8001で一旦200番で返ってきたコンテンツは10分ポーリングしない。

proxy_cache_valid  200 10m;

cat /etc/nginx/nginx.conf

#######################################################################
#
# This is the main Nginx configuration file.  
#
# More information about the configuration options is available on 
#   * the English wiki - http://wiki.nginx.org/Main
#   * the Russian documentation - http://sysoev.ru/nginx/
#
#######################################################################

#----------------------------------------------------------------------
# Main Module - directives that cover basic functionality
#
#   http://wiki.nginx.org/NginxHttpMainModule
#
#----------------------------------------------------------------------

user              nginx;
worker_processes  2;

error_log  /var/log/nginx/error.log;
#error_log  /var/log/nginx/error.log  notice;
#error_log  /var/log/nginx/error.log  info;

pid        /var/run/nginx.pid;


#----------------------------------------------------------------------
# Events Module 
#
#   http://wiki.nginx.org/NginxHttpEventsModule
#
#----------------------------------------------------------------------

events {
    worker_connections  1024;
}


#----------------------------------------------------------------------
# HTTP Core Module
#
#   http://wiki.nginx.org/NginxHttpCoreModule 
#
#----------------------------------------------------------------------

http {
    include       /etc/nginx/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/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    #gzip  on;

    proxy_cache_path /var/www/nginx_cache levels=1:2 keys_zone=czone:10m max_size=50m inactive=120m;
    proxy_temp_path  /var/www/nginx_tmp;
    upstream backend {
        ip_hash;
        server 127.0.0.1:8001;
    }

    #
    # The default server
    #
    server {
        listen       8001;
        server_name  _;
        client_max_body_size 7m;
        #charset koi8-r;

        #access_log  logs/host.access.log  main;

        location / {
            root   /var/www/hara19.jp;
            index  index.php index.html index.htm;

            # static files
            if (-f $request_filename) {
                expires 30d;
                break;
            }

            # request to index.php
            if (!-e $request_filename) {
                rewrite ^(.+)$  /index.php?q=$1 last;
            }
        }

        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        #
        location ~ \.php$ {
            root           html;
            fastcgi_pass   127.0.0.1:9000;
            fastcgi_index  index.php;
            fastcgi_param  SCRIPT_FILENAME  /var/www/hara19.jp/$fastcgi_script_name;
            include        fastcgi_params;
        }


        error_page  404              /404.html;
        location = /404.html {
            root   /usr/share/nginx/html;
        }

        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   /usr/share/nginx/html;
        }
    }

server {
        listen 80;      
        server_name hara19.jp;
        client_max_body_size 7m;
        #access_log  /var/log/nginx/hara19.jp.access.log;
        error_log  /var/log/nginx/hara19.jp.error.log;
                                        
        location ~ .*\.(htm|html|jpg|JPG|gif|GIF|png|PNG|swf|SWF|css|CSS|js|JS|inc|INC|ico|ICO) {
            root    /var/www/hara19.jp;
            index   index.html;
            ssi     on;
            break;
        }

        location /wp-admin { proxy_pass http://backend; }
        location /wp-login.php { proxy_pass http://backend; }

        location / {
            if ($http_cookie ~* "comment_author_|wordpress_(?!test_cookie)|wp-postpass_" ) {
                set $do_not_cache 1;
            }

            if ($http_user_agent ~* “2.0\ 2MMP|240×320|400X240|AvantGo|BlackBerry|Blazer|Cellphone|Danger|DoCoMo|Elaine/3.0|EudoraWeb|Googlebot-Mobile|hiptop|IEMobile|KYOCERA/WX310K|LG/U990|MIDP-2.|MMEF20|MOT-V|NetFront|Newt|Nintendo\ Wii|Nitro|Nokia|Opera\ Mini|Palm|PlayStation\ Portable|portalmmm|Proxinet|ProxiNet|SHARP-TQ-GX10|SHG-i900|Small|SonyEricsson|Symbian\ OS|SymbianOS|TS21i-10|UP.Browser|UP.Link|webOS|Windows\ CE|WinWAP|YahooSeeker/M1A1-R2D2|iPhone|iPod|Android|BlackBerry9530|LG-TU915\ Obigo|LGE\ VX|webOS|Nokia5800″) {
                set $do_not_cache 1;
            }

            proxy_no_cache     $do_not_cache;
            proxy_cache_bypass $do_not_cache;

            proxy_pass http://backend;
            proxy_cache czone;
            proxy_cache_key $scheme$proxy_host$uri$is_args$args;
            proxy_cache_valid  200 10m;
        }
}

    # Load config files from the /etc/nginx/conf.d directory
    include /etc/nginx/conf.d/*.conf;

}

基本的にKRAYさんの記事の通りで動くが何点か変更しています。

1.画像がアップできない

画像をアップするとHTTPエラーとか怒られてアップできなくなりました。

ログ側ではファイルサイズが大き過ぎるというエラーメッセージ。

これはフロント、バックエンド両方の設定にclient_max_body_size 7m;を足すことで解決。

2011/03/27 02:38:09 [error] 24514#0: *83720 client intended to send too large body: 2521360 bytes, client: 114.22.29.149, server: hara19.jp, request: "POST /wp-admin/async-upload.php HTTP/1.1", host: "hara19.jp"

2.画像のサムネールが表示できなくなった

http://hara19.jp/wp-content/themes/cityguide/thumb.php?src=http://hara19.jp/wp-content/uploads/2011/03/3a5ec14e4f8d60356ea75c7177384b1d.png&w=616&h=365&zc=1&q=90のような動的に生成するサムネールがバッテンになっていました。エラーメッセージは「remote host “hara19.jp” not allowed」。

これはWordPress本体ではなくThemeに含まれるthumb.phpを修正することで解決。proxyを採用して80と8001のサーバに分離したことで、8001サーバから見るとhara19.jp(80)は別ドメイン扱いになっていた様子。

TimThumb version : 1.14

function checkExternal ($src) {
    $allowedSites = array(
        'hara19.jp',
        'flickr.com',
        'picasa.com',
        'blogger.com',
        'wordpress.com',
        'img.youtube.com',
    );

3.CPU数に合わせて設定変更

さくらVPSだと仮想2コアだからworker_processes 2;にすると処理能力あがるとのご指摘いただいて変更しました。現状の速度は変わりませんでしたがコネクション数が増えたときの対策ということで。

さくらVPSについて

元々海外の256MB2000円くらいのVPSで動かしていたのをさくらVPS512MB、さくらVPS1GBとステップアップしてきました。

海外のVPSだとコンソールのタイムラグがあったり「負荷高いからサーバー落としたよ!再起動後負荷対策してね(ハート)」というメールが来てしょんぼりしたりしましたがさくらVPSについては価格以上の品質だと感じます。

VPS512MBでも980円、1GBでも2980円とお小遣いでサーバ借りられるのは素晴らしい時代ですね!

さくら

hara19.jpの歴代アクセス上位のエントリ

ちょっと負荷テストしたいのでみなさんゆっくりしていってね!

ちなみにサイトのDaily最高の36444PVはジャニーズが代々木第一体育館で募金活動していた関連記事3本に同時にアクセス来ていた日でした。

  1. 渋谷プラネタリウム
    46,233PV。東急文化会館にあった五島プラネタリウムを継ぐもの。「はやぶさ」ドキュメンタリーを放映しているので大人も行くべき
  2. 地震のデマ・チェーンメールその1
    67,294PV、87はてブ、2250tweets、227いいね。一人一人デマを潰していくのは手におえないのでブログに書くことにした。
  3. 辻元清美氏と阪神大震災
    35,048PV、104tweets。辻元清美氏がコピー機を持って現地入りし「自衛隊は違憲なので、自衛隊から食料を受け取らないで」とビラを配ったというコピペ、よく見るけどこれデマなんですよ。知ってました?
  4. コスモプラネタリウム渋谷の上映時間、チケット入手方法
    30,321PV。コスモプラネタリウム渋谷は予約不可で当日券のみなのでチケット取るには早く良く必要あり。
  5. ラフォーレ原宿の公式アカウントがtwitter活用事例として凄まじい件
    20,802PV、545はてブ、905tweets、211いいね。ラフォーレのセール公式アカウント( @Laforet_SALE )は期間中に529件、100tweet/一日と怒涛のつぶやき。うち約半数の221件が画像付き投稿とtwitter企業活用の正統派事例と言える
  6. ジャニーズチャリティーイベント@代々木第一体育館
    17,922PV。はっはっは!人がゴミのようだ!とラピュタの台詞を言いたくなる光景なので必見。
  7. 地震のデマ・チェーンメールその2 15,863PV
  8. 辻元清美のピースボートが救援物資を足止め・横流し?
    14,574PV。辻元氏がいわき市で物資を足止めして出身団体のピースボートに配布させて手柄にしようとしている、という悪意のあるデマ。ピースボートは福島県いわき市にはいない。宮城県石巻市で支援活動している。
  9. デマ:東電技術者が原発から逃げた上酒を飲んでいた?
    14,447PV、522tweets。ピースボートの件と同じく二階堂ドットコムがネタ元だったので二階堂ドットコムは信頼できないよね、ということで取り扱ったらセンセーショナルだったのでよくRTされた。
  10. ジャニーズ代々木第一体育館チャリティーイベントで早くも行列ができてる? 13,263PV
  11. ジャニーズ募金の行列が凄い 12,499PV
  12. サボイ表参道店オープン
    7380PV。TVで表参道店に言及したCMが流れていてSEO的に上位だったため

謝辞

この記事はKRAYの@amachinが書いた「WordPressを100倍速くする! MySQLの調整やnginx proxy cache」を参考にしています。

@amachinありがとう!ところでKRAYのサイトの右側のわんこが気になって仕方なかったので今度モフモフしに行っていいですかイイですか!

19 COMMENTS

4/17 今日の箇条書き | グラスレ

[…] ・WordPressをチューニングして30倍高速化した方法 レンタルサーバーだからあんまりできなかったけど、これ参考にしてグラスレ軽くしました。 […]

現在コメントは受け付けておりません。