CDNで画像配信の続き

当面はGyazoを使った疑似CDN配信で運用すると言ったが、実はウソだ。
さくっとCloudinaryを導入しちゃいました。
CDNの機能でフォーマット変換が行えるようになりましたので、記事内の画像をWebPに置き換えてしまいました。
今どきWebPを表示できないブラウザなんて存在しないと思うので、問答無用で置き換えてしまっても大丈夫でしょう。
たぶん…大丈夫…

Gyazoを使った疑似CDNは1年くらい使ってて、キャッシュミス発生時以外はそれなりに快適ではあったんですが、リサイズ時のファイルサイズが大きいとかフォーマット変換が出来ないとか細かいところで不便なところが合ったので移行してしまいました。

今回はとりあえずでCloudinaryを採用してみましたが、imgixとかも良さそうなので気が向いたら試してみるかもしれません。

CDNで画像配信?

今どきは個人ブログの画像もCDN配信するのが普通だよねってことで、15件くらい前の記事から画像置き場としてGyazoを利用させてもらってます。
Gyazoに保存した画像はCloudflareから配信されるため、実質的にCDN配信を実現してる感じですね。

Cloudflareからの配信なので基本的には快適な動作なんですが、キャッシュミスがそこそこ発生しているのが難点ですが本格的なCDNに比べて簡単に使えるので我慢ですね。
無償利用できる画像配信専門のCDNを使えという意見もあるとは思いますが、Gyazoの有料プランを別件で使ってたのでCDN用途に流用しちゃってます。
そのうちWebPへの移行とか考え始めたらimgixとかCloudinaryの導入を考えてみたいと思います。

それでは生存確認を兼ねた投稿は以上となります。

ECDSA証明書

楕円曲線暗号の証明書は面倒くさいから導入しないと言ったな。あれは嘘だ。

というわけで、ECDSAの証明書に切り替えました。
暗号スイートはこんな感じの設定です。

ssl_protocols       TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers         ECDSA+AES:+AES256:+SSLv3;

ECDSAで使える暗号スイートは非常に少ないので、以下の方針としました。

  • 暗号化はAESのみ
  • 鍵長は128bitを優先
  • AESGCMを優先
  • SHA1は優先度を下げる
openssl ciphers -v 'ECDSA+AES:+AES256:+SSLv3'
ECDHE-ECDSA-AES128-GCM-SHA256 TLSv1.2 Kx=ECDH     Au=ECDSA Enc=AESGCM(128) Mac=AEAD
ECDHE-ECDSA-AES128-SHA256 TLSv1.2 Kx=ECDH     Au=ECDSA Enc=AES(128)  Mac=SHA256
ECDHE-ECDSA-AES256-GCM-SHA384 TLSv1.2 Kx=ECDH     Au=ECDSA Enc=AESGCM(256) Mac=AEAD
ECDHE-ECDSA-AES256-SHA384 TLSv1.2 Kx=ECDH     Au=ECDSA Enc=AES(256)  Mac=SHA384
ECDHE-ECDSA-AES128-SHA  SSLv3 Kx=ECDH     Au=ECDSA Enc=AES(128)  Mac=SHA1
ECDHE-ECDSA-AES256-SHA  SSLv3 Kx=ECDH     Au=ECDSA Enc=AES(256)  Mac=SHA1

あとは、証明書の自動更新がちゃんと動くかだ・・・

ブルートフォースアタック

今週の火曜日・水曜日と、このブログに対してブルートフォースアタックと思われるアクセスがありました。
話題のWordpressのadminを狙った攻撃だったようです。

当サイトはLimit Login Attemptsプラグインと、Google Authenticatorプラグインを導入済みでしたので、
ひっそりと運営してる個人サイトなんか狙うなよと思いつつも静観していたのですが・・・
Lockoutしても、Lockoutしても、次から次へと新しいIPから接続してくる/(^o^)\ナンテコッタイ

最終的に、1200以上のIPからの接続がありました。
BOTネット凄すぎる(‘A`)

何事も無くやり過ごせたので、Lockoutするまで接続してきた983IPを晒しておきます。

続きを読む →

FastCGI Cacheの設定を更新しました

昨日の記事で、暫くこの設定で運用しますと書きましたが、早くも問題が出てしまいました(‘A`)

一部のクローラーや、Wordpress自身のピンバック機能がHEADメソッドでアクセスを行うようですが、HEADでキャッシュが生成されると中身の無いキャッシュが出来てしまうことが分かりました。

HEADでボディ無しデータを取得したタイミングでキャッシュを作ってしまうわけですので、正しい動きではあるのですがキャッシュ運用という観点では非常に困る挙動です。

HEADの禁止とか、リクエストメソッド毎に別キャッシュを生成するとか色々考えたのですが、今回はHEADアクセス時にはキャッシュを使わないで動作させることにしました。

        if ($request_method = "HEAD") {
            set $do_not_cache 1;
        }

こんな感じの設定を追加して、HEADアクセスの時はキャッシュをバイパスさせます。

元記事の方も設定サンプルを更新してあります。

今度こそ設定変更なしで長期運用するんだ……

FastCGI Cache

NginxのCacheがとても速いのは前回の記事でよく分かりましたが、Webサーバーと
Proxyサーバーをそれぞれ用意するのはリソースの無駄に思えます。
と言うわけで、Nginxのもう一つのCache機能であるFastCGI Cacheに設定を変更しました。

Proxy CacheはNginxがバックエンドのWebサーバーに処理を転送し返ってきたコンテンツを
キャッシュして、Nginx自身はキャッシュを送信することでパフォーマンスを上げる仕組みです。
今回導入したFastCGI CacheはFastCGIのレスポンスをキャッシュする機能です。
これを利用すると、Proxy Cacheを利用したのと同じ効果をProxyサーバーなしに実現できますので、
無駄な通信と処理がなくなりシンプルな構成となります。

設定も通常のFastCGIを使う設定にCache設定を追加するだけなので、シンプルで簡単です。
Cache制御の設定もProxyの時と同じ考え方で設定できます。

2013/01/13 設定を修正しました。
HEADアクセスで空のキャッシュが出来てしまう現象の対策

http {
    include                 mime.types;
    default_type            application/octet-stream;
    sendfile                on;
    directio                8m;
    keepalive_timeout       30;

    # CacheのHIT/MISSなどの情報をログに出力
    log_format  main       '$remote_addr - $remote_user [$time_local] "$request" '
                           '$status $body_bytes_sent "$http_referer" '
                           '"$http_user_agent" $upstream_cache_status';

    gzip                    on;
    gzip_http_version       1.1;
    gzip_types              text/plain
                            text/xml
                            text/css
                            application/xml
                            application/xhtml+xml
                            application/rss+xml
                            application/atom_xml
                            application/javascript
                            application/x-javascript ;
    gzip_disable            "MSIE [1-6]¥." "Mozilla/4";
    gzip_comp_level         1;
    gzip_proxied            any;
    gzip_vary               on;
    gzip_buffers            4 8k;
    gzip_min_length         1000;

    fastcgi_cache_path      /var/tmp/nginx/fcache levels=1:2 
                            keys_zone=fcache:8m max_size=100m inactive=7d;

    server {
        listen          80;
        server_name     mmio.net;
        access_log      /var/log/nginx-blog.log main;

        # 静的ファイルの配信
        location / {
            root     /home/www/blog/ ;
            index    index.html index.htm index.php;
            location ~* \.(jpg|jpeg|gif|png|css|js|ico)$ {
                expires 14d;
            }
            # WordPressのパーマリンク対策
            try_files $uri $uri/ /index.php;
        }
        location = /favicon.ico { log_not_found off; }
        location = /robots.txt  { log_not_found off; }
        location ~ /\.ht { deny  all; }

        # モバイルデバイスはキャッシュを使わない
        if ($http_user_agent ~* '(DoCoMo|UP\.Browser|SoftBank|WILLCOM|
                                  emobile|iPhone|iPod|Android.*Mobile)') {
            set $do_not_cache 1;
        }
        # WordPressにログイン中はキャッシュを使わない
        if ($http_cookie ~* "wordpress_logged_in_[^=]*=([^%]+)%7C") {
            set $do_not_cache 1;
        }
        # HEADアクセスはキャッシュを使わない
        if ($request_method = "HEAD") {
            set $do_not_cache 1;
        }

        
        # PHP処理をFastCGIに転送
        location ~ \.php$ {
            fastcgi_pass         unix:/tmp/php.sock;
            fastcgi_index        index.php;
            fastcgi_param        SCRIPT_FILENAME  /home/www/blog/$fastcgi_script_name;
            fastcgi_cache        fcache;
            fastcgi_no_cache     $do_not_cache;
            fastcgi_cache_bypass $do_not_cache;
            fastcgi_cache_key    $scheme://$host$request_uri;
            fastcgi_cache_valid  200 7d;
            fastcgi_cache_valid  301 7d;
            fastcgi_cache_valid  any 10m;
            include              fastcgi_params;
        }
        # Cache破棄のためのURL
        location ~ /purge(/.*) {
            allow 127.0.0.1;
            allow 49.212.135.193;
            deny  all;
            fastcgi_cache_purge fcache $scheme://$host$1;
        }
    }
}

キャッシュ保持期間が7日とやや長めですが、記事の更新時に自動的にキャッシュを破棄する
仕組みを導入していますので、更新がいつまでも反映されないという事態は発生しないはず。
ngx_cache_purgeモジュールを組み込んでいない環境であれば、保持期間は10分程度にしておくのが
無難だと思います。

とりあえずは、不具合などが起きない限りこの設定で運用してみたいと思います。

WordPress高速化の効果測定

今までに行ってきたWordpressの高速化設定ですが、どの程度の効果があるかベンチマークしてみました。

試験環境
さくらのVPS 2G/FreeBSD8.2(64bit)
nginx/1.2.3 + php/5.2.17
Wordpress3.5

試験コマンド
同時接続10で10秒間テストを実行

ab -c 10 -t 10 https://mmio.net/ 

素の状態
APC:OFF, Cache:OFF

Server Software:        nginx/1.2.3
Server Hostname:        mmio.net
Server Port:            80

Document Path:          /
Document Length:        45331 bytes

Concurrency Level:      10
Time taken for tests:   12.473 seconds
Complete requests:      21
Failed requests:        0
Write errors:           0
Total transferred:      1266922 bytes
HTML transferred:       1260862 bytes
Requests per second:    1.68 [#/sec] (mean)
Time per request:       5939.750 [ms] (mean)
Time per request:       593.975 [ms] (mean, across all concurrent requests)
Transfer rate:          99.19 [Kbytes/sec] received

なにもチューニングしてない状態のWordpressだと1.68件/秒しか処理できませんでした。
Wordpressは重いと良く言われていますが、思わず納得してしまいそうな遅さです。

APCを使ってPHPを高速化
APC:ON, Cache:OFF

Server Software:        nginx/1.2.3
Server Hostname:        mmio.net
Server Port:            80

Document Path:          /
Document Length:        45329 bytes

Concurrency Level:      10
Time taken for tests:   10.021 seconds
Complete requests:      130
Failed requests:        0
Write errors:           0
Total transferred:      6012923 bytes
HTML transferred:       5985653 bytes
Requests per second:    12.97 [#/sec] (mean)
Time per request:       770.837 [ms] (mean)
Time per request:       77.084 [ms] (mean, across all concurrent requests)
Transfer rate:          585.98 [Kbytes/sec] received

APCによりPHP実行が高速化されたため、12.97件/秒と大幅に速度が向上しています。
この程度の処理能力があれば、普通に使う分には何も不満は感じないはずです。
と言うか、10万PV/dayとかのブログでも余裕で捌けそうな気がします・・・

Nginx ProxyCacheを使った高速化
APC:ON, Cache:ON

Server Software:        nginx/1.2.3
Server Hostname:        mmio.net
Server Port:            80

Document Path:          /
Document Length:        45337 bytes

Concurrency Level:      10
Time taken for tests:   10.000 seconds
Complete requests:      24795
Failed requests:        0
Write errors:           0
Total transferred:      1129185044 bytes
HTML transferred:       1124176252 bytes
Requests per second:    2479.50 [#/sec] (mean)
Time per request:       4.033 [ms] (mean)
Time per request:       0.403 [ms] (mean, across all concurrent requests)
Transfer rate:          110271.78 [Kbytes/sec] received

Nginx ProxyCacheによる動的コンテンツの再利用は効果が絶大です。
2479.5件/秒と、ちょっと意味が分からない速度となっています。

NginxのProxyCacheが必要な状況と言うのがちょっと思い浮かばないので、
Wordpressを快適に使うにはAPCでPHPを高速化するだけで十分じゃね?
と言う事で良いですかね?
だめ?

NginxでProxy Cacheしてみた

年末に少しだけ書いたWordpressの高速化ですが、高速化の最終系とも言える
Nginx proxy cacheに手を出してみました。

性能強化しても、アクセスなんて殆ど無いんですけどね。
やってみたいから設定した! ただそれだけです。

基本方針としては

  • 静的コンテンツも動的コンテンツも可能な限りキャッシュする。
  • モバイル系デバイスからの接続はキャッシュしない。
  • 管理画面はキャッシュしない。
  • ログイン中はキャッシュしない。
  • 記事のポストやコメント書き込みがあったらキャッシュを破棄する。

で、こんな感じで設定してみました。

http {
    include       mime.types;
    default_type  application/octet-stream;

    sendfile        on;
    directio        8m;
    keepalive_timeout  10;

    gzip              on;
    gzip_http_version 1.1;
    gzip_types        text/plain
                      text/xml
                      text/css
                      application/xml
                      application/xhtml+xml
                      application/rss+xml
                      application/atom_xml
                      application/javascript
                      application/x-javascript ;
    gzip_disable      "MSIE [1-6]\." "Mozilla/4";
    gzip_comp_level   1;
    gzip_proxied      off;
    gzip_vary         on;
    gzip_buffers      4 8k;
    gzip_min_length   1000;

    proxy_cache_path   /var/tmp/nginx/cache levels=1:2 keys_zone=czone:8m max_size=100m inactive=7d;
    proxy_set_header   Host               $host;
    proxy_set_header   X-Real-IP          $remote_addr;
    proxy_set_header   X-Forwarded-Host   $host;
    proxy_set_header   X-Forwarded-Server $host;
    proxy_set_header   X-Forwarded-For    $proxy_add_x_forwarded_for;
    proxy_cache_valid  200                1d;
    proxy_cache_valid  any                10m;
    
    upstream backend {
        ip_hash;
        server 127.0.0.1:8080;
    }

### front cache ###
    server {
        listen       80;
        server_name  mmio.net;
        
        location /wp-admin { proxy_pass http://backend; }
        location ~ .*\.php { proxy_pass http://backend; }
        location / {
            proxy_pass         http://backend;
            proxy_no_cache     $do_not_cache;
            proxy_cache_bypass $do_not_cache;
            proxy_cache czone;
            proxy_cache_key    $uri$is_args$args;

            if ($http_user_agent ~* '(DoCoMo|UP\.Browser|SoftBank|WILLCOM|emobile|iPhone|iPod|Android.*Mobile)') {
                set $do_not_cache 1;
            }
            if ($http_cookie ~* "comment_author_[^=]*=([^%]+)%7C|wordpress_logged_in_[^=]*=([^%]+)%7C") {
                set $do_not_cache 1;
            }
        }
        location ~ /purge(/.*) {
           allow 127.0.0.1;
           allow 49.212.135.193;
           deny all;
           proxy_cache_purge czone $1$is_args$args;
        }
    }    

### backend server ###
    server {
        listen       8080;
        server_name  mmio.net;
        access_log  /var/log/nginx-backend.log;

        location / {
            root   /home/www/blog/ ;
            index  index.html index.htm index.php;
            ## static files ##
            if (-f $request_filename) {
                expires 14d;
                break;
            }
            try_files $uri $uri/ /index.php;
	    }
        location ~ \.php$ {
            fastcgi_pass   unix:/tmp/php.sock;
            fastcgi_index  index.php;
            fastcgi_param  SCRIPT_FILENAME  /home/www/blog/$fastcgi_script_name;
            include        fastcgi_params;
        }
        location = /favicon.ico { log_not_found off; }
        location = /robots.txt  { log_not_found off; }
        location ~ /\.ht { deny  all; }
    }
}

この設定で、動的コンテンツは1日、静的コンテンツは14日のキャッシュが行われます。
Wordpressのキャッシュ制御プラグインへのインタフェースも用意したので、
更新時のキャッシュ破棄も問題なしです。
これで、アクセス数が激増しても大丈夫!

ほとんどアクセス無いんだけどね(´・ω・`)