EC2でnginx動かしてパフォーマンス検証した。
ひとまず動かしてみる。
yumでインストール。楽でいいね。
$ sudo yum install nginx ==snip== Running Transaction Installing : libxslt-1.1.26-2.6.amzn1.x86_64 1/5 Installing : GeoIP-1.4.8-1.5.amzn1.x86_64 2/5 Installing : libXpm-3.5.8-2.8.amzn1.x86_64 3/5 Installing : gd-2.0.35-10.9.amzn1.x86_64 4/5 Installing : nginx-1.0.15-1.9.amzn1.x86_64 5/5 ==snip== Installed: nginx.x86_64 0:1.0.15-1.9.amzn1 Dependency Installed: GeoIP.x86_64 0:1.4.8-1.5.amzn1 gd.x86_64 0:2.0.35-10.9.amzn1 libXpm.x86_64 0:3.5.8-2.8.amzn1 libxslt.x86_64 0:1.1.26-2.6.amzn1
いろいろ一緒にインストールされた。
起動してみる。
$ sudo nginx
ブラウザからアクセスしたら無事にページが表示されました。
パフォーマンスを検証する
- 比較対象がないとということでapacehと比較することにした。
- 条件を決める
- 測定にはjmeterを使うことにする。
- webサーバーによく使われそうなm1.smallとm1.largeでそれぞれ検証する
- スレッドグループのパラメータは以下の感じ
- スレッド数を5, 30でそれぞれ計測
- スレッドグループをそれぞれ60秒間動かしたときのlatency, スループットを計測
- 接続もしくはレスポンスに300ms以上かかったものはエラーとみなし、エラーレートも計測する。
- リクエストで1KBのデータを投げてレスポンスも1KBのデータを返すことにする。
- ちょっとはまったのが、nginxはPOSTリクエストに対して静的ファイルを返せないらしい。405 not allowedが返ってしまう。なので、queryストリングに1KB分のデータを載せることにした。
結果は以下のとおり。
- m1.small
計測項目 | apache | nginx | |
---|---|---|---|
5スレッド | latency(平均値) | 27 | 13 |
latency(中央値) | 5 | 13 | |
スループット | 153.4req/s | 321.2req/s | |
エラーレート | 0.09% | 0.00% | |
30スレッド | latency(平均値) | 113 | 13 |
latency(中央値) | 39 | 13 | |
スループット | 261.0req/s | 1904.7req/s | |
エラーレート | 33.40% | 0.00% |
- m1.large
計測項目 | apache | nginx | |
---|---|---|---|
5スレッド | latency(平均値) | 4 | 13 |
latency(中央値) | 5 | 13 | |
スループット | 875.3req/s | 322.1req/s | |
エラーレート | 0.01% | 0.00% | |
30スレッド | latency(平均値) | 5 | 13 |
latency(中央値) | 5 | 13 | |
スループット | 3823.4req/s | 1903.9req/s | |
エラーレート | 0.00% | 0.00% |
nginxは環境に左右されずにそれなりの性能を出す。
apacheは負荷<リソースな状態なときに高い性能を出す(ようにみえる。)
ちょっと気になったのでm1.largeで更に大きな負荷(500スレッド)をかけてみた。
- m1.large
計測項目 | apache | nginx | |
---|---|---|---|
500スレッド | latency(平均値) | 83 | 24 |
latency(中央値) | 23 | 18 | |
スループット | 5118.7req/s | 9697.2req/s | |
エラーレート | 15.78% | 0.00% |
やはり負荷が高なくなってくるとリソースが比較的豊富なm1.largeでもapacheの性能が落ちてきた。
EC2にfluentdをいれてapacheのアクセスログをs3に保存してみる。
ruby1.9.2以降の環境を作る
fluentdにはruby1.9.2以上が必要らしいのでruby1.9.xをインストール。
rvmで1.9.3をインストールしました。
ちなみにこの記事を書いてる時点でのAmazon Linuxのrubyは1.8.7。
rvmでruby環境をインストールする場合、fluentdのプラグインに必要なライブラリを、
事前にOSにインストールしておかないまずい。今回の場合、以下のライブラリが必要。
libxml2-devel, libxslt, openssl
$ curl -L get.rvm.io | bash -s stable $ export PATH=$PATH:~/.rvm/bin $ source ~/.rvm/scripts/rvm $ rvm install 1.9.3 $ rvm use 1.9.3 $ ruby -v ruby 1.9.3p194 (2012-04-20 revision 35410) [x86_64-linux]
fluentdのインストール
gemをつかってfluentdをインストール
gem install fluentd . . . Successfully installed msgpack-0.4.7 Successfully installed json-1.7.5 Successfully installed yajl-ruby-1.1.0 Successfully installed iobuffer-1.1.2 Successfully installed cool.io-1.1.0 Successfully installed http_parser.rb-0.5.3 Successfully installed fluentd-0.10.25
いろいろ一緒にインストールされた。
ひとまず動かしてみる
$ fluentd --setup ./fluent # configファイルが作成される Installed ./fluent/fluent.conf. $ fluentd -c ./fluent/fluent.conf -vv & # これでfluentdが起動する $ echo '{"json":"hello fluentd"}' | fluent-cat debug.text 2012-09-20 08:00:34 +0000: plugin/in_forward.rb:139:initialize: accepted fluent socket object_id=19099020 2012-09-20 08:00:34 +0000 debug.text: {"json":"hello fluentd"} 2012-09-20 08:00:34 +0000: plugin/in_forward.rb:180:on_close: closed fluent socket object_id=19099020
configファイルを見ると下記のような記述になっているので、上記のように標準出力にjsonが表示されてるのでOKとおもわれる。
## match tag=debug.** and dump to console <match debug.**> type stdout </match>
あと、こんなconfigファイルの記述があったのでhttp経由でのログ出力もためしてみた。
# HTTP input # http://localhost:8888/<tag>?json=<json> <source> type http port 8888 </source>
こんな感じ。
$ curl http://localhost:8888/debug?json=%7b%22title%22%3a%22hello%20fluentd%22%7d 2012-09-20 08:12:56 +0000 debug: {"title":"hello fluentd"}
fluentdでapacheのアクセスログを監視するように設定する
fluentd-plugin-s3をインストール
$ gem install fluent-plugin-s3 . . . Successfully installed uuidtools-2.1.3 Successfully installed multi_json-1.3.6 Successfully installed multi_xml-0.5.1 Successfully installed httparty-0.9.0 Successfully installed nokogiri-1.5.5 Successfully installed aws-sdk-1.1.4 Successfully installed fluent-plugin-s3-0.2.2 7 gems installed . . .
いろいろインストールされた。
次にfluentdのconfigファイルを書く
<source> type tail format apache path /var/log/httpd/access_log tag apache.access </source> <match apache.access> type s3 aws_key_id YOUR_AWS_ACCESS_KEY aws_sec_key YOUR_AWS_SECRET_KEY s3_bucket factory-fluentd s3_endpoint s3-ap-northeast-1.amazonaws.com path / buffer_path /tmp/fluentd_s3 time_slice_format %Y%m%d-%H time_slice_wait 10m #flush_interval 5s これを書くと、timesliceを無視して指定したインターバルごとにs3にflushされる。 utc </match>
これでapacheにアクセスしてログを吐かせるとs3にちゃんとオブジェクト(ファイル)ができあがってました。便利。
ログも1行ずつs3に書きに行くわけではなくてある程度バッファして書き込んでいってくれるのでs3のAPI叩きすぎてクラウド破産、なんてことにはならずに済みそうですね。
あとは、転送失敗時の処理とか、バッファファイルのためのディスク領域が足りなくなっちゃった時の対処とか気になるところですが今回はこれでおしまいということで。
PHP5.3で無名関数を使う
PHP5.3の無名関数でちょっとハマったのでメモ。
どうやら無名関数(だけじゃなくて普通の関数もだと思うけど。)はPHPの実行前のフェーズで検証がされる模様。
ちゃんと動く。
<?php $a = array(1,2,3,4,5); $b = array_map(function($item){return $item * 10;},$a); print_r($b); /* Array ( [0] => 10 [1] => 20 [2] => 30 [3] => 40 [4] => 50 ) */
これがうまく動かない。
- $bがarrayじゃないと言われてる。これって、無名関数の初期化がPHP実行時の一番最初のほうのフェーズで行われているということなんだろうか。
<?php $a = array(1,2,3,4,5); $b = array(); array_map(function($item){array_push($b, $item * 10);},$a); print_r($b); /* [23-Aug-2012 05:01:33 UTC] PHP Warning: array_push() expects parameter 1 to be array, null given in /home/aaa/array.php on line 5 */
これはもちろん大丈夫だった。
<?php $a = array(1,2,3,4,5); $b = array(); foreach($a as $item){ array_push($b, $item * 10); } print_r($b); /* Array ( [0] => 10 [1] => 20 [2] => 30 [3] => 40 [4] => 50 ) */
というか、PHPは関数を実行前に検証しますよっていうだけの話だったのかも。
LLでのDNSルックアップはどこかにキャッシュされるのか。
rubyとpythonとphpとperlで試してみた。
それぞれの言語ごとに下記のコードを動かしながら、
tcpdump dst port 53
でチェックしてみた。結果としてはすべての言語においてDNSレコードはキャッシュされることなく毎回DNSルックアップしてた。JavaだとVM上にDNSレコードがキャッシュされるらしい。
LLだと毎回DNSルックアップされるということがわかったので、通信処理のオーバーヘッドが気になる時、もしくは気にしなきゃいけないくらいの頻度で通信するようなアプリケーションを使うならhostsに書くなどがよさげ。
逆にDNSを使った負荷分散の時は、このへん特にケアしなくて良い感じですね。
- php
<?php require("HTTP/Request.php"); $args = $_SERVER["argv"]; $url = $args[1]; $http = new HTTP_Request($url); $count = 0; for(;;){ $response = $http->sendRequest(); print $count++ . "\n"; sleep(5); }
#!/usr/bin/perl use strict; use warnings; use LWP::UserAgent; my $ua = LWP::UserAgent->new(); my $count = 0; while(1){ $ua->get($ARGV[0]); print $count++ . "\n"; sleep(5); }
from urllib import urlopen import sys import time argvs = sys.argv count = 0 while True: urlopen(argvs[1]) count += 1 print count time.sleep(5)
require 'net/http' require 'uri' url = URI.parse(ARGV[0]) count = 0 while 1 > 0 Net::HTTP.start(url.host, url.port){|http| http.get('/') } sleep 5 end