s_tajima:TechBlog

渋谷で働くインフラエンジニアのTechブログです。

ElasticSearchのindexの圧縮設定に関するベンチマーク

ElasticSearchのmappingの設定で、
compressに関する設定がindexのサイズ、応答性能にどう影響するかのベンチマークを取った。

テスト環境

ベンチマークは以下のような構成で実施。

f:id:s_tajima:20140228195659j:plain

上記のような構成で、①indexサイズ, ②応答性能(httpdの%Drequesttime_microsecで計測)を計測。

  • 各種バージョン
    • ElasticSearch: v1.0.0
    • Kibana: v3.0.0-milestone5
  • 登録データは実サービスのアクセスログ(ltsv)
  • 応答性能はkibanaが実際に投げる//_searchに対するPOSTを集計
  • Kibanaでの定期的なアクセス
    • それぞれのindexにAuto-Refresh 1mでアクセスするDashBoardを作成
    • 手元のMacChromeでそれらをtabで1枚ずつ開いて放置

それぞれのIndex(test01, test02, test03)の設定の差分は以下の通り。

  • test01
    • compressに関する設定なし(defaultの状態)
  • test02
    • 以下の設定をmapping templateに記述
{ "mappings": { "_default_": { "_source": { "compress": true } } } }
  • test03
    • 以下の設定をmapping templateに記述
{ "settings": { "index.store.compress.stored" : "true"} }


※正直これらの設定が具体的にどんなものなのかわかっていないので後日調査予定

結果

①indexサイズ

f:id:s_tajima:20140228194829p:plain

②応答性能

f:id:s_tajima:20140228194839p:plain

まとめ

  • いずれの設定も応答性能に影響はなさそう。
  • test02の設定を入れることによってindexサイズが大きくなってしまった。

今回使用した設定と、indexのmetadataはこちらにまとめておきます。


Configs for benchmarking of ElasticSearch compress ...

Fluentd forwardを先の名前解決結果によって切り替える

やりたいこと

以下のような図で表されるような構成をとっている場合に、
aggregator A -> aggregator Bへのforward先切り替えを、名前解決によって行いたい。

f:id:s_tajima:20140219182131j:plain

検証

以下の様な設定でテスト

この設定をした上で名前解決されるIPを変更してみる。
結果は失敗。しばらく待ってみてもaggregator Aにforwardされ続けてしまう。

この辺りの実装を追うためにソースを確認。
処理を追っていくと怪しそうなのは以下のあたり

fluentd-0.10.39/lib/fluent/plugin/out_forward.rb

@expire_dns_cacheによってキャッシュの挙動がかわりそう。
肝心のexpire_dns_cacheの初期値は...

 config_param :expire_dns_cache, :time, :default => nil  # 0 means disable cache 

nil。# persistent cache になる模様。

forwardの設定に

expire_dns_cache 0

を追加したら無事名前解決の変更を反映してくれるようになった。

ElasticSearch + Kibana3のアクセスコントロールについて

Elasticsearch + Kibana3でログ解析環境を構築する際、アクセスコントロールの設定に困ったのでそれを解決したというメモ

通常Kibana3は、以下の図のようにブラウザが直接ElasticSearchにリクエストをかけている。

f:id:s_tajima:20140219122410j:plain

ただし、この状態ではKibana3の利用者が自由にElasticSearchにクエリをなげられてしまうし、利用者自体の制限もできない。
ということでElasticSearchの前段にReverseProxyをはさむことで、クエリの制限とユーザ認証をかけたい。
今回やりたかったのはReverseProxyとしてhttpdをはさんでBASIC認証(+LDAPによるユーザの制御)を入れるということ。
素直にやると以下のようになる。

f:id:s_tajima:20140219122414j:plain

しかしこれだと問題が発生。
"httpd: VH2" にBASIC認証をかけると、アクセス時には
リクエストヘッダにAuthorizationヘッダが付与されていなければいけないのだが、
この構成ではElasticSearchのXHRの通信にAuthorizationヘッダが付与されない。
するとブラウザからElasticSearchへのリクエストが通らないためKibana3は動かない。

この問題を解決するために以下のような構成に変更。

f:id:s_tajima:20140219122418j:plain

kibana3のconfig.jsの elasticsearch: の設定を、
kibana3自身を返すHost, Portと同じものにしてpathを/esとする。
この構成ならばXHRにAuthorizationヘッダが付与されるのでElasticSearchへのアクセスに認証がかけられる。
具体的な設定は以下のような感じ。

AWSでリージョン間の自動DR構成を構築してみた #vgadvent2013

こんにちは、
VOYAGE GROUP エンジニアブログ Advent Calendar 2013 の2日目担当の @s_tajima です。

僕は今インフラエンジニアとして働いているのですが、
障害が発生すると携帯にアラートメールや電話がとんできて対応を迫られます。

そして障害というのはたいてい楽しいイベントが行われているときに発生するものです。
そういうものと割り切ってせっせと対応をするのもよいのですが、
僕は年末の楽しいイベントを邪魔されたくありません。
それはもちろんAWSの東京リージョンのデータセンターがすべて同時に火事でなくなってしまうような非常事態でもです。

ということで今日はAWSのリージョン間自動DR構成をつくってみた話をしようと思います。

続きを読む

right_awsのデバッグログ出力を制御する方法

最近、AWSのRuby用コンソールツールの一つであるright_awsを使っているのですが、
スクリプトを実行した時に、

I, [2012-06-23T17:55:15.996727 #25742]  INFO -- : New RightAws::AcwInterface using shared connections mode
I, [2012-06-23T17:55:15.997220 #25742]  INFO -- : Opening new HTTPS connection to monitoring.ap-northeast-1.amazonaws.com:443

のようなデバッグログがいちいち出力されるのが邪魔だなあと思ったので消そうと思いました。

しかし、right_awsはまともなドキュメントがないためやり方がわからず...
なのでライブラリ本体のコードを読んで、デバッグログ制御の方法を調べてみました。


コードを追っていくと、ログ出力に関係のありそうなのが、

  • right_aws-3.0.4/lib/awsbase/right_awsbase.rb 314行 ~ 318行
      @logger = @params[:logger]
      @logger = ::Rails.logger       if !@logger && defined?(::Rails) && ::Rails.respond_to?(:logger)
      @logger = RAILS_DEFAULT_LOGGER if !@logger && defined?(RAILS_DEFAULT_LOGGER)
      @logger = Logger.new(STDOUT)   if !@logger
      @logger.info "New #{self.class.name} using #{@params[:connections]} connections mode"

つまり@params[:logger]に任意のloggerオブジェクトを渡せばいいと。

こんな感じのコードを

  • before
Acw = Rightscale::AcwInterface.new(access_key, access_secret)

こんなふうに書き換えたらログレベルを制御できるようになりました。

  • after
logger = Logger.new(STDOUT)
logger.level = Logger::WARN
Acw = Rightscale::AcwInterface.new(access_key, access_secret, {:logger => logger})

AWSのコンソールツールの実行負荷の比較

AWSのコンソールツール実行時の負荷について検証してみた。

  • それぞれのツールの導入方法などは割愛。
  • 実行するコマンド, コードは以下の通り
EC2 API Tools
$ ec2-describe-regions
AWS SDK for PHP
# describe_regions.php
<?php
require_once 'AWSSDKforPHP/sdk.class.php';
$ec2 = new AmazonEC2();
$response = $ec2->describe_regions();
var_dump($response->body);

負荷(dstatコマンドで実行時の負荷を計測)

平常時
$ dstat
----total-cpu-usage---- -dsk/total- -net/total- ---paging-- ---system--
usr sys idl wai hiq siq| read  writ| recv  send|  in   out | int   csw 
  0   0 100   0   0   0| 592B 2362B|   0     0 |   0     0 |1004   435 
  0   0 100   0   0   0|   0     0 |1164B  242B|   0     0 |1021    16 
  0   0 100   0   0   0|   0     0 | 246B  258B|   0     0 |1006    23 
EC2 API Tools
$ dstat
----total-cpu-usage---- -dsk/total- -net/total- ---paging-- ---system--
usr sys idl wai hiq siq| read  writ| recv  send|  in   out | int   csw 
  0   0 100   0   0   0|   0     0 | 672B  746B|   0     0 |1005    49 
 14   1  85   0   0   0|   0     0 | 792B  388B|   0     0 |1006   152 
 59   2  39   0   0   0|   0     0 | 552B  388B|   0     0 | 999   488 
 75   3  22   0   0   0|   0     0 | 492B  388B|   0     0 |1009   507 
 64   2  34   0   0   0|   0     0 | 432B  388B|   0     0 |1011   353 
 67   1  32   0   0   0|   0     0 | 492B  534B|   0     0 |1021   422 
 25   1  74   0   0   1|   0   536k|4028B 1274B|   0     0 |1028   277 
 31   0  68   0   0   0|   0     0 |1737B 5226B|   0     0 |1029   105 
 89   2  10   0   0   0|   0     0 | 486B  258B|   0     0 |1012   190 
 21   1  77   0   0   0|   0     0 | 552B  706B|   0     0 |1015   145 
AWS SDK for PHP
$ dstat
----total-cpu-usage---- -dsk/total- -net/total- ---paging-- ---system--
usr sys idl wai hiq siq| read  writ| recv  send|  in   out | int   csw 
  0   0 100   0   0   0|   0     0 | 432B  388B|   0     0 |1011    19 
  1   1  99   0   0   0|   0     0 | 678B 1119B|   0     0 |1015    50 
  1   0 100   0   0   0|   0     0 |4155B 1247B|   0     0 |1024    29 
  0   0 100   0   0   0|   0     0 |1363B 1972B|   0     0 |1023    34 

実行時間(timeコマンドで実行時間を計測)

EC2 API Tools
$ time ec2-describe-regions > /dev/null

real    0m7.565s
user    0m8.366s
sys     0m0.231s
AWS SDK for PHP
$ time php describe_regions.php > /dev/null

real    0m1.423s
user    0m0.039s
sys     0m0.008s

結論

  • 圧倒的にSDK for PHPの方がサーバーにやさしかった。

PHPUnitのデータプロバイダの使い方

以下のような映画館の料金判定のメソッドに対して、テストを書くことを想定。

  • 12歳以下と13歳以上で、子供料金と大人料金を区別する
    • 子供料金:1000円、大人料金:1800円
  • 男性と女性を区別する
    • 毎週水曜日をレディースデーに設定し、女性であれば料金を半額にする

実装

<?php
class Movie {
    function check($gender, $age, $date){
        date_default_timezone_set('Asia/Tokyo');

        //入力値が期待されているものかのチェック
        if ($gender !== "man" && $gender !== "woman") return false;
        if (!is_int($age) || $age < 0) return false;

        $date = explode('/', $date);
        if(!checkdate($date[1], $date[2], $date[0])) return false;

        $price = ($age <= 12) ? 1000 : 1800; 
        if($gender === "man") return $price;

        $weekday = date("l", mktime(10, 10, 10, $date[1], $date[2], $date[0]));
    
        if($weekday === "Wednesday") $price = $price/2;
        return $price;
    }   
} 

データプロバイダを使わない場合

<?php
require_once "./Movie.php";

class MovieTest extends PHPUnit_Framework_TestCase
{   
    public function setup()
    {   
        $this->Movie = new Movie();
    }   

    public function testMovie()
    {   
        #男性のテスト
        $this->assertEquals(1000, $this->Movie->check('man', 12, '2012/05/15'));
        $this->assertEquals(1800, $this->Movie->check('man', 13, '2012/05/15'));
        $this->assertEquals(1000, $this->Movie->check('man', 12, '2012/05/16'));
        $this->assertEquals(1800, $this->Movie->check('man', 13, '2012/05/16'));

        #女性のテスト
        $this->assertEquals(1000, $this->Movie->check('woman', 12, '2012/05/15'));
        $this->assertEquals(1800, $this->Movie->check('woman', 13, '2012/05/15'));
        $this->assertEquals(500, $this->Movie->check('woman', 12, '2012/05/16'));
        $this->assertEquals(900, $this->Movie->check('woman', 13, '2012/05/16'));

        #例外のテスト
        $this->assertEquals(false, $this->Movie->check('men', 12, '2012/05/15'));
        $this->assertEquals(false, $this->Movie->check('man', -1, '2012/05/15'));
        $this->assertEquals(false, $this->Movie->check('man', 12, '2012/15/15'));
    }   
}

これをデータプロバイダを使うとこうなる

<?php
require_once "./Movie.php";

class MovieTest extends PHPUnit_Framework_TestCase
{   
    public function setup()
    {   
        $this->Movie = new Movie();
    }   

    public static function patternProvider()
    {   
        $values = array(
            array(1000,  'man',   12, '2012/05/15'),
            array(1800,  'man',   13, '2012/05/15'),
            array(1000,  'man',   12, '2012/05/16'),
            array(1800,  'man',   13, '2012/05/16'),
            array(1000,  'woman', 12, '2012/05/15'),
            array(1800,  'woman', 13, '2012/05/15'),
            array(500,   'woman', 12, '2012/05/16'),
            array(900,   'woman', 13, '2012/05/16'),
            array(false, 'men',   12, '2012/05/15'),
            array(false, 'man',   -1, '2012/05/15'),
            array(false, 'man',   12, '2012/15/15'),
        );  
        return $values;
  }

    /** 
     * @dataProvider patternProvider
     */
    public function testMovie($price, $gender, $age, $date)
    {   
        $this->assertEquals($price, $this->Movie->check($gender, $age, $date));
    }
}  
データプロバイダの使い方なのでプログラムの仕様とかテストケースとかにはふれないで...