読者です 読者をやめる 読者になる 読者になる

tumblr

tumblr(タンブラー)は、メディアミックスブログサービス。ブログとミニブログ、そしてソーシャルブックマークを統合したマイクロブログサービスである。アメリカのDavidville.inc(現: Tumblr, Inc.)により2007年3月1日にサービスが開始された。

入門! nginx

サーバ技術


最近話題のnginxについに手を出したのですが、「nginx入門」みたいなブログ記事も一切見当たらず、あるのは英語のドキュメント記事くらい…という状況だったので、自分なりに訳して理解した部分を忘れないよう覚書。
今node.jsもちょこちょこやっているのですが、これまた物凄い勢いで開発が進む上に、その情報のほとんどは英語なわけでやはりもうホントに英語が読めないとどうしようもないんだなぁと実感しているわけです。まぁstackoverflowとか見ててもそこまで難しい文法使ってるわけでもないので、英語を見た瞬間に拒否反応起こしたりしなきゃなんとかなりそうですが。
「毎度毎度ブログ長すぎ死ね」とはてブのコメントで話題の僕のブログ、今日も長いです。

nginxってそもそもどう読むんだよ

「エンジンエックス」と読みます。正直すごくかっこいいです。apacheとかtomcatとかnginxとか、サーバは何故こんなにもかっこいい名前をしているんでしょうか。
公式ページには以下のような概要。

nginx [えんじんえっくす] は Igor Sysoev によって作られた HTTP とリバースプロキシのサーバで、メールプロキシサーバでもあります。Rambler (RamblerMedia.com) を含むロシアの多くの高負荷サイトで5年以上も動いています。Netcraft によると、nginx は 2010 年 4 月時点で 4.70% の人気サイトでサーバーとして、もしくはプロキシとして利用されています。成功例としては FastMail.FM や Wordpress.com があります。

http://nginx.org/ja/

という感じでプロキシサーバとしての用途が多いようです。
apacheがprefork形式で何もしてなくてもやたらとメモリを消費するのに比べ、nginxはnode.jsと同じくイベント駆動アーキテクチャを使用しているのでメモリ使用量が少なくて済むために、メモリ使用量の限られるVPSサーバで使われることも多いみたいです。wordpressとかapacheで動かしたらメモリ512Mじゃ足りなそうで怖いんですよね。


プロキシサーバの役割

一応覚書。


通常のフォワードプロキシはクライアントと オリジンサーバ (訳注: コンテンツ生成元のサーバ) の間に位置する中間サーバです。 オリジンサーバからコンテンツを取得する過程では、クライアントは 行き先としてオリジンサーバを指定しつつプロキシにリクエストを送り、 プロキシはオリジンサーバからコンテンツ取得のリクエストを送り、 コンテンツが取得できればそれをクライアントに返します。 クライアントが他のサイトにフォワードプロクシ経由でアクセスするには、 特別にそれ用の設定をしなければなりません。

フォワードプロキシの一般的な使用方法は、ファイアウォールによって 制限されている内部のクライアントにインターネットへのアクセスを 提供するものです。フォワードプロキシはネットワークの使用量を 減らすために (mod_cache で提供されている) キャッシュ機能を用いることもできます。
(中略)
一方リバースプロキシは、クライアントには普通の ウェブサーバのように見えます。クライアント側に特別な設定は必要ありません。 クライアントはリバースプロキシの名前空間に対して通常のコンテンツへの リクエストを行ないます。プロキシはリクエストをどこに送れば良いかを判定し、 あたかも自分自身がオリジンサーバであったかのようにクライアントに コンテンツを返します。

リバースプロキシのよくある利用方法は、インターネットユーザに ファイアウォールの中にあるサーバにアクセスを与えるというものです。 リバースプロキシは複数のバックエンドサーバへ負荷分散をするために 使ったり、遅いバックエンドエンドサーバのためにキャッシュ機能を提供したり するために使えます。また、リバースプロキシは複数のサーバを 同じ URL 空間にまとめるために使うこともできます。

http://httpd.apache.org/docs/2.1/ja/mod/mod_proxy.html

プロキシサーバは2種類あって、まず1つ目がフォワードプロキシ。よく会社に置かれてますが、社内LANからインターネットやメールを送る際はプロキシサーバに必ずそのリクエストを送り、プロキシサーバがそのリクエストに対するレスポンスを代理(proxy)で取得してくるというようなわけです。1度取得したページをキャッシュするためにリクエストを減らすことができたり、内部からのリクエストをプロキシサーバが受けたときにフィルタリングして特定ページへのリクエストは弾くということもできます。

2つ目のリバースプロキスはそのまんまフォワードの逆なのですが、アクセスを受けるアプリケーションサーバなどの前に置き、一度不特定多数の利用者からのリクエストを全て受けてから適宜APサーバへリクエストを振ります。同じ機能のAPサーバが複数台ある場合はそれらにうまいことアクセスを振り分けるロードバランサの役割を果たし、また前述のようなキャッシュサーバとして一度APサーバから取得したコンテンツを返すことも出来ます。


インストールする

とりあえずはHTTPサーバとしてちゃんと動いて、プロキシサーバとしても動くことを確認できるようにしてみましょう。

apacheの場合、インストール後にモジュールを追加することになっても設定ファイルにそのモジュールのパスを入れて再読込とかすればうまくいくのですが、nginxの場合はモジュールを追加する場合はビルドしなおす必要があります。面倒です。この記事での最終的な目的としてwebsocket(socket.io)のプロキシサーバとしても使えるようにしたいわけなんですが、現時点ではnginxはwebsocketをサポートしていないためモジュールを追加する必要があるわけです。また、この記事を書いている時点で最新安定版の1.0.11はSRPMも見つからないので、ソースからインストールすることにします*1


http://nginx.org/en/download.html から最新安定版のsrcをダウンロードします。一応このページの下の方にはビルド済のrpmがあります。

$ wget http://nginx.org/download/nginx-1.0.11.tar.gz
$ tar zxvf nginx-1.0.11.tar.gz
$ cd nginx-1.0.11


ダウンロード〜解凍までできたらコンパイルですが、この時コンパイルオプションに必要なモジュールを指定します。なので前述のwebsocket用のモジュール(https://github.com/yaoweibin/nginx_tcp_proxy_module)をここでダウンロード、パッチをあてます

$ git clone https://github.com/yaoweibin/nginx_tcp_proxy_module.git
$ patch -p1< nginx_tcp_proxy_module/tcp.patch


ここでやっとコンパイル出来ます。モジュールが必要ならばオプションが必要です。

$ ./configure --add-module=./nginx_tcp_proxy_module
$ make

tcpモジュール用のオプション以外にも色々と指定が出来ます。こちらのが詳しいので以下参照
http://wp.serpere.info/archives/1799
※ログやtmpファイルのパスを変える場合はそのディレクトリも作っておいてください。

これができたらインストールです。僕はこの時点でソースインストールしたものを管理するためにpacoを使っています。
http://d.hatena.ne.jp/rx7/20081011/p2
全然どうでもいいですが、「paco」で検索すると全く関係ないよく分からない単語がインクリメントサーチに出てくるのでドキドキします。不敬罪で逮捕されたりしませんように。

$ make install


これでインストールが完了しました。コマンドは /usr/local/nginx/sbin/nginx にあるので、もっとたたきやすい場所にシンボリックリンクを張って、起動してみます。

$ ln -s /usr/local/nginx/sbin/nginx /sbin/nginx
$ sudo nginx

これで80番ポートはすでに開いているので、アクセスすればwelcome to nginx とかいう文字が出てくるはずです。
起動スクリプトが欲しい場合は
http://shiken.infrabu.info/nginx/nginx%E3%82%92%E3%82%BD%E3%83%BC%E3%82%B9%E3%81%8B%E3%82%89%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%EF%BC%88%EF%BC%8B%E5%9F%BA%E6%9C%AC%E8%A8%AD%E5%AE%9A%EF%BC%89/
を参照。僕はyumで一旦インストールして起動スクリプトなどを作らせてremoveしてからソースインストールという荒業を使いました。


設定をしてみる

設定をしてみます。nginxの設定ファイルはcontextと呼ばれるブロックの階層構造になっており、より大きいブロックの設定値がデフォルト値として下方のブロックに伝わります。主に登場するcontextは以下の3つです。

  • http
  • server
  • location

うえから順番に影響力が強いです。実際には以下のような構造となって設定ファイルに書かれます。

http {

    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    server {

          listen 80;
          server_name localhost;

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

}


大まかにはhttpにはnginxの全体的な設定、serverにはバーチャルホストやlistenポート、locationにはリクエストURLのパスをどう扱うかという感じのことを書きます。ネストが深くなるほど、詳細な部分を書いていく感じです。

apacheと同じくディレクティブによって設定を記述します。contextによってはこのディレクティブは使えないということがあるので、どのディレクティブが使えるかはwikiを見てください。基本ほとんどのことはnginxのwikiを見れば分かります。もの凄く詳しいです。


http://blog.martinfjordvald.com/2010/07/nginx-primer/にあるサンプルを見てみましょう。

server {
      listen          80;
      server_name     domain.com *.domain.com;
     rewrite         ^  http://www.domain.com$request_uri? permanent;
}

server {
      listen          80;
      server_name     www.domain.com;

      index           index.html;
      root            /home/domain.com
}

バーチャルホストが2つある設定です。上のserverブロックは domain.com か何か適当なサブドメインを付けてリクエストされたときにマッチします。
下のブロックではwww.domain.comにマッチした場合の設定です。nginxではより詳細なマッチが優先されるので、www.domain.comでアクセスした場合は下のブロックに行きます。

上のブロックのrewriteなんですが、wikiを見ると

syntax: rewrite regex replacement flag

とあるので、上のブロックにマッチした全てのURIをwww.domain.comに書き換えて301リダイレクト(permanent redirect)を行います。
また、下のブロックのindexはapacheで言うところのdirectoryIndexに、rootはServerRootになります。
という感じで割とスッキリと設定ができるわけです。

リバースプロキシの設定

つぎはリバースプロキシの設定をしてみます。
一番最初に出した例のように、localhost宛に来たリクエストを別のマシン(192.168.1.1)に送るよう設定します。

server {
      listen 80;
      server_name localhost;
      
      location / {
            proxy_pass http://192.168.1.1/
      }
}

とりあえずはこれだけの記述でリバースプロキシを設定することができます。最低限必要なのはlocationブロックの中の proxy_pass です。この場合はlocalhost/に来た全てのリクエストを192.168.1.1に投げています。
もちろんリバースプロキシに必要な設定は実際はこれだけではないと思います。その場合はwikiのproxyモジュールにあるディレクティブを参考に設定してください。wikiのfullexampleでは、proxy用の設定ファイルを作りそれをnginx.confのほうでincludeしています。

proxy_conf
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;
client_max_body_size    10m;
client_body_buffer_size 128k;
proxy_connect_timeout   90;
proxy_send_timeout      90;
proxy_read_timeout      90;
proxy_buffers           32 4k;

各ディレクティブの意味はwikiを参照してください。$hostなど$のつくものは組み込み変数です。


上の方で書いたように、リバースプロキシはロードバランサとしての役割も果たします。wikiのfullexampleを参考にしてアクセスを振り分ける方法を見てみます。

upstream big_server_com {
      server 127.0.0.3:8000 weight=5;
      server 127.0.0.3:8001 weight=5;
      server 192.168.0.1:8000;
      server 192.168.0.1:8001;
}

server {
      listen          80;
      server_name     big.server.com;
      access_log      logs/big.server.access.log main;

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


big.server.comに来たアクセスを振り分ける設定ですが、このドメインに来たリクエストをproxy_passで設定されている big_server_com というサーバ群に投げます。で、これはどこに設定されているかというと、serverブロックの上のupstreamブロックに設定されています。
upstreamブロック内のserverディレクティブによって示されるIPアドレスをもつサーバにリクエストが振り分けられます。このupstreamブロック内でのserverディレクティブはロードバランス時の設定も同時に書くことが出来ます。
上にある例の中の weight は重み付けです。ある回数のリクエストがあった場合、デフォルト値を1としてどのくらいの割合で各サーバにリクエストを振り分けるかを指定します。この場合、12回アクセスがあればそのうち5回を 127.0.0.1:8000 に、もう5回を 127.0.0.1:8001 に、1回を 192.168.0.1:8000 に、もう1回を 192.168.0.1:8001 に、という配分になります。



とまぁこんな感じに設定を行っていくわけです。
apacheの場合、設定ファイルに最初から長ったらしいコメントがズラズラと書かれていて平気で1000行とか超えるためにもの凄く見にくいのが不満でしたが、nginxの場合は特にそのようなこともなくスッキリと書けるのが魅力的に思います。
あとsocket.ioのリバプロ設定方法もいつか書きます。

*1:SRPM以外でもビルドできる方法ってあるんでしょうか?詳しい方教えてください。