Tweak m1.small

From misc notes
Jump to navigation Jump to search

(2015-07: 以下の内容は古いです。現状では php5.5 以降の Zend OPcache、apcu、さらに php7、HHVM 等の動きがあります)

m1.small instance をしゃぶり尽くす

AWS って on demand 的な使い方をした場合の課金や、ストレージ課金は驚異的に安価だとおもいますが、常時稼働が必要な instance の CPU 課金は(デフレスパイラル下の日本に於いては)微妙に割高感があります。 その中で、2番目に低価格な m1.small でどこまで逝けるか を試してみるつもりです。

m1.small は Xen の CPU cap (割り当てCPU時間の制御) されており、Amazon に CPU を盗まれています。B)

# mpstat

00:00:00 AM  CPU   %user   %nice    %sys %iowait    %irq   %soft  %steal   %idle    intr/s
00:00:00 AM  all   34.95    0.00    4.85    0.00    0.00    0.00   60.19    0.00     96.12

CPU cap は、m1.large なら問題なさそうです(30%steal)。長期に運用する場合は m1.large で reserved instance にすれば価格は m1.small とほぼ同様なので、このパターンで使うのがベストだと思います。(*64 bit になります)

驚異的な安さの t1.micro は CPU cap が厳しくて、用途が限定されます。t1.micro の使い道は、64bit, EBS‐backed, PVGRUB な t1.micro AMI でデプロイメントして、問題なければ上のクラスのインスタンス・サイズにして実戦投入するといった用途にはすごく便利で、しかも超安価です。

Concepts Micro Instances

m1.small instance とは

スペック

1.7 GB of memory
1 EC2 Compute Unit (1 virtual core with 1 EC2 Compute Unit)
160 GB of instance storage
32-bit platform

The art of limitations

メインメモリはふんだんにあるので、メモリーの有効活用がキモになります。 Xen の CPU cap (割り当てCPU時間の制御) がされているので、重い処理をできるだけ少なくして、メモリーキャッシュを有効に使う設計をすべきです。

Bare metal サーバと異なり、「仮想サーバの設定」の範囲内ギリギリでつかえるか? 制限する事が重要なポイントになります。The art of limitations ですね ;)

ターゲット

Linux + Apache + MySQL + PHP といった文字通りの LAMP サーバ。

個人の Blog サイトで、それほどアクセスは無いが、たまにはスパイクがあるような感じ。

(WordPressMediaWiki を運用)

私は debian/ubuntu 派なので、EC2 で安定している ubuntu を使います。

MySQL

データベースが CPU 負荷のボトルネックになります。 m1.small で扱えきれる性能に制限します。つまり、この性能に合わせて設計して行きます。

実機で負荷を観察したところ、最大接続数が10程度に抑えておくのが良さそうです。

my.cnf

余裕を見て最大接続数を 16 とします。

max_connections        = 16

また、接続数が 8 程度までは常用として、thread の設定をします。

thread_cache_size      = 14
thread_concurrency     = 8

Query Cache を有効にします。

query_cache_size       = 64M
query_cache_limit      = 2M
query_cache_type       = 1

メモリーを有効に使用するように一時テーブルをメモリーに展開するように設定します。

tmpdir                 = /run/shm
tmp_table_size		= 256M
max_heap_table_size	= 256M

ただし、リプリケーション・スレーブの場合は、TMPDIR, tmpdir に tmpfs は使ってはいけません。(If the MySQL server is acting as a replication slave, you should not set --tmpdir to point to a directory on a memory-based filesystem or to a directory that is cleared when the server host restarts. )

いろいろ試して、mysqltuner.pl で設定の確認してみて下さい。

その他の my.cnf チューニング系リンク

LAMP システムを調整する: 第 3 回 MySQL サーバーを調整する
DSAS開発者の部屋:5分でできる、MySQLのメモリ関係のチューニング!
MySQLTunerを使ってMySQLを速くする

Run OPTIMIZE TABLE to defragment tables for better performance

mysqlcheck -u root -p --auto-repair --check --optimize --all-databases

Apache

データーベースに負けず劣らず apache も CPU を喰いますね。(軽量化を模索中...)

config

PHP をつかうので MPM prefork が前提になってしまいます。 という訳で、最近流行りの設定を仕込みます。

前項の制限から考えて 16 程度 prefork します。

StartServers 16
MinSpareServers 16
MaxSpareServers 16
MaxClients 16
ServerLimit 16

この程度でも十分さばけています ;) というか、以下で記述する、メモリーが十分にあるからこそ出来るキャッシュ作戦のおかげなのです :) ナイスな仕様ですね EC2!!!

もう一つの重要ポイントである KeepAlive 系は、コンテンツが WordPress や MediaWiki であるので以下の設定をします。

MaxKeepAliveRequests 20
KeepAliveTimeout 3

(phpmyadmin の 1ページ目用なら MaxKeepAliveRequests 40 てな感じで)

おまけ

ServerSignature Off
ServerTokens Prod
TraceEnable Off

パラノイア

<LocationMatch "/\..*">
    Order allow,deny
    Deny from all
</LocationMatch>

<DirectoryMatch "/CVS/|/RCS/">
    Order allow,deny
    Deny from all
</DirectoryMatch>

<DirectoryMatch "/\.svn/|/\.git/|/\.hg/|/\.bzr/|/\.cvs/">
    Order allow,deny
    Deny from all
</DirectoryMatch>

<FilesMatch "~$|#$|,v$|,t$">
    Order allow,deny
    Deny from all
</FilesMatch>

<FilesMatch "\.bak$|\.BAK$|\.orig$|\.ORIG$|\.org$|\.ORG$|\.rej$">
    Order allow,deny
    Deny from all
</FilesMatch>
DirectoryIndex は index.php のみにする
.htaccess は使わない (AllowOverride none)
Options FollowSymLinks しておく (処理が軽くなる)

YSlow / Page Speed

YSlowPage Speed で指摘される点を対処。A評価 になるようにがんばる。

ここでのポイントは、データ転送量を極力少なくして、かつ、極力クライアントにキャッシュしてもらうという事。(大規模事業者はbyte単位でこの手の最適化をやっていますね)

mod_expires

<IfModule mod_expires.c>
          ExpiresActive On
          ExpiresDefault "access plus 15 minutes"
          ExpiresByType text/css "access plus 1 months"
          ExpiresByType text/javascript "access plus 1 months"
          ExpiresByType application/x-javascript "access plus 1 months"
          ExpiresByType application/javascript "access plus 1 months"
          ExpiresByType image/jpeg "access plus 10 years"
          ExpiresByType image/png "access plus 10 years"
          ExpiresByType image/gif "access plus 10 years"
          ExpiresByType image/x-icon "access plus 10 years"
</IfModule>

mod_deflate

<IfModule mod_deflate.c>
          DeflateCompressionLevel 1
          AddOutputFilterByType DEFLATE text/html
          AddOutputFilterByType DEFLATE text/plain
          AddOutputFilterByType DEFLATE text/css
          AddOutputFilterByType DEFLATE text/javascript
          AddOutputFilterByType DEFLATE text/xml
          AddOutputFilterByType DEFLATE application/x-javascript
          AddOutputFilterByType DEFLATE application/javascript
          AddOutputFilterByType DEFLATE application/xml
          AddOutputFilterByType DEFLATE application/rdf+xml
</IfModule>

ここでのポイントは "DeflateCompressionLevel 1" とする事。mod_deflate の使命であるデータ転送量を抑えつつ、 CPU 使用率を極力下げる事が CPU cap 下の m1.small には大事です。

ETag を切る

FileETag none

mod_rpaf

後述するリバースプロキシを適用した場合のアクセスログ対策として、mod_rpaf を導入

<IfModule mod_rpaf.c>
          RPAFenable On
          RPAFsethostname On
          RPAFproxy_ips 127.0.0.1   
</IfModule>

Cache 三兄弟

memcached 導入

# apt-get install memcached
# apt-get install php5-memcache

/etc/memcached.conf , /etc/init.d/memcached , /usr/share/memcached/scripts/start-memcached

-m 64 -p 11211 -U 0 -u www-data -t 16 -l 127.0.0.1

パッケージの内容・更新頻度が気になるなら memcachedPHP memcache を手動管理する(こっちのほうがよさげ)

memcached

# apt-get install libevent-dev
(memcached compile & install)

php-memcache

# apt-get install php5-dev
# apt-get install php-pear
# pecl install memcache

更新

# pecl upgrade memcache

再インストール

PHP の更新にともなう再インストール

# pecl install -f memcache

WordPress の memcached 設定

Memcached Object Cache
object-cache.php

MediaWiki の memcached 設定

memcached - MediaWiki

リンク

MySQL HA/Scalability Guide :: 4 Using MySQL with memcached

メモ

PHP memcache ではなく PHP memcached なんてのあるのね。

WordPress の object-cache.php は PHP memcache を使っているし、 MediaWiki は memcached-client を使っている。

PHP memcached の方がパフォーマンスが良いらしいし、柔軟だとの事。(memcache 系はまだまだ発展中ですね)

PHP の session handler に "memcache" の 代わりに "memcached" にすると若干性能向上するらしい。

libmemcached , PHP memcached を導入

libinnodb
Igbinary
libMemcached

# pecl install memcached

メモ その2

Batcache なる WordPress Plugin を実験中

WordPress › Batcache « WordPress Plugins

varnish 導入

apache に仕事をさせないように、リバースプロキシで可能なかぎりメモリーにキャッシュします。

# apt-get install varnish

/etc/default/varnish , /etc/init.d/varnish

-p thread_pools=1 -p thread_pool_add_delay=10 -p cli_timeout=20 -u www-data -g www-data -w 16,800,300 -s malloc,64M

(キャッシュはメモリーに展開する: -s malloc,64M)

Varnish best practices « Kristian Lyngstol's Blog
Varnish Oscon 2009

パッケージの内容・更新頻度が気になるなら varnishd のリポジトリから導入する(こっちのほうがよさげ)

vcl

MediaWiki用のサンプルをベースにして作成した、現在使用中の vcl (様子見中...) (varnish 2.1版)

# set default backend if no server cluster specified
backend default {
  .host = "127.0.0.1"; 
  .port = "8080"; 
}

# access control list for "purge": open to only localhost and other local nodes
acl purge {
  "127.0.0.1";
}

sub vcl_recv {
# for mod_rpaf logging src IP address
  if (req.restarts == 0) {
    if (req.http.x-forwarded-for) {
      set req.http.X-Forwarded-For =
	req.http.X-Forwarded-For ", " client.ip;
    } else {
      set req.http.X-Forwarded-For = client.ip;
    }
  }

# removes all cookies named __utm? (utma, utmb...) - REMOVE! Google Analytics tracking Cookies
  if (req.http.Cookie) {
    set req.http.Cookie = regsuball(req.http.Cookie, "(^|; ) *__utm.=[^;]+;? *", "\1");
   
    if (req.http.Cookie == "") {
      remove req.http.Cookie;
    }
  }

# If they are going to the mobile site, force the iphone user agent
  if (req.http.host ~ "^m.egrep.jp") {
    set req.http.user-agent = "iPhone";
  }

# Check the regular site for a few things...
  if (req.url ~ "^\/blog\/") {
# Force the http.host to be the mobile version of the site to ensure the cache lookup hits the mobile version of the site
    if(req.http.user-agent ~ "(iPhone|iPod|Android|CUPCAKE|bada|blackberry\ 9800|blackberry9500|blackberry9520|blackberry9530|blackberry 9550|dream|incognito|s8000|webOS|webmate|Opera\ Mini)") {
      set req.http.host = "m.egrep.jp";
    }
# Drop any cookies sent to Wordpress.
    if (!(req.url ~ "wp-(login|admin)")) {
      unset req.http.cookie;
    }
  }

# for Trick of DirectoryIndex OLD Static contents
  if (req.request == "GET" && req.url ~ "^\/nxhack\/") {
    if (req.url ~ "/$") {
      set req.url = req.url "index.html";
      return (lookup);
    }
  }

# always cache these items:
  if (req.request == "GET" && req.url ~ "\.(js)") {
    return (lookup);
  }
    
# images
  if (req.request == "GET" && req.url ~ "\.(gif|jpg|jpeg|bmp|png|tiff|tif|ico|img|tga|wmf)$") {
    return (lookup);
  }

# various other content pages
  if (req.request == "GET" && req.url ~ "\.(css|html)$") {
    return (lookup);
  }

# multimedia 
  if (req.request == "GET" && req.url ~ "\.(svg|swf|ico|mp3|mp4|m4a|ogg|mov|avi|wmv)$") {
    return (lookup);
  }

# xml
  if (req.request == "GET" && req.url ~ "\.(xml)$") {
    return (lookup);
  }

# Serve objects up to 2 minutes past their expiry if the backend
# is slow to respond.
  set req.grace = 120s;

# This uses the ACL action called "purge". Basically if a request to
# PURGE the cache comes from anywhere other than localhost, ignore it.
  if (req.request == "PURGE") {
    if (!client.ip ~ purge) {
      error 405 "Not allowed.";
    }
    return (lookup);
  }

# Pass any requests that Varnish does not understand straight to the backend.
  if (req.request != "GET" &&
      req.request != "HEAD" &&
      req.request != "PUT" &&
      req.request != "POST" &&
      req.request != "TRACE" &&
      req.request != "OPTIONS" &&
      req.request != "DELETE") {
    /* Non-RFC2616 or CONNECT which is weird. */
    return (pipe);
  }

# Pass anything other than GET and HEAD directly.
  if (req.request != "GET" && req.request != "HEAD") {
    /* We only deal with GET and HEAD by default */
    return (pass);
  }

# Pass requests from logged-in users directly.
  if (req.http.Authorization || req.http.Cookie) {
    /* Not cacheable by default */
    return (pass);
  }

  /* Do not cache if request contains an Expect header */
  if (req.http.Expect) {
    return (pipe);
  }

# Pass any requests with the "If-None-Match" header directly.
  if (req.http.If-None-Match) {
    return (pass);
  }

# Force lookup if the request is a no-cache request from the client.
  if (req.http.Cache-Control ~ "no-cache") {
    purge_url(req.url);
  }
  
# normalize Accept-Encoding to reduce vary
  if (req.http.Accept-Encoding) {
    if (req.http.User-Agent ~ "MSIE 6") {
      unset req.http.Accept-Encoding;
    } elsif (req.http.Accept-Encoding ~ "gzip") {
      set req.http.Accept-Encoding = "gzip";
    } elsif (req.http.Accept-Encoding ~ "deflate") {
      set req.http.Accept-Encoding = "deflate";
    } else {
      unset req.http.Accept-Encoding;
    }
  }

  return (lookup);
}

sub vcl_pipe {
# Note that only the first request to the backend will have
# X-Forwarded-For set.  If you use X-Forwarded-For and want to
# have it set for all requests, make sure to have:
# set req.http.connection = "close";

# This is otherwise not necessary if you do not do any request rewriting.
 
  set req.http.connection = "close"; 

  return (pipe);
}

# Called if the cache has a copy of the page.
sub vcl_hit {
  if (req.request == "PURGE") {
    purge_url(req.url);
    error 200 "Purged";
  }
 
  if (!obj.cacheable) {
    return (pass);
  }

  return (deliver);
}

# Called if the cache does not have a copy of the page.
sub vcl_miss {
  if (req.request == "PURGE") {
    error 200 "Not in cache";
  }

  return (fetch);
}

# Called after a document has been successfully retrieved from the backend.
sub vcl_fetch {

# set minimum timeouts to auto-discard stored objects
#  set beresp.prefetch = -30s;
  set beresp.grace = 120s;

  if (beresp.ttl < 48h) {
    set beresp.ttl = 48h;
  }
 
# Drop any cookies Wordpress tries to send back to the client.
  if (req.url ~ "^\/blog\/") {
    if (!(req.url ~ "wp-(login|admin)")) {
      unset beresp.http.set-cookie;
    }
  }

# strip the cookie before the image is inserted into cache.
  if (req.url ~ "\.(png|gif|jpg|swf|css|js|ico|tiff|jpeg|bmp|tif)$") {
    unset beresp.http.set-cookie;
  }

  if (!beresp.cacheable) {
    return (pass);
  }

  if (beresp.http.Set-Cookie) {
    return (pass);
  }

  if (req.http.Authorization && !beresp.http.Cache-Control ~ "public") {
    return (pass);
  }  

  if (beresp.cacheable) {
    /* Set how long Varnish will keep it */
#    set beresp.ttl = 1w;

# for OLD Statick Contents
    if (req.url ~ "^\/nxhack\/") {
      /* for static text/html */
      if (req.url ~ "\.html$") {
	/* Set how long Varnish will keep it */
	set beresp.ttl = 30d;

	/* Set the clients TTL on this object */
	set beresp.http.cache-control = "max-age=2592000";
      }
    }

    /* marker for vcl_deliver to reset Age: */
    set beresp.http.magicmarker = "1";
  }

  return (deliver);
}

sub vcl_deliver {
  if (resp.http.magicmarker) {
    /* Remove the magic marker */
    unset resp.http.magicmarker;

    /* By definition we have a fresh object */
    set resp.http.Age = "0";
  }

# Disable BRAIN-DAMAGED Internet Explorer 8 MIME Sniffing
  set resp.http.X-Content-Type-Options = "nosniff";

#  unset resp.http.Age;
#  unset resp.http.Server;
  unset resp.http.Via;
  unset resp.http.X-Varnish;
  unset resp.http.X-Vary-Options;

  return (deliver);
}


(varnish 3.0 版への diff)

@@ -13,8 +13,7 @@
 # for mod_rpaf logging src IP address
   if (req.restarts == 0) {
     if (req.http.x-forwarded-for) {
-      set req.http.X-Forwarded-For =
-	req.http.X-Forwarded-For ", " client.ip;
+      set req.http.X-Forwarded-For = req.http.X-Forwarded-For + ", " + client.ip;
     } else {
       set req.http.X-Forwarded-For = client.ip;
     }
@@ -49,7 +48,7 @@
 # for Trick of DirectoryIndex OLD Static contents
   if (req.request == "GET" && req.url ~ "^\/nxhack\/") {
     if (req.url ~ "/$") {
-      set req.url = req.url "index.html";
+      set req.url = req.url + "index.html";
       return (lookup);
     }
   }
@@ -128,7 +127,7 @@
 
 # Force lookup if the request is a no-cache request from the client.
   if (req.http.Cache-Control ~ "no-cache") {
-    purge_url(req.url);
+    ban_url(req.url);
   }
   
 # normalize Accept-Encoding to reduce vary
@@ -163,11 +162,11 @@
 # Called if the cache has a copy of the page.
 sub vcl_hit {
   if (req.request == "PURGE") {
-    purge_url(req.url);
+    ban_url(req.url);
     error 200 "Purged";
   }
  
-  if (!obj.cacheable) {
+  if (!(obj.ttl > 0s)) {
     return (pass);
   }
 
@@ -206,19 +205,19 @@
     unset beresp.http.set-cookie;
   }
 
-  if (!beresp.cacheable) {
-    return (pass);
+  if (!(beresp.ttl > 0s)) {
+    return (hit_for_pass);
   }
 
   if (beresp.http.Set-Cookie) {
-    return (pass);
+    return (hit_for_pass);
   }
 
   if (req.http.Authorization && !beresp.http.Cache-Control ~ "public") {
-    return (pass);
+    return (hit_for_pass);
   }  
 
-  if (beresp.cacheable) {
+  if (beresp.ttl > 0s) {
     /* Set how long Varnish will keep it */
 #    set beresp.ttl = 1w;


(varnish 3.0 版から 4.0番への diff)

@@ -1,3 +1,6 @@
+###  -*- mode:c -*-
+
+vcl 4.0;
 # set default backend if no server cluster specified
 backend default {
   .host = "127.0.0.1";
@@ -10,8 +13,11 @@
   "127.0.0.1";
 }
 
+# vcl_recv is called whenever a request is received
 sub vcl_recv {
+# Remove the proxy header (see https://httpoxy.org/#mitigate-varnish)
   unset req.http.proxy;
+
 # for mod_rpaf logging src IP address
   if (req.restarts == 0) {
     if (req.http.x-forwarded-for) {
@@ -26,7 +32,7 @@
     set req.http.Cookie = regsuball(req.http.Cookie, "(^|; ) *__utm.=[^;]+;? *", "\1");
 
     if (req.http.Cookie == "") {
-      remove req.http.Cookie;
+      unset req.http.Cookie;
     }
   }
 
@@ -48,74 +54,70 @@
   }
 
 # for Trick of DirectoryIndex OLD Static contents
-  if (req.request == "GET" && req.url ~ "^\/nxhack\/") {
+  if (req.method == "GET" && req.url ~ "^\/nxhack\/") {
     if (req.url ~ "/$") {
       set req.url = req.url + "index.html";
-      return (lookup);
+      return (hash);
     }
   }
 
 # always cache these items:
-  if (req.request == "GET" && req.url ~ "\.(js)") {
-    return (lookup);
+  if (req.method == "GET" && req.url ~ "\.(js)") {
+    return (hash);
   }
 
 # images
-  if (req.request == "GET" && req.url ~ "\.(gif|jpg|jpeg|bmp|png|tiff|tif|ico|img|tga|wmf)$") {
-    return (lookup);
+  if (req.method == "GET" && req.url ~ "\.(gif|jpg|jpeg|bmp|png|tiff|tif|ico|img|tga|wmf)$") {
+    return (hash);
   }
 
 # various other content pages
-  if (req.request == "GET" && req.url ~ "\.(css|html)$") {
-    return (lookup);
+  if (req.method == "GET" && req.url ~ "\.(css|html)$") {
+    return (hash);
   }
 
 # multimedia
-  if (req.request == "GET" && req.url ~ "\.(svg|swf|ico|mp3|mp4|m4a|ogg|mov|avi|wmv)$") {
-    return (lookup);
+  if (req.method == "GET" && req.url ~ "\.(svg|swf|ico|mp3|mp4|m4a|ogg|mov|avi|wmv)$") {
+    return (hash);
   }
 
 # xml
-  if (req.request == "GET" && req.url ~ "\.(xml)$") {
-    return (lookup);
+  if (req.method == "GET" && req.url ~ "\.(xml)$") {
+    return (hash);
   }
 
 # Serve objects up to 2 minutes past their expiry if the backend
 # is slow to respond.
-  set req.grace = 120s;
+# set req.grace = 120s;
 
 # This uses the ACL action called "purge". Basically if a request to
 # PURGE the cache comes from anywhere other than localhost, ignore it.
-  if (req.request == "PURGE") {
+  if (req.method == "PURGE") {
     if (!client.ip ~ purge) {
-      error 405 "Not allowed.";
+      return (synth(405, "Not allowed."));
+    } else {
+      return (purge);
     }
-    return (lookup);
   }
 
 # Pass any requests that Varnish does not understand straight to the backend.
-  if (req.request != "GET" &&
-      req.request != "HEAD" &&
-      req.request != "PUT" &&
-      req.request != "POST" &&
-      req.request != "TRACE" &&
-      req.request != "OPTIONS" &&
-      req.request != "DELETE") {
-    /* Non-RFC2616 or CONNECT which is weird. */
+  if (req.method != "GET" && req.method != "HEAD" &&
+      req.method != "PUT" && req.method != "POST" &&
+      req.method != "TRACE" && req.method != "OPTIONS" &&
+      req.method != "DELETE") {
     return (pipe);
-  }
+  } /* Non-RFC2616 or CONNECT which is weird. */
 
 # Pass anything other than GET and HEAD directly.
-  if (req.request != "GET" && req.request != "HEAD") {
-    /* We only deal with GET and HEAD by default */
+  if (req.method != "GET" && req.method != "HEAD") {
     return (pass);
-  }
+  } /* We only deal with GET and HEAD by default */
 
 # Pass requests from logged-in users directly.
-  if (req.http.Authorization || req.http.Cookie) {
-    /* Not cacheable by default */
+# Only detect cookies with "session" and "Token" in file name, otherwise nothing get cached.
+  if (req.http.Authorization || req.http.Cookie ~ "session" || req.http.Cookie ~ "Token") {
     return (pass);
-  }
+  } /* Not cacheable by default */
 
   /* Do not cache if request contains an Expect header */
   if (req.http.Expect) {
@@ -129,7 +131,7 @@
 
 # Force lookup if the request is a no-cache request from the client.
   if (req.http.Cache-Control ~ "no-cache") {
-    ban_url(req.url);
+    ban(req.url);
   }
 
 # normalize Accept-Encoding to reduce vary
@@ -145,7 +147,7 @@
     }
   }
 
-  return (lookup);
+  return (hash);
 }
 
 sub vcl_pipe {
@@ -157,66 +159,67 @@
 # This is otherwise not necessary if you do not do any request rewriting.
 
   set req.http.connection = "close";
-
-  return (pipe);
 }
 
 # Called if the cache has a copy of the page.
 sub vcl_hit {
-  if (req.request == "PURGE") {
-    ban_url(req.url);
-    error 200 "Purged";
+  if (req.method == "PURGE") {
+    ban(req.url);
+    return (synth(200, "Purged"));
   }
 
-  if (!(obj.ttl > 0s)) {
+  if (!obj.ttl > 0s) {
     return (pass);
   }
-
-  return (deliver);
 }
 
 # Called if the cache does not have a copy of the page.
 sub vcl_miss {
-  if (req.request == "PURGE") {
-    error 200 "Not in cache";
+  if (req.method == "PURGE") {
+    return (synth(200, "Not in cache"));
   }
-
-  return (fetch);
 }
 
 # Called after a document has been successfully retrieved from the backend.
-sub vcl_fetch {
-
+sub vcl_backend_response {
 # set minimum timeouts to auto-discard stored objects
-#  set beresp.prefetch = -30s;
   set beresp.grace = 120s;
 
+
   if (beresp.ttl < 48h) {
     set beresp.ttl = 48h;
   }
 
 # Drop any cookies Wordpress tries to send back to the client.
-  if (req.url ~ "^\/blog\/") {
-    if (!(req.url ~ "wp-(login|admin)")) {
+  if (bereq.url ~ "^\/blog\/") {
+    if (!(bereq.url ~ "wp-(login|admin)")) {
       unset beresp.http.set-cookie;
     }
   }
 
 # strip the cookie before the image is inserted into cache.
-  if (req.url ~ "\.(png|gif|jpg|swf|css|js|ico|tiff|jpeg|bmp|tif)$") {
+  if (bereq.url ~ "\.(png|gif|jpg|swf|css|js|ico|tiff|jpeg|bmp|tif)$") {
     unset beresp.http.set-cookie;
   }
 
-  if (!(beresp.ttl > 0s)) {
-    return (hit_for_pass);
+  if (!beresp.ttl > 0s) {
+    set beresp.uncacheable = true;
+    return (deliver);
   }
 
   if (beresp.http.Set-Cookie) {
-    return (hit_for_pass);
+    set beresp.uncacheable = true;
+    return (deliver);
   }
 
-  if (req.http.Authorization && !beresp.http.Cache-Control ~ "public") {
-    return (hit_for_pass);
+#       if (beresp.http.Cache-Control ~ "(private|no-cache|no-store)") {
+#          set beresp.uncacheable = true;
+#          return (deliver);
+#        }
+
+  if (beresp.http.Authorization && !beresp.http.Cache-Control ~ "public") {
+    set beresp.uncacheable = true;
+    return (deliver);
   }
 
   if (beresp.ttl > 0s) {
@@ -224,14 +227,14 @@
 #    set beresp.ttl = 1w;
 
 # for OLD Statick Contents
-    if (req.url ~ "^\/nxhack\/") {
+    if (bereq.url ~ "^\/nxhack\/") {
       /* for static text/html */
-      if (req.url ~ "\.html$") {
-         /* Set how long Varnish will keep it */
-         set beresp.ttl = 30d;
+      if (bereq.url ~ "\.html$") {
+	/* Set how long Varnish will keep it */
+	set beresp.ttl = 30d;
 
-         /* Set the clients TTL on this object */
-         set beresp.http.cache-control = "max-age=2592000";
+	/* Set the clients TTL on this object */
+	set beresp.http.cache-control = "max-age=2592000";
       }
     }

vcl サンプルのリンク

Manual:Varnish caching
VarnishAndWordpress – Varnish
VCLExampleLongerCaching – Varnish
VCLExampleRemovingSomeCookies – Varnish
Making Posterous faster with Varnish - The Official Posterous Tech Blog

DirectoryIndex に関連する vcl テクニック

A Guide to Varnish VCL

WPtouch の扱い

Improving Wordpress Performance Supporting WPtouch Varnish | ephur.net

MediaWiki の設定

Configuring MediaWiki

遅い(重い) backend への対応

Child (9999) not responding to CLI, killing it.

てなログが出ている場合

-p cli_timeout=20

で様子を見る

varnish 運用メモ

強制的にキャッシュに食わす

wget -m --delete-after -nd -P /run/shm/ -q http://www.example.com/

よく使うコマンド

varnishadm -T 127.0.0.1:6082 'help'
varnishadm -T 127.0.0.1:6082 'param.show'
varnishadm -T 127.0.0.1:6082 'param.show -l'
varnishadm -T 127.0.0.1:6082 'purge.url .*'
varnishadm -T 127.0.0.1:6082 'purge.url /foo/bar'
varnishadm -T 127.0.0.1:6082 'purge.list'

ubuntu Lucid Lynx の場合以下が必要

-S /etc/varnish/secret

デバッグログ

varnishlog

PHP Cache 導入

PHP cache でよく使われている、APCeAccelerator を導入します。 APC の方が安定しているようです。また eAccelerator を圧縮無しモードで使用した場合 CPU 消費が少ないようです。

APC を導入する場合

パッケージを使う場合

# apt-get install php-apc

パッケージを使わない場合

# apt-get install php5-dev
# apt-get install php-pear
# apt-get install apache2-prefork-dev
# pecl install apc
# echo "extension=apc.so" >> /etc/php5/conf.d/apc.ini
# /etc/init.d/apache2 restart

apc.ini

; configuration for php APC module
extension=apc.so

[apc]
apc.shm_size=64M
apc.filters = wp-cache-config
apc.max_file_size=4M

更新

# pecl upgrade apc
# pecl upgrade -f apc-VERSION

再インストール

PHP の更新にともなう再インストール

# pecl install -f apc

eAccelerator メモ

eaccelerator.ini

extension=eaccelerator.so

[eaccelerator]
eaccelerator.shm_size="64"
eaccelerator.cache_dir="/tmp/eaccelerator"
eaccelerator.enable="1"
eaccelerator.optimizer="1"
eaccelerator.check_mtime="1"
eaccelerator.debug="0"
eaccelerator.filter=""
eaccelerator.shm_max="0"
eaccelerator.shm_ttl="0"
eaccelerator.shm_prune_period="0"
eaccelerator.shm_only="1"
eaccelerator.allowed_admin_path="/var/foo/bar"
eaccelerator.compress="0"
;;;;;; eaccelerator.filter="!wp-cache-config"
;eaccelerator.keys="shm_only"
;eaccelerator.sessions="shm_only"
;eaccelerator.content="shm_only"

Linux

(いや...まぁいっか...では最小限で...)

sysctl.conf

vm.swappiness=0
kernel.panic_on_oops=1
kernel.panic=1
kernel.shmmax=268435456
Performance – Varnish

for DB EBS Volume

echo 'noop' > /sys/block/sdX/queue/scheduler

'deadline' か 'noop' が良い。(微妙にしか変わらないので、どちらでも...)

雑多なメモ

PHP

php.ini

expose_php = Off
max_execution_time = 6000000
memory_limit = 128M
display_errors = Off
post_max_size = 128M
upload_max_filesize = 128M
date.timezone = Asia/Tokyo
session.save_handler = memcache
session.save_path = tcp://127.0.0.1:11211
PHP & Performance

output_buffering = 8192
register_globals = Off
magic_quotes_gpc = Off
expose_php = Off
register_argc_argv = Off
always_populate_raw_post_data = Off
session.use_trans_sid = 0
session.auto_start = 0
session.gc_divisor = 10000

WordPress

wp-config.php

define('WP_MEMORY_LIMIT', '128M');

翻訳ファイルのキャッシュ

(追記) WordPress の勉強会に出て教えてもらった手法。

001 Prime Strategy Translate Accelerator

私はこのプラグインで「サイト表示の翻訳」を停止にしています。

MediaWiki

LocalSettings.php

ini_set( 'memory_limit', '128M' );