work.log

元エンジニアの備忘録的ブログ

HAProxyとNginxで作るコンテンツキャッシュシステム (後編)

投稿:2014-05-09 20:42  更新:

オープンソースの HAProxy とNginx を組み合わせてコンテンツキャッシュシステムを構築するメモの続きです。

既存の環境にも後付けできるようなコンテンツキャッシュシステムを作るという事で、前回は HAProxy について書きましたが今回は Nginx の部分について。

Nginx でリバースプロキシとキャッシュを構成して、HAProxy と繋げるまでやってみたいと思います。

Nginx の構成例

Nginx は version 1.4.7 を使ってリバースプロキシ + キャッシュを構成してみます。

下記は FreeBSD の Ports からインストールしたものですが、こんな感じのオプションを指定してインストールしてます。cache purge モジュールはぶっちゃけお好みで構いません。

# nginx -V
nginx version: nginx/1.4.7
TLS SNI support enabled
configure arguments: --prefix=/usr/local/etc/nginx --with-cc-opt='-I /usr/local/include' --with-ld-opt='-L /usr/local/lib' --conf-path=/usr/local/etc/nginx/nginx.conf --sbin-path=/usr/local/sbin/nginx --pid-path=/var/run/nginx.pid --error-log-path=/var/log/nginx-error.log --user=nginx --group=nginx --http-client-body-temp-path=/var/tmp/nginx/client_body_temp --http-fastcgi-temp-path=/var/tmp/nginx/fastcgi_temp --http-proxy-temp-path=/var/tmp/nginx/proxy_temp --http-scgi-temp-path=/var/tmp/nginx/scgi_temp --http-uwsgi-temp-path=/var/tmp/nginx/uwsgi_temp --http-log-path=/var/log/nginx-access.log --add-module=/usr/ports/www/nginx/work/ngx_cache_purge-2.1 --with-http_geoip_module --with-http_gzip_static_module --with-http_stub_status_module --with-pcre --with-http_ssl_module

Nginx はインストール後に下記記事のようにディレクトリを構成しています。

設定ファイルは見通しを重視して、最小限のものを nginx.conf に記述、その他は conf.d/ 配下に置いた各種ファイルをインクルードするように構成してみます。

user	nginx nginx;
pid	/var/run/nginx.pid;

worker_processes	2;
worker_rlimit_nofile	4096;

events {
	worker_connections	1024;
}

http {

	include  /usr/local/etc/nginx/mime.types;

	default_type  text/plain;

	geo $noip {
		default         0;
		127.0.0.1       1;
	}

	gzip              on;
	gzip_http_version 1.0;
	gzip_types        text/plain
			  text/xml
			  text/css
			  text/javascript
			  application/xml
			  application/xhtml+xml
			  application/rss+xml
			  application/atom_xml
			  application/javascript
			  application/x-javascript
			  application/x-httpd-php;
	gzip_disable      "MSIE [1-6]\.";
	gzip_disable      "Mozilla/4";
	gzip_comp_level   1;
	gzip_buffers      4 8k;
	gzip_min_length   1100;

	log_format lb     '$remote_addr - $remote_user [$time_local] "$request" '
			  '$status $body_bytes_sent "$http_referer" '
			  '"$http_user_agent" "$http_x_forwarded_for"';

	sendfile          on;
	tcp_nopush        on;
	server_tokens     off;
	keepalive_requests 100;
	keepalive_timeout 0;

	proxy_connect_timeout  60;
	proxy_send_timeout     60;
	proxy_read_timeout     60;
	proxy_cache_path /home/www/cache/cache1 levels=1:2 keys_zone=cache1:512k inactive=1h max_size=390m;
	proxy_cache_path /home/www/cache/cache2 levels=1:2 keys_zone=cache2:512k inactive=1h max_size=390m;
	proxy_temp_path  /home/www/temp;

server {

	listen  *:80 default_server;

	root /home/nginx/htdocs;

	access_log  /var/log/nginx-access.log combined;
	error_log   /var/log/nginx-error.log warn;

	location / {
		if ($noip) { access_log  off; }
		if ($uri ~* "^/check.html$") { access_log  off; }
			index  index.html;
        }

}

	include  /usr/local/etc/nginx/conf.d/virtual.conf;

}

nginx.conf 中に記述した conf.d/virtual.conf の中身はこんな感じ。

server {

	listen       80;
	server_name  worklog.be;

	access_log  /var/log/nginx-access.log lb;
	error_log   /var/log/nginx-error.log warn;

	proxy_set_header    X-Real-IP       $http_x_forwarded_for;
	proxy_set_header    Host            $http_host;
	proxy_redirect      off;
	proxy_max_temp_file_size    0;

	set $do_not_cache 0;
	set $proxy_cache_key "$scheme://$host$request_uri";

	location / {
		proxy_cache cache1;
		include /usr/local/etc/nginx/conf.d/wordpress.conf;
		proxy_pass http://192.168.0.101:80;
	}

}

続いて、virtual.conf 中に記述した conf.d/wordpress.conf の中身はこんな感じ。

ここはコピペするだけなので以降は基本触らない。かなり大味だがキャッシュに関する制御とモバイルデバイス用のキャッシュキーを設定してある。

モバイルデバイスはもうちょっと厳密にやった方がいいけどとりあえずはこんな感じで。

proxy_no_cache      $do_not_cache;
proxy_cache_bypass  $do_not_cache;
proxy_cache_key     $proxy_cache_key;
proxy_cache_valid   200 3d;
proxy_cache_valid   any 1s;

if ($request_method != GET) {
	set $do_not_cache 1;
}

if ($uri ~* "(feed|xmlrpc.php)$") {
	set $do_not_cache 1;
}

if ($http_user_agent ~* ^.*(Android|iPhone|iPod).*) {
	set $proxy_cache_key "mobile::$proxy_cache_key";
}

その他、必要なものは同じ要領で記述していけばソコソコ見やすいコンフィグになると思います。

後は、nginx -t で問題がなければ daemon を起動して Nginx 側は終わりです。

HAProxy と Nginx の連携

最後は HAProxy とNginx の連携です。

既にこの状態でも連携しているといえばしていますが、ヘルスチェック用の html を Nginx に置いていないのと HAProxy で必要な vip を listen させてないのでそれぞれやっていきます。

http --> [haproxy *1] -- req --> [nginx *2] -- req --> [apache]

 * [server] <= 物理サーバの凡例
 * ip は下記を割り当て
   haproxy vip       = 192.168.0.1, 192.168.0.2
   proxy1 (nginx) ip = 192.168.0.51
   proxy2 (nginx) ip = 192.168.0.52
   www1 (apache) ip  = 192.168.0.101
   www2 (apache) ip  = 192.168.0.102

キャッシュ対象はこのような流れにしたいので、virtual.conf に記述したプロキシ先の ip をまずは HAProxy に付与してあげます。(この場合は 192.168.0.101)

次は下記のように Nginx の DocumentRoot に html を置いてあげます。(構文は気にしなくて OK)

echo '<html>OK</html>' > /home/nginx/htdocs/check.html

これで HAProxy のヘルスチェックが up するのでバランシングされるようになります。

最後は worklog.be の zone を 192.168.0.1 に向けるとキャッシュ対象のトラフィックは上記のような経路で流れるようになり連携が完了です。

後は、Nginx 終端の Apache で log を tail しながら上手くコンテンツが分かれてリクエストされているかを確認します。Apache のアクセスログには HAProxy, Nginx の ip が記述されると思うので、必要であれば mod_rpaf 等で X-Forwarded-For ヘッダから変換してあげると良いです。

以上、HAProxy と Nginx を連携させたコンテンツキャッシュシステムはこれで一旦完成です。

Nginx 側はドメイン名単位で都度設定追加が必要な所ですが、HAProxy は収容ホスト (www1 とか) を予め羅列させて書いて置けば以降は vip の up/down だけで割りと汎用的には使えると思います。(一応そんな感じの使い方を想定しています。)

HAProxy は作成する TCP ソケットの数がどうしても多くなるのでこれで reload 等をしなくても良いはず!

とりあえず、書けるのはここまでなのですがこれから色々とテストをしてみようと思っているのでまた続きがあれば書きたいと思います。