Figを使って複数のDockerコンテナをコマンド一発で立ち上げる
土日を使ってDocker入門を読みながらDockerを触ってみました。
Docker入門 Immutable Infrastructureを実現する
- 作者: 松原豊,米林正明
- 出版社/メーカー: 技術評論社
- 発売日: 2014/04/25
- メディア: Kindle版
- この商品を含むブログ (4件) を見る
とりあえずRails/MySQL/Elasticsearchで構成されたWebサービスの開発環境を作ることを想定して、ひと通り手でコマンドを打ちながらMySQLとElasticsearchのコンテナを作ってRailsから接続させることはできました。
$ docker run -d -p 9200:9200 -p 9300:9300 dockerfile/elasticsearch $ docker run -d -p 3306:3306 -e MYSQL_ROOT_PASSWORD=password mysql
これはこれで良いのですが毎回イメージごとに引数を思い出すのはだるいのでシェルスクリプトでも書こうかなと思ったら、ちょうどFigという複数のコンテナを一元管理する仕組みを提供してくれるツールを見つけたので使ってみました。
前提
- DockerとFigがインストール済
- OS XではHomebrewでboot2dockerとFigをインストールできます
- 使うイメージ
fig.yml
docker run
で起動するときのオプションをfig.ymlに書きます。
リファレンスはここにありますがdocker run
のオプションをそのまま書くだけなので簡単です。
elasitcsearch: image: dockerfile/elasticsearch ports: - '9200:9200' mysql: image: mysql ports: - '3306:3306' environment: MYSQL_ROOT_PASSWORD: password
実行
あとはfig up
するだけです。
実行するとMySQLとElasticsearchの起動ログがずらずらと流れて起動完了します。 これで最新のMySQLとElasticsearchをホストOSの環境を汚すことなくインストールして使うことができます。
fig scale
さらにfig scale
というコマンドを使うと簡単にサービスを複数立ち上げることもできます。
以下はMySQLのコンテナを3つに増やした場合ですが、fig scale mysql=3
とするだけです。
(ただし、ポートフォワードしてると衝突してしまうため3306:3306
としていたものを3306
に変更しました)
% fig scale mysql=3 Starting figtest_mysql_2... Starting figtest_mysql_3... % fig ps Name Command State Ports ----------------------------------------------------------------------------------------- figtest_elasitcsearch_1 /elasticsearch/bin/elastic ... Up 9300/tcp, 9200->9200/tcp figtest_mysql_3 mysqld --datadir=/var/lib/ ... Up 49155->3306/tcp figtest_mysql_2 mysqld --datadir=/var/lib/ ... Up 49154->3306/tcp figtest_mysql_1 mysqld --datadir=/var/lib/ ... Up 49153->3306/tcp
この環境でMySQLだけ増やしても意味無いですが、ResqueなどのWorkerを複数立ち上げたりロードバランサのコンテナを追加したりすると開発環境でも本番環境に近い構成が簡単に作れるようになるかもしれませんね。
まとめ
Figを使うと
- プロジェクトが依存するミドルウェアをコマンド1つで立ち上げられる
- 1つのマシンに複数バージョンのミドルウェアを共存させられる
- チーム開発時にDockerのことを知らないメンバーでも簡単に同じ環境を作れる
といったことが簡単に実現させられるようになります。
しかもFigの開発元は最近Docker社に買収されFigはDockerの公式ツールになるようなので今後もっと便利になっていきそうです。
通話中の画面サイズ
iPhoneには通話中やテザリング中にステータスバーの高さが2倍になる仕様があります。 この時、狭められた分のviewのサイズはどうなっているか調べてみました。
基本的な考え方
まずiOS7の場合viewは通常画面の一番上から始まるので、透過したステータスバーと20px分重なっているのが通常状態です。
通話中などにステータスバーが2倍の40pxになると、20px重なっている分は変わらず、さらに20px分viewが縮められます。
ではこれを元にいくつかのパターンを見てみます。
普通のviewの場合
まず普通のviewの場合、基本的な考え方の通り、ステータスバーが2倍になるとyが+20され、heightが-20されます。 (画像中の数字は{{x,y},{width,height}}) 通話中はステータスバーが透過しなくなるため一番上のグレーのバーがステータスバーの下に潜り込んで見えなくなっています。
UINavigationControllerの場合
UINavigationControllerの中のviewの場合はheightのみ-20されます。親のviewに対する起点は変わってないので当然ですね。
ChildViewControllerの場合
以下のようにchildViewControllerを2つセットした画面で試してみます。1つ目はUINavigationController、2つ目はただのViewControllerです。
親のviewは基本通りy+20, height-20で、赤いただのViewControllerをセットしたChild ViewControllerは特に変化していません。 ここまではいいのですが、なぜか1つ目のUINavigationControllerをセットしたところだけheightが-20されています。
この挙動は使う側としてはあまり期待していない挙動だと思うのですが、もしかするとステータスバーの高さが変わった時にUINavigationControllerの高さを問答無用で20px縮める処理がどこかにあるのかもしれません。
@y_310 最後のEmbeddedされたViewの高さが変わる件ですが、ViewのautoresizingMaskの設定次第だと思います。コードをダウンロードして見たみたところ上のViewだけUIViewAutoresizingFlexibleHeightが入ってました。
— 所 友太 (@tokorom) 2014, 4月 21
原因はautoresizingMaskの設定でした。UINavigationController側のContainer Viewだけ以下の様な設定になっていて高さがリサイズされていました。
このようにUIViewAutoresizingFlexibleHeightを外してやると高さが変わらなくなって期待通りの挙動になりました。
今回の検証に使ったコードはこちらにあります。 https://github.com/y310/StatusBarExample
UITableViewControllerのchildViewControllersにUITableViewControllerを入れる時のTips
UITableViewControllerの各セクションをChildViewControllerの仕組みを使って別のUITableViewControllerで管理しようとしてハマったのでメモ。
この画像のようにStatic cellにcontainer viewを入れて、そこに別のUITableViewControllerをembed segueで接続することで、セクションごとに別のデータを扱う場合にViewControllerを分けることができます。
ただ、このままだと、各セルの中で上下にスワイプした時に画面全体ではなくセルの中だけがスクロールしてしまいます。 それを防ぐためにはスクロールしないようにすればいいだろうと思ってScroll ViewのScrolling Enabledのチェックを外し一見問題なく動くようになったのですが、色々いじっているうちに奇妙な挙動をすることに気づきました。。
スクロールしてから適当な行をタップすると時々1回目のタップに反応しないのです。 ChildViewControllerのtableView:didSelectRowAtIndexPath:も呼ばれません。 2回目以降のタップは正常に反応します。
試行錯誤した結果、原因はわからないのですがScrolling EnabledをNOにするのをやめて、BouncesをNOにすることで、この問題を起こさずにスクロールさせない状態を実現できました。 ただし、当然ですがスクロールをしないようにしているわけではないので親のcontainer viewとChildViewControllerのviewのサイズを同じにしておかないと子のviewがスクロールしてしまいます。 そのためtableView:heightForRowAtIndexPath:で高さを調整してやる必要があります。
検証のために作ったサンプルコードです。
第2回elasticsearch勉強会でKibanaの発表をしてきました #elasticsearchjp
2013年11月12日にリクルートテクノロジーズで開催された「第2回 ElasticSearch勉強会」で「Kibana入門」というタイトルで発表してきました。 社外で発表をするのは初めてだったのでどんな雰囲気になるか不安でしたが、色々質問していただけて安心しました。懇親会でも話しかけていただけてありがとうございました。
長さの関係で発表資料に書かなかったことをいくつかこちらに書いておきます。
クエリのピン止め
クエリは名前をつけてピン止めすることができます。ヒストグラムパネルやテーブルパネルはグラフに使用するクエリをすべてのクエリ、ピン止めしたクエリ、ピン止めしていないクエリ、選択したクエリから選べますのでパネルごとに異なるクエリを指定したい時に便利になります。
ヒストグラムの一部を拡大
画面上特にヒントがないので意外と気づきづらいですが、ヒストグラムはグラフ上でドラッグすることで選択した範囲を拡大できます。
設定の保存場所
資料ではelasticsearchに保存する場合のみ記載しましたが、その他にjsonファイルとしてエクスポートする方法と、ブラウザのLocal Storageに保存する方法があります。ただしLocal Storageは"Set as my home"つ まり自分のデフォルトダッシュボードとして1件しか保存できません。 またデータのロードについては、elasticsearch、ファイル以外にgistからURLを指定して読み込む方法もあります。これによってApache用のダッシュボード等一般的な設定についてはオープンに共有していくことも可能です。
logstash形式ではないログを表示する
fluent-plugin-elasticsearchなどでログをelasticsearchに入れると、インデックス名がlogstash-YYYY.MM.DD、各ドキュメントは必ず@timestampというフィールドを持つというデータが作られます。 しかしこの形式はKibanaにとっては必須ではありません。インデックス名に日付が入っていることでKibanaは自動的に過去のインデックスに順番にクエリを投げていって結果を集約してくれますが、1つのインデックスにすべてのデータが入っている場合など、そういった挙動を必要としない場合は任意のインデックス名を指定することができます。 また@timestampというフィールドについても名前はKibanaの設定で変更できますし、時刻フィールドが存在しなくてもfacetを使った円グラフ等の表示は可能です。 とは言え、デフォルトの設定値は上記のようなlogstash形式になっているので特に事情がない場合はそれに従うのが良いでしょう。
最後に
今回の発表で、現時点のKibanaで機能的にできることは概ね紹介できたかと思っています。あとはこれらの機能をどのように実際のユースケースに応用していくかというのが今後の課題になるかと思いますので、利用事例を公開してくださる方が増えるのを楽しみにしています。
Kibana 3 + Rails + Fluentdのサンプルアプリを作ってみた
miyagawaさんのPodcast Rebuild: 19でKibanaの話があってちょっと盛り上がり始めてるので、簡単に動作を試せるサンプルアプリセットを作ってみました。
https://github.com/y310/kibana-trial
git cloneしてREADMEに書いてある手順を実行していくと大体動くと思います。
railsからfluentdにログを送る部分は、こんな感じでrack middlewareを使って送ります。
# application_controller.rb class ApplicationController < ActionController::Base around_filter :collect_metrics def collect_metrics yield # ensureを使うのは例外時のログも捕捉するため ensure # controllerのコンテキストで取りたい情報はここで追加する env['rack.metrics'] = params.slice(:user_id, :controller, :action).merge( path: request.path, user_agent: request.user_agent, method: request.method ) end end # lib/rack/metrics_logger.rb module Rack class MetricsLogger def initialize(app) @app = app end def call(env) status, headers, body = @app.call(env) # リクエストの終わりに取りたい情報はここで追加 metrics = (env['rack.metrics'] || {}).merge( status: status, runtime: headers['X-Runtime'] ) logger.post("kibana", metrics) [status, headers, body] end def logger @logger ||= Fluent::Logger::FluentLogger.new(nil) end end end
ひと通り立ち上がったら、curl localhost:3000
を何回か叩くと http://localhost:4567/index.html でKibanaのグラフが表示されるはずです。
これはjmeter/test_request.jmxを使ってJMeterで1分間リクエストを流した時のグラフです。
バー1本が1秒なので、8 rpsくらいのリクエストが流れていることがわかります。
平均レスポンスタイムはどうなっているでしょうか。 右上のヒストグラムの設定リンクをクリックすると以下の設定画面が出てくるので、Modeをmeanにして、Value Fieldをruntimeにすると平均レスポンスタイムのグラフに変わります。
サンプル数が少ないのでばらつきが激しいですが大体40msくらいで返せているようです。
次は、エラーになったリクエストがどれくらいあるか見てみます。
検索クエリはstatusが200のものと、それ以外のものとします。
Kibanaでは複数の検索クエリに名前をつけてPinしておくことができます。
ちょっとわかりづらいですが、クエリフィールドにクエリ(ここではstatus:200
)を入力してから、フィールドの端にある緑の丸をクリックすると画像のようなポップアップが表示されるので、ここにクエリの名前を入力して左上のピンアイコンをクリックすると保存されます。
同様にエラーのリクエストについても-status:200
と入力して保存します。
この時点でグラフには2つのクエリの結果がstackされて表示されます。
OKに比べてErrorが少なすぎて見づらいのでグラフを分けてみます。 設定からそのグラフで表示させるクエリを選ぶことができるので、グラフを追加した上でそれぞれのグラフに1つずつクエリを割り当てます。
するとこのように成功したリクエストとエラーリクエストを分けて見ることができます。
最後にここまで作ったDashboardを保存します。右上のフロッピーアイコンをクリックすると保存ウィンドウが出てくるので、そこに名前を入力して保存してください。保存した設定はフォルダアイコンの方からロードすることができます。この設定はElasticSearchのkibana-intというインデックスに保存されます。
まとめ
こんな感じでクエリを保存してグラフに設定することで見たい情報を一覧できるダッシュボードを簡単に作ることができます。 データの表示形式はヒストグラム以外にも円グラフや地図、表など色々ありますが、今までの経験ではヒストグラムと表以外はあまり実用上役に立ったことはありませんでした。他の表示方法で便利に使っているユースケースがあったらぜひ教えて下さい!
OmniDiskSweeperが便利
なんだかディスク容量がやけに少ないように思って内訳を確認したところ「その他」が肥大化していることがわかりました。
その他って何だって話で地道にduしたりしてたけど埒が明かないのでちょっと調べてみたらOmniDiskSweeperというツールを見つけました。OmniGraffleなどの高いツールで有名なOmni社の製品だけどこれは無料。
こんな感じでディレクトリごとに容量を表示してくれるのでどこに巨大なデータが眠っているのかすぐわかります。
今回は以前試しにmysqlにインポートしたWikipediaのデータが25GBもあったのが原因でした。もう不要なのでさくっとdropして解決。
edge rails(3.2.0.beta)に上げてみた
戯れに手元のrailsをmasterを見るようにしてみたらサーバが起動しなくなりました。
hamlのエラー
#gem 'rails', '3.0.9' # Bundle edge Rails instead: gem 'rails', :git => 'git://github.com/rails/rails.git'
$ bundle exec rails s => Booting WEBrick => Rails 3.2.0.beta application starting in development on http://0.0.0.0:3000 => Call with -d to detach => Ctrl-C to shutdown server Exiting /Users/y_310/work/sample_prj/vendor/bundle/ruby/1.9.1/gems/haml-3.1.2/lib/haml/template/patch.rb:16:in `alias_method': undefined method `delegate_template_exists?' for class `ActionView::Base' (NameError)
エラーをぐぐってみるとどうやらhamlのバグっぽい。
https://github.com/nex3/haml/issues/395
そしてこの問題はここですでに修正済。
https://github.com/nex3/haml/pull/398
ただ普通にgem installして入るバージョンは3.1.0でギリギリこの修正コミットは入っていないみたい。
仕方ないのでhamlもedgeを見るようにする。
gem 'haml', :git => 'git://github.com/nex3/haml.git'
rjsのエラー
hamlを最新にして再度サーバを起動しようとするとまたエラーが出ました。
=> Booting WEBrick => Rails 3.2.0.beta application starting in development on http://0.0.0.0:3000 => Call with -d to detach => Ctrl-C to shutdown server Exiting /Users/y_310/work/sample_prj/vendor/bundle/ruby/1.9.1/bundler/gems/rails-3b5eb11664b8/actionpack/lib/action_view/railtie.rb:34:in `block (3 levels) in <class:Railtie>': undefined method `debug_rjs=' for ActionView::Base:Class (NoMethodError)
これもググッてみるとこのページがヒットしました。
http://weblog.rubyonrails.org/2011/4/21/jquery-new-default
rails3.1で標準のJSライブラリがprototype.jsからjQueryになった際に、rjsもデフォルトで入らなくなったらしい。
rjsは必要ないので書いてあるとおりにconfig/environments/development.rbから以下の行を削除
# remove next line config.action_view.debug_rjs = true
これで無事にサーバが起動しました。