月別アーカイブ: 5月 2013

nginxでクライアントの接続調整をする

 リスティング広告を出していたりすると、出稿時に大量のアクセス(確認クローラ)が来てサイトの負荷が急上昇することがあるよね。

 さくらVPSお名前.com VPS(KVM)の2GBプランくらいでWordpress位の重さのページを素のまま表示させると、毎秒20アクセスとかで一杯一杯になる。

 そんな時は、nginxのリクエスト制御を使うと裏側の負荷上昇を穏やかにできる。

map $http_user_agent $ualimited{
    default ”;
    ~*googlebot 1;
    ~*msnbot 2;
    ~*bingbot 2;
    ~*slurp 3;
    …
}
limit_req_zone $ualimited zone=uazone:10m rate=10r/s;
server {
    …
    location / {
        …
        limit_req zone=uazone burst=200;
    }
}

 limit_req_zoneで、$ualmitedをキーにして、uazoneゾーンを毎秒10リクエストに制限する定義を作っている。
 で、ロケーション / に、limit_reqで定義したuazone制限を適用している。
※burstは、待ち行列の設定。 これが設定されていないと、リミットに達した時点で503エラーとなる。 burstが設定されていると、その値までのリクエストを待ち行列に突っ込んで、逐次処理することができる(burstを超えれば503になる) クローラが来た時にエラーを表示させると評価に問題が出るので、エラーではなく処理を遅延させて、正しく応答させる。
 limit_req_zoneの第一引数の値ごとにカウントするので、通常は$binary_remote_addrとかにして、リモートIPごとに制限するわけだけど、クローラは複数IPでガンガン来るので、ユーザエージェントで判別したいので、mapを使っている。
 if文で制限を直接設定できなかったので、UserAgent文字列から数値型にマッピングして、定義されているUAの場合に数値を与えて、それ以外の時は空にしてある。

 Wordpressであれば、/wp-content以下のファイルはスタティックで処理負荷が軽いので

    location / {
        …
        limit_req zone=uazone burst=200;
    }
    location /wp-content {
        …
    }

のように、wp-contentのロケーションを切れば、wp-content以下には要求制限がかからなくなる。

 前回のプロクシキャッシュを有効化して、

proxy_cache_path /dev/shm/c levels=1:2 keys_zone=cachezone:512m;
server{
    location / {
        …
        limit_req zone=uazone burst=200;
    }
    location /wp-content {
        …
        proxy_cache cachezone;
    }
}

のようにすれば、wp-content以下のスタティックファイルだけキャッシュすることができる。

(116)


カテゴリー: サーバ設定 | コメントをどうぞ

nginxのプロクシの裏側キャッシュを制御する

 nginxと裏のサーバ間のキャッシュを設定する。
 nginxのproxyでキャッシュする場合は、proxy_cacheを設定する。

proxy_cache_path /dev/shm/c levels=1:2 keys_zone=cachezone:512m;
server {
    location / {
        proxy_cache cachezone;
        proxy_pass http://appservers;
    }
}

 proxy_cache_pathでキャッシュの置き場所、置き方等を定義して、proxy_cacheでキャッシュ領域を割り当てる。
 で、この場合、プロクシを通過したデータを512Mまでキャッシュする。

 nginxでは細かい設定ができるのがイイね。

proxy_cache_path /dev/shm/c levels=1:2 keys_zone=cachezone:512m;
server {
    location / {
        set $dontcache 1;
        if ($request_filename ~* ".*\.jpg$"){
            set $dontcache 0;
        }
        if ($request_filename ~* ".*\.png$"){
            set $dontcache 0;
        }
        proxy_cache_bypass $dontcache;
        proxy_cache cachezone;
        proxy_pass http://appservers;
    }
}

 こうすると、要求されたファイル名がjpg, pngで終わるときしかキャッシュしない(proxy_cache_bypassに与えた値が0以外であるとき、キャッシュ機構をバイパスする)
 if文なので、$http_user_agent を使えばBOTと通常クライアントで異なる制御をしたりできる。
 また、特定のクッキーを持っている時だけバイパスするということも可能なので、開発ユーザにクッキーを食べさせておいて、該当クッキーをチェックするようにすると便利。

if ($cookie_author ~ “true”){
    set $dontcache 1;
}

みたいなこともできるし、そもそも、$cookie_author を 1/0 で制御するなら

proxy_cache_bypass $dontcache $cookie_author;

でも大丈夫。

(35)


カテゴリー: サーバ設定 | コメントをどうぞ

nginxで、入り口でまとめて認証をかける

 リリース前のサイトを本番環境でテストするとき、認証をかけたりするよね。
 DNSラウンドロビンとかLVSでバランシングしているときは、各ホストに.htaccessと.htpasswd書いたりするわけだけど、nginxでバランシングしている場合は、nginxの設定ファイルにまとめてかける。

server {
listen 80;
server_name hoge.tld;

location / {
auth_basic “auth”;
auth_basic_user_file “/usr/share/nginx/.htpasswd”;
proxy …
}
}

みたいな感じ。

nginxで認証を要求せず、裏のサーバが認証要求した場合には裏の認証要求が通る。
両方で認証を要求すると同じユーザ名:パスワードのセットで認証出来れば通るけど、それ以外の時は認証不能になる。

(45)


カテゴリー: サーバ設定 | コメントをどうぞ

NGINXをProxy利用 = UpstreamでKeepaliveを使う

 バランシングのテストにお名前.com VPS(KVM)を3台契約して、1台をバランサ、2台をアプリケーションサーバ(Apace)として構築して試験中。

 デフォルト設定でNGINXを通すと、NGINX-Apache間がHTTP 1.0 Closeコネクションになるため、abとかで叩くとApacheのスレッドが大量に立ち上がる。 この状態だと、リクエストのたびに新しい通信が立ち上がってくるし、Apacheのプロセスが切り替わりまくるからパフォーマンス的に美味しくない。
 調べてみると、最近のNGINXではupstream.keepaliveのディレクティブを使うことで、裏の通信をKeepAlive接続にできるようになるので、早速導入して評価。

と、設定前に負荷試験。

abで同時接続100、10000回のGETを実行(400KBのHTML、GZIPで60KBになるファイル)
NGINXでGZIP/SSL処理してAPACHEにつなぐと、200requests/sec程度のスループットだった。

#nginx.conf

upstream appservers {
server 192.168.10.10:80;
server 192.168.10.11:80;
keepalive 1000;
}

server {
listen 443;
server_name _;

location / {
proxy_http_version 1.1;
proxy_set_header Connection “”;
proxy_pass http://appservers;
}
}

 upstream.keepaliveをセットするのと、proxy_http_versionを1.1に(KeepAlive実装)、proxy_set_headerでConnectionを空にする(デフォルトでは、Connection: closeを送信してしまうため)

 Apache側は

KeepAlive On
MaxKeepAliveRequests 1000
KeepAliveTimeout 15

としてみた。

 先と同じようにabを実行してみた結果、210requests/sec程度になった。 もともと、ネットワーク転送量がワイヤースピード近くに達していたため、速度的にはそれほど改善していない。
 しかし、サーバのリソース的には相当改善が見られた。 CPU使用率のSys値(ユーザプログラム以外の部分で消費される無駄)が低下した。
 重いアプリケーションを動作させている場合には、オーバーヘッドが低下する分だけ応答速度の向上が期待できそうだ。

(390)


カテゴリー: サーバ設定 | コメントをどうぞ

NGINXを使う(Proxy利用)

 Webサーバとしては使い慣れていることもあってApacheをずっと使い続けてきたが、細かい設定がしにくいという点があり、今回NGINXを使用することにした。
 ただし、今回はApacheを代替するわけではなく、Proxyとして利用することにした。
 バランシングのテストにはお名前.com VPS(KVM)を3台契約して実装してみた。

 まずは、基本のProxy構成。
 と言っても、何も難しいことはない。

server {
listen 80 default_server;
server_name _;

location / {
proxy_pass http://localhost:10080;
}
}

 これだと、NGINXがポート80で待ち受けて、すべてのリクエストをlocalhostポート10080に送る(Apache等をポート10080で待たせておく)
このままだと何も効果がないから、ここから色々追加していく。

location /php/ {
proxy_pass http://localhost:10080;
}
location /ruby/ {
proxy_pass http://localhost:20080;
}

 こうなると、/phpに来たリクエストは10080に、/rubyに来たリクエストは20080に行くので、apache + passengerを一つの入口で受けられるようになる。 localhostの部分を変えて、192.168.10.10とかにすれば、別サーバに流れていくから、一つの入口からアプリ単位で内部では別サーバで動かすことができる。

 更に、ロードバランシングも簡単に作れる。

upstream appservers {
server 192.168.10.10:80;
server 192.168.10.11:80;
}

server {
listen 80 default_server;
server_name _;

location / {
proxy_pass http://appservers;
}
}

 これで、来たリクエストは192.168.10.10と11に分配される。

 upstream.serverには簡単に重み付けが出来るので、

server 192.168.10.10:80 weight=5;
server 192.168.10.11:80 weight=10;

みたいにすることで、アプリケーションサーバの重み付けができる。

 さらに、スタンバイ構成も簡単で

server 192.168.10.10:80;
server 192.168.10.11:80;
server 192.168.10.12:80 backup;

とすると、10,11が応答しない時だけ12に振り分けることもできる(backupと書かれたサーバが正常時待機系となる) 10,11のいずれかが復帰すればbackupは待機状態に戻る。
VerisgnのSSL証明書はバランシングすると台数分の取得を要求されるが、ホットスタンバイ・コールドスタンバイであれば、同時に動作する台数分で良いのこんなかんじで設定できるのは便利。

 HTTPのgzip圧縮をNGINX側に任せられるので、アプリケーションサーバを本来の処理に専念させることもできる。

server {
listen 80 default_server;
server_name _;

gzip on;

location / {
proxy_pass http://appservers;
}
}

 また、SSL処理もNGINX側で行う事になる。

server {
listen 443;
server_name _;

ssl on;
ssl_certificate /etc/pki/tls/certs/ssl.crt;
ssl_certificate_key /etc/pki/tls/private/server.key;

location / {
proxy_pass http://appservers;
}
}

 NGINXでは帯域制限が容易にできるので、

location / {
proxy_pass http://appservers;
}

location /file/ {
limit_rate 2m;
proxy_pass http://appservers;
}

こんなふうにすると、file配下のダウンロードを2Mに制限できるので、ファイルダウンロードのせいでアプリが遅くなるみたいなことの解消が期待できる。

 また、制御構造を使って、

location / {
proxy_pass http://appservers;
if ($http_user_agent ~* “msnbot”){
limit_rate 1m;
}
}

みたいなこともできるので、BOT様の速度を制限して通常接続の帯域を確保できる。

 色々、更に便利な機能があるから、しばらく検証しよう。

(196)


カテゴリー: サーバ設定 | コメントをどうぞ

安物のRAID装置は禍の元

 サーバの記憶装置では通常RAID構成がされていますよね。

 以前の職場で、なぜかRocketRAIDアダプタが乗っているサーバがあって、RAID5構成になっていました。 予算をケチって安く売っているカードを買って自前で組んだのでしょう。

 そのサーバであるときファイルが読めなくなりました。 アレイマネージャを確認すると何もエラー無し。
論理エラーかとも思ったのですが、システムが異常停止したわけでもない状態のサーバ機で論理エラーが発生することはあまりありません(主記憶やバスはECCで保護されているので)
OSのシステムログを確認するとデバイスドライバの応答停止エラーが発生していました。
しかし、他のファイルのアクセスは全く問題なく、システムの再起動を実行してもそのファイルだけアクセス不能。
システムを落として、別マシンで個別ドライブをフルリードスキャンしてみた結果、1台のドライブで読み込み不能セクタが発生していました(アクセスすると数十秒応答停止して、SMARTを確認すると再配置待ちセクタなどの値が変化する状態)
どうやら、このRAIDアダプタはドライブがデバイスタイムアウトした場合の扱いがしっかり実装されておらず、ドライブが応答しないときにアダプタもタイムアウトしてしまうお粗末な物だったようです。
結局、該当のドライブを外した状態で電源投入すると、Critical状態でシステムが起動して、該当ファイルは読み込めました。

 確かに、RAID5の仕様である、ストライピング&パリティ書き込みを実行して、高速化と1デバイスが喪失された状態でデータ復元は出来ましたが、冗長構成はエラーが発生しても処理を継続し、かつ正常にエラーが検出できなければ実用に耐えません。 このアダプタの存在によって、障害の切り分けが困難になったという点は特に問題ですね。

 やはり、価格差はあってもメジャーブランドの製品を選択するべきだと認識させられました。 予算的にハードウェアRAID構成が難しい場合は、無理にRAIDアダプタを搭載せずソフトウェアRAIDで構成しておく方がベターでしょう。

(21)


カテゴリー: LAMP[Linux, Apache, MySQL, PHP] | コメントをどうぞ