ロードバランサとしての pound と nginx
自宅で運営してるサイトの負荷が、結構高い。
で、試しに pound を導入したんだけど、アクセス数が上がると、どうにも安定しない。
結局 nginx でのロードバランスに落ち着いたんだけれど、そこまでの経緯を書きとめる。
いろいろチューニングして行く上で、pixiv の中の人の資料が非常に、非常に、参考になった。ありがとうありがとう。
pixivのインフラを支える技術 techsemi20090925_03_pixiv_kamipo.pdf
Pound でアクセスが増えると Too many open files エラー
こいつがめっちゃ頻発します。解決策としては、起動スクリプトなんかに
ulimit -n 50000
って書いてやればいい。という検索結果が山ほど出るんですが、それでもエラーが止まらない。
試しに、File Discriptors のグラフを取ってみました。
予想では、徐々に数値が上がっていって、制限値に到達したところでエラーが出始めると思ってたんです。
でも、全然そんな感じじゃない。
あるタイミングから、ばいーんと跳ねる。もちろん、ulimit -n 50000 してるので、許容量内です。
- 実サーバがアクセス過多で死ぬ?
- pound 側で何らかの問題が発生
- キューが詰まり始める?
- File Discriptors ばいーん現象
その結果、Too many open files エラーが発生しているっぽい感じ。
なので、File Discriptors の制限値が原因ではないようで。。
エラーとしては、こんなのが大量に出てます。
error copy chunk cont: Connection timed out copy_chunks flush error: Connection timed out
ソースを見てみても、原因は分からず。
一度こうなっちゃうと、restart しないと復帰してくれないので、大変面倒でした。
Pound では reset by peer エラーがたくさん記録される
reset by peer 系のエラーは、クライアントがページのロードが完了する前に
別のページへ移動しようとした場合などに起こるみたいです。
なので、記録しないようにしたかったのです。
しかし、LogLevel 0 とかにしても、こいつは記録されるようで。。
ログファイルの容量も大変なことになるし。
仕方ないので、ソースを少し書き換えて対応しましたが、
微妙に応答速度が遅くなってしまいました('A`)
svc.c の logmsg 関数に追記した内容。
va_end(ap); /* ここから追加 */ if ( strstr( buf, "reset by peer" ) != NULL ) { return; } /* ここまで追加 */ if(log_facility == -1) {
機能面の不足
pound にはないけど nginx にはある機能として、次のものがあります。
gzip で圧縮して送信
やりとりする情報を、gzip で圧縮してからクライアントに返す機能がついている。
また、gzip に対応していないクライアントにはそのまま返す、といった機能も備わっている。
gzip での送信を ON にした結果、通信量がかなり減った。
LB鯖なので、inbound に対して outbound がかなり減ることになる。
ファイルのキャッシュ
nginx では、拡張子などを指定して、ファイルをキャッシュする機能がある。
この機能を用いると、バックエンドのサーバとの通信量を減らすことができる。
これも実施した結果、通信量がガッツリ減った。
LB鯖は、キャッシュされている分、inbound が減っている。
バックエンド鯖は、通信量が減ることになる。イイネ!
結論
結論としては、やはり、nginx よいねーという感じ。
pound も悪くはないんだけど、使いどころがちょっと違ったのかなぁ。
開発も盛んな nginx を選んだ方が、無難なのかもしれない。
おまけ1 poundのインストール手順
pound のインストール
yum -y install openssl-devel cd /usr/local/src wget http://www.apsis.ch/pound/Pound-2.6.tgz tar zxvf Pound-2.6.tgz cd Pound-2.6 ./configure make make install vi /usr/local/etc/pound.cfg
LogLevel 3 # 必要に応じて変更、0だとログを記録しない。 LogFacility local1 ListenHTTP Address *.*.*.* # ロードバランサの待ち受けIP Port 80 # ロードバランサの待ち受けポート Service # server1 BackEnd # BackEnd 〜 End 間に、実サーバを記述。 #HeadRequire "Host: .*www.server1.com.*" # ドメイン名で振り分けるときは、このように記述する。 #HeadDeny "Host: .*www.server0.com.*" Address *.*.*.* Port 80 Priority 1 # 優先度を1〜9で指定。 End # server2 BackEnd Address *.*.*.* Port 80 Priority 1 End End End
vi /etc/rc.d/init.d/pound # 起動スクリプトの作成
適当に拾ってきたもの。
#!/bin/sh # # pound # # chkconfig: 345 85 15 # description: reverse-proxy and load-balancer # # Source function library . /etc/rc.d/init.d/functions # Get network config . /etc/sysconfig/network # Pound Directory POUND="/usr/local/sbin/pound" CFG="/usr/local/etc/pound.cfg" # See how we were called. case "$1" in start) # Check if the normal service is already running? if [ ! -f /var/lock/subsys/pound ]; then echo "Starting pound:" $POUND -f $CFG RETVAL=$? [ $RETVAL -eq 0 ] && touch /var/lock/subsys/pound echo ${base} else #msg_Already_Running pound echo "pound already started." # exit 1 fi ;; stop) # Stop daemons. if [ -f /var/lock/subsys/pound ]; then #msg_stopping pound echo "Stopping pound:" killall $POUND rm -f /var/lock/subsys/pound > /dev/null 2>&1 echo else echo "pound is not running." exit 1 fi ;; restart|reload) $0 stop $0 start ;; configtest) $POUND -c -v -f $CFG ;; *) echo "usage: pound {start|stop|configtest|restart}" exit 1 ;; esac exit $RETVA
chmod +x /etc/rc.d/init.d/pound
ログ出力設定
rsyslog ではなく syslog を使っている場合は、rsyslog を syslog に読み替えて設定する。
vi /etc/rsyslog.conf
# *.info;mail.none;authpriv.none;cron.none; /var/log/messages *.info;mail.none;authpriv.none;cron.none;local1.none /var/log/messages # 変更 # 以下の行も追加 local1.* /var/log/pound
service rsyslog restart
pound の起動
service httpd stop # Apache が起動しているなら、停止しておく。 service pound start # Pound の起動 chkconfig pound on # Pound の自動起動設定
Web サーバ (分散先サーバ) のアクセスログ設定
vi /etc/httpd/conf/httpd.conf
LogFormat "%{X-Forwarded-For}i %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined # 変更
service httpd restart
おまけ2 nginx のインストール手順
nginx インストール
yum -y install pcre-devel cd /usr/local/src wget http://nginx.org/download/nginx-1.2.4.tar.gz tar zxvf nginx-1.2.4.tar.gz cd nginx-1.2.4 ./configure make make install
nginx 設定
mkdir /var/log/nginx vi /usr/local/nginx/conf/nginx.conf
worker_processes 4; pid /var/run/nginx.pid; events { worker_connections 1024; } http { proxy_cache_path /var/cache/nginx/static_file_cache levels=1:2 keys_zone=cache_static_file:128m inactive=1d(←1日アクセスがなかったら削除) max_size=512m; proxy_temp_path /var/cache/nginx/temp; sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 65; types_hash_max_size 2048; include /usr/local/nginx/conf/mime.types; default_type application/octet-stream; access_log /var/log/nginx/access.log; error_log /var/log/nginx/error.log; gzip on; gzip_disable "msie6"; include /etc/nginx/conf.d/*.conf; server { listen *.*.*.*<受付IP>:80; server_name www.hoge.com; location / { proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; set $do_not_cache 0; if ($request_method != GET) { set $do_not_cache 1; } if ($uri !~* ".(jpg|png|gif|jpeg|css|js|swf|pdf|html|htm)$") { set $do_not_cache 1; } proxy_no_cache $do_not_cache; proxy_cache_bypass $do_not_cache; proxy_cache cache_static_file; proxy_cache_key $scheme$host$uri$is_args$args; proxy_cache_valid 200 1h; proxy_cache_valid any 1m; proxy_pass http://backend; } } upstream backend { # server1 server *.*.*.* weight=1; # server2 server *.*.*.* weight=2; } }
起動スクリプトの作成
vi /etc/init.d/nginx
適当に拾ってきたもの。
#!/bin/sh # # nginx - this script starts and stops the nginx daemon # # chkconfig: - 85 15 # description: Nginx is an HTTP(S) server, HTTP(S) reverse \ # proxy and IMAP/POP3 proxy server # processname: nginx # config: /usr/local/nginx/conf/nginx.conf # config: /etc/sysconfig/nginx # pidfile: /var/run/nginx.pid # Source function library. . /etc/rc.d/init.d/functions # Source networking configuration. . /etc/sysconfig/network # Check that networking is up. [ "$NETWORKING" = "no" ] && exit 0 nginx="/usr/local/nginx/sbin/nginx" prog=$(basename $nginx) NGINX_CONF_FILE="/usr/local/nginx/conf/nginx.conf" [ -f /etc/sysconfig/nginx ] && . /etc/sysconfig/nginx lockfile=/var/lock/subsys/nginx make_dirs() { # make required directories user=`$nginx -V 2>&1 | grep "configure arguments:" | sed 's/[^*]*--user=\([^ ]*\).*/\1/g' -` if [ -z "`grep $user /etc/passwd`" ]; then useradd -M -s /bin/nologin $user fi options=`$nginx -V 2>&1 | grep 'configure arguments:'` for opt in $options; do if [ `echo $opt | grep '.*-temp-path'` ]; then value=`echo $opt | cut -d "=" -f 2` if [ ! -d "$value" ]; then # echo "creating" $value mkdir -p $value && chown -R $user $value fi fi done } start() { [ -x $nginx ] || exit 5 [ -f $NGINX_CONF_FILE ] || exit 6 make_dirs echo -n $"Starting $prog: " ulimit -n 50000 # ← 念のため。 daemon $nginx -c $NGINX_CONF_FILE retval=$? echo [ $retval -eq 0 ] && touch $lockfile return $retval } stop() { echo -n $"Stopping $prog: " killproc $prog -QUIT retval=$? echo [ $retval -eq 0 ] && rm -f $lockfile return $retval } restart() { configtest || return $? stop sleep 1 start } reload() { configtest || return $? echo -n $"Reloading $prog: " killproc $nginx -HUP RETVAL=$? echo } force_reload() { restart } configtest() { $nginx -t -c $NGINX_CONF_FILE } rh_status() { status $prog } rh_status_q() { rh_status >/dev/null 2>&1 } case "$1" in start) rh_status_q && exit 0 $1 ;; stop) rh_status_q || exit 0 $1 ;; restart|configtest) $1 ;; reload) rh_status_q || exit 7 $1 ;; force-reload) force_reload ;; status) rh_status ;; condrestart|try-restart) rh_status_q || exit 0 ;; *) echo $"Usage: $0 {start|stop|status|restart|condrestart|try-restart|reload|force-reload|configtest}" exit 2 esac
nginx の起動
service httpd stop # Apache が起動しているなら、停止しておく。 service nginx start # nginx の起動 chkconfig nginx on # nginx の自動起動設定