Quantcast
Viewing all 107 articles
Browse latest View live

複数行からなるログを解析するために、EmbulkのparserプラグインをRubyで開発する話(準備編)

最近、仕事の関係で、1個のログが1行〜複数行からなる特殊なログを解析する必要があり、Rubyでパーサを書く機会がありました。

しかし、そういえばこのパース処理ってEmbulkを使えばより簡単に作れて、かつ機能追加(パース結果をデータベースに入れるとか)が可能になるんじゃないか、と思い、parserプラグインRubyで開発する方法を調べてみました。

ちなみに、Fluentdのin_tailプラグインのmultilineで頑張って分析することも考えたのですが、先日のTreasure Data Tech Talkの懇親会で古橋さんに相談したところ、「パーサの処理が複雑なら、Embulkのparserプラグインを自分で書いたほうが楽ですよ」とアドバイスを頂いたので、今回はEmbulkで行くことにしました。

Embulkのインストール

私は、上記の動作環境で試しました。EmbulkのGitHubにあるQuick Start通りにインストールして、特に引っかかるところはありませんでした。

サンプルログの解析

同じくEmbulkのGitHubにあるTrying Examples通りにコマンドを実行すると、サンプルログを解析するための設定ファイルが生成されます。

embulk example ./try1
embulk guess   ./try1/example.yml -o config.yml
embulk preview config.yml
embulk run     config.yml

上記の手順通りに進めると、以下の様なconfig.ymlが生成されます。今回、独自のパーサを作るにあたっても設定ファイルは必要なので、このファイルをベースに作ります。

in:type: file
  path_prefix: /Users/myoshiz/devel/embulk-plugins/try1/csv/sample_
  decoders:- {type: gzip}parser:charset: UTF-8
    newline: CRLF
    type: csv
    delimiter:','quote:'"'escape:''skip_header_lines:1columns:- {name: id, type: long}- {name: account, type: long}- {name: time, type: timestamp, format:'%Y-%m-%d %H:%M:%S'}- {name: purchase, type: timestamp, format:'%Y%m%d'}- {name: comment, type: string}exec:{}out:{type: stdout}

独自のパーサを作る場合、parserパラメータ内のtypeは自分のパーサ名を指定するために必要、charsetとnewlineも後述するLineDecoderを使うなら必要、その他のパラメータはCSVパーサ固有のパラメータなので不要になります。

parserプラグインのひな形(Ruby用)の生成

embulkコマンドにはnewというパラメータがあり、色々なプラグインのひな形を作成できます。new以降の引数を与えないと、以下のように選べるカテゴリーが確認できるのですが、Rubyのほうはまだ未実装の部分が多いみたいですね。

happyturn% embulk new
2015-03-15 15:22:57.591 +0900: Embulk v0.5.2
Usage: new <category> <name>
categories:
    ruby-input                 Ruby record input plugin    (like "mysql")
    ruby-output                Ruby record output plugin   (like "mysql")
    ruby-filter                Ruby record filter plugin   (like "add-hostname")
    #ruby-file-input           Ruby file input plugin      (like "ftp")          # not implemented yet [#21]
    #ruby-file-output          Ruby file output plugin     (like "ftp")          # not implemented yet [#22]
    ruby-parser                Ruby file parser plugin     (like "csv")
    ruby-formatter             Ruby file formatter plugin  (like "csv")
    #ruby-decoder              Ruby file decoder plugin    (like "gzip")         # not implemented yet [#31]
    #ruby-encoder              Ruby file encoder plugin    (like "gzip")         # not implemented yet [#32]
    java-input                 Java record input plugin    (like "mysql")
    java-output                Java record output plugin   (like "mysql")
    java-filter                Java record filter plugin   (like "add-hostname")
    java-file-input            Java file input plugin      (like "ftp")
    java-file-output           Java file output plugin     (like "ftp")
    java-parser                Java file parser plugin     (like "csv")
    java-formatter             Java file formatter plugin  (like "csv")
    java-decoder               Java file decoder plugin    (like "gzip")
    java-encoder               Java file encoder plugin    (like "gzip")

examples:
    new ruby-output hbase
    new ruby-filter int-to-string

今回はRubyでパーサを書きたいので、次のようにコマンドを実行します。今回は、プラグインの名前を仮にmultiline-log-sampleとします。

happyturn% pwd
/Users/myoshiz/devel/embulk-plugins
appyturn% embulk new ruby-parser multiline-log-sample
2015-03-15 15:47:58.832 +0900: Embulk v0.5.2
Creating embulk-parser-multiline-log-sample/
  Creating embulk-parser-multiline-log-sample/README.md
  Creating embulk-parser-multiline-log-sample/LICENSE.txt
  Creating embulk-parser-multiline-log-sample/.gitignore
  Creating embulk-parser-multiline-log-sample/Rakefile
  Creating embulk-parser-multiline-log-sample/Gemfile
  Creating embulk-parser-multiline-log-sample/embulk-parser-multiline-log-sample.gemspec
  Creating embulk-parser-multiline-log-sample/lib/embulk/parser/multiline-log-sample.rb
  Creating embulk-parser-multiline-log-sample/lib/embulk/guess/multiline-log-sample.rb

parserプラグインのひな形(Ruby用)の動作確認

これが正しい方法かは分からないのですが、私がやってみて楽だった方法を紹介します。

まず、embulk bundleコマンドで、開発用のEmbulk動作環境を作ります。ここでは、開発用のディレクトリを、ひな形を作ったのと同じディレクトリ /Users/myoshiz/devel/embulk-plugins の直下に作成しました。

happyturn% pwd
/Users/myoshiz/devel/embulk-plugins
happyturn% embulk bundle ./embulk_bundle
2015-03-15 11:56:36.230 +0900: Embulk v0.5.2
Initializing ./embulk_bundle...
  Creating ./embulk_bundle/.bundle/config
  Creating ./embulk_bundle/embulk/input/example.rb
  Creating ./embulk_bundle/embulk/output/example.rb
  Creating ./embulk_bundle/embulk/filter/example.rb
  Creating ./embulk_bundle/Gemfile
Fetching: bundler-1.8.5.gem (100%)
Successfully installed bundler-1.8.5
1 gem installed
The Gemfile specifies no dependencies
Resolving dependencies...
Bundle complete! 0 Gemfile dependencies, 1 gem now installed.
Bundled gems are installed into ..

次に、ここで生成したディレクトリに、先ほど作成したひな形をコピーします。parserディレクトリは自動生成されないのですが、embulk_bundle/embulk/parserディレクトリを作り、ここにmultiline-log-sample.rbをコピーすれば動作します。

その上で、embulkコマンドの実行時に-bオプションでこのembulk_bundleディレクトリを指定すると、開発環境のEmbulkが使われます。例えば、次のようにembulk previewを実行できます。

happyturn% embulk preview config.yml -b ./embulk_bundle

ただし、この時点では、config.ymlのなかで、parserのtypeにmultiline-log-sampleを指定しても、読み込んだバッファの数だけダミーの分析結果(["col1", 2, 3.0])が返されるだけです。

2015-03-17追記

twitterで@hiroysatoさんに教えていただいたのですが、embulk_bundleディレクトリ以下にmultiline-log-sample.rbにコピーする必要はないみたいです。embulk previewコマンドの-Iオプションで、ロードパスにプラグインのlibディレクトリを追加するだけで実行できました(参考:How to develop embulk parser plugin)。

happyturn% embulk preview -I embulk-parser-multiline-log-sample/lib config.yml

embulk previewコマンドのヘルプはこんな感じ。

happyturn% embulk preview --help
2015-03-17 08:45:47.741 +0900: Embulk v0.5.2
Usage: preview <config.yml>
    -b, --bundle BUNDLE_DIR          Path to a Gemfile directory
    -l, --log-level LEVEL            Log level (fatal, error, warn, info, or trace)
    -I, --load-path PATH             Add ruby script directory path ($LOAD_PATH)
    -C, --classpath PATH             Add java classpath separated by : (CLASSPATH)
    -G, --vertical                   Use vertical output format

parserプラグインの開発

今回想定するログ

今回書くparserプラグインでは、以下の様なログを想定します。一応お断りしておくと、私が仕事で出くわしたログとは形式を変えて記載しています。

今回は、基本的には生成時刻とエラーレベルとエラーメッセージが出力されるものの、ERRORレベルのときにはスタックトレースが表示される場合もある、というログを想定します。また、スタックトレースについては無視するのではなく、その直前のERRORログの一部としてパースしたい、というニーズがあるものとします。

Dummy multi-line log

行単位のログ読み込み

自動生成されたMultilineLogSampleParserPluginクラスは、以下のrunメソッドでログを読み込み、その結果をpage_builderに書き込みます。

defrun(file_input)
        while file = file_input.next_file
          file.each do |buffer|
            # parsering code
            record = ["col1", 2, 3.0]
            page_builder.add(record)
          endend
        page_builder.finish
      end

で、あとはこの file.each do |buffer| 以降の処理を書き換えるだけ、と思ったのですが……このbufferには行の区切りを無視してログが書き込まれます。例えば、"2015-03-14 20:16:45, 183"という時刻の"2015-03-"までが前のバッファ、"14 20:16:45, 183"が次のバッファに分かれるといったことが普通に起こってしまいます。この結合を自前で行わなければいけないとしたら、ちょっと面倒ですよね……。

で、私はここで行き詰まってしまったのですが、LineDecoderというユーティリティを使えばこの結合を自動化出来るよ、と古橋さん(@frsyuki)にアドバイスしてもらいました。RubyのコードからこのLineDecoderを呼び出すやり方には(Javaで書かれたLineDecoderをJRubyから呼ぶために)少し癖があるのですが、古橋さんからサンプルコードも頂いて、なんとかLineDecoderを使うことができました。

以下は、LineDecoderを使って行単位でログを読み込み、行全体を1個のカラムに格納して返すパーサの例です。古橋さんからもらったサンプルコードと内容はほぼ同じですが、こちらはembulk newで自動生成されたコードも、比較のためにコメントアウトして残しています。

LineDecoder Usage Sample

ちなみに、今回行き詰まったときにコードを読んで確認しましたが、config.ymlに書かれたcharsetとnewlineはきちんとLineDecoder.javaに渡されます。

複数行からなるログのパース

ここまで来れば、あとは1行ずつログを読み込んでパースするrubyのプログラムを書くのと、何も変わらないかと思います。

次回は実際に、上記のログのパーサをparserプラグインとして書いてみて、そのコード量や、Embulkでパースすることによるメリットなどを整理してみます。とりあえず、パース結果をPostgreSQLサーバに登録できるようにするところが当面の目標かなと思ってます。

参考文献


複数行からなるログを解析するために、EmbulkのparserプラグインをRubyで開発する話(実践編)

前回の準備編からの続きです。

基本的には、準備編で用意したコードに、Rubyでパース処理を書き足すだけで、簡単に独自のparserプラグインを作れました。ただ、その際にいくつか調べた点や、自分で工夫した点があったのでご紹介します。何かの参考になれば幸いです。

今回のコード

今回書いたサンプルコードは、GitHubにアップロードしました。

muziyoshiz/embulk-parser-multiline-log-sample

中身はembulk new ruby-parserコマンドで生成したひな形の、ほとんどそのままで、編集したのはREADME.mdと、lib/embulk/parser/multiline-log-sample.rbのみです。今回は、手元にある特殊なログを解析する、というユースケースのため、guessの方は書きませんでした。

lib/embulk/parser/multiline-log-sample.rbを直接見たい方はこちら(↓)。

embulk-parser-multiline-log-sample/multiline-log-sample.rb

今回書いたパーサの解説

前回のおさらいになりますが、今回は以下のようなログが手元にあることを想定します。

  • 基本的には生成時刻とログレベルとエラーメッセージが出力される
  • ログレベルはINFO、WARN、またはERROR
  • ERRORレベルのときにはスタックトレースが表示される場合もある
  • スタックトレースについては、その直前のERRORログの一部としてパースしたい、というニーズがある

Dummy multi-line log

今回書いたパーサは、エラーレベルが出力された行から、次のエラーレベルが出力された行の1行前までを、ひとかたまりのログとして解釈します。そして、そのログの一部を、以下のカラムに出力します。

  • "time": 時刻
  • "log_level": ログレベル
  • "message": ログレベルの後ろに表示されるメッセージ
  • "second_line": 2行目のログ(スタックトレースの1行目を含む場合がある)
  • "third_line": 3行目のログ(スタックトレースの2行目を含む場合がある)
  • "caused_by": Caused by節に続くクラス名
  • "caused_by_message": Caused byのクラス名に続くメッセージ

カラムの定義は、self.transactionメソッド内で行います。今回は以下のようにしました。

        columns = [
          Column.new(0, "time", :timestamp),
          Column.new(1, "log_level", :string),
          Column.new(2, "message", :string),
          Column.new(3, "second_line", :string),
          Column.new(4, "third_line", :string),
          Column.new(5, "caused_by", :string),
          Column.new(6, "caused_by_message", :string),
        ]

カラムに使える型として、ひな形に含まれていた:stringと:integer以外に何があるのか分からなかったのですが、embulk/lib/embulk/column.rbに以下の記載があったので、この5種類かと思います。ドキュメントにも記載があるのかもしれませんが、ちょっと見つけられませんでした。

moduleTypeifEmbulk.java?
      defself.from_java(java_type)
        java_type.getName.to_sym
      enddefself.new_java_type(ruby_type)
        case ruby_type
        when:booleanJava::Types::BOOLEANwhen:longJava::Types::LONGwhen:doubleJava::Types::DOUBLEwhen:stringJava::Types::STRINGwhen:timestampJava::Types::TIMESTAMPelseraiseArgumentError, "Unknown type #{ruby_type.inspect}: supported types are :boolean, :long, :double, :string and :timestamp"endendendend

設定ファイル

独自のパーサなので特に設定も要らないのですが、以下のように、配列で指定したログレベルだけ出力するようにしてみました。log_levelsを書かなかった場合は、すべてのログレベルを表示します。なお、charsetとnewlineは、前回の準備編で紹介した、LineDecoderユーティリティが使う設定です。

in:type: any file input plugin type
  parser:type: multiline-log-sample
    charset: UTF-8
    newline: LF
    log_levels:["WARN", "ERROR"]

設定は、self.transactionメソッドの中で、

        task = {
          "decoder_task" => DataSource.from_java(parser_task.dump),
          "log_levels" => config.param("log_levels", :array, default: nil)
        }

のようにハッシュに入れてから、initメソッドの中で

@log_levels = task["log_levels"]

のようにインスタンス変数に格納して使うようです。

config.paramメソッドで指定できる型についても、ドキュメントは見当たらなかったのですが、:arrayが普通に使えました。恐らく、embulk/lib/embulk/data_source.rbにある以下の型が使えるのかな、と思います。もしこれが合ってるとしたら、カラムに使える型とは、微妙に違いますね。

  • :integer
  • :float
  • :string
  • :bool
  • :hash
  • :array

その他の工夫

コードを見てもらえればわかると思いますが、今回は複数行のログを1つのかたまりとして解釈するために、特殊な状態遷移をしています(もっと綺麗な書き方もあると思いますけど……)。

このコードを書くにあたって、以下のように書くとインデントが深くなってしまうので、

while decoder.nextFile
        while line = decoder.poll
          # do somethingendend

以下のように、ファイルの切れ目を無視して、とにかく次の行を読み続けるメソッドを作成し、decoder.pollの代わりにこちらを使いました。

# Return new line from any file or nildefread_new_line(decoder)
        begin
          line = decoder.poll
        rescue# At first time, java.lang.IllegalStateException is thrownendif line == nilreturnnilif decoder.nextFile == false
          read_new_line(decoder)
        else
          line
        endend

こういうニーズが多ければ、LineDecoderクラスの方に足しても良いと思いますけど、まあこれは特殊なニーズかも……。

embulk previewの実行

こういう感じでパースされます。バッチリですね。なお、この例では問題ありませんが、ログ出力場所と分析に使うマシンのタイムゾーンが違っている場合は、どういう設定が要るのか少し気になりました。

happyturn% embulk preview -I embulk-parser-multiline-log-sample/lib config.yml
2015-03-22 21:34:57.502 +0900: Embulk v0.5.3
2015-03-22 21:34:58.197 +0900 [INFO] (preview): Listing local files at directory '/Users/myoshiz/devel/embulk-plugins/testdata' filtering filename by prefix 'myproject.log.'
2015-03-22 21:34:58.201 +0900 [INFO] (preview): Loading files [/Users/myoshiz/devel/embulk-plugins/testdata/myproject.log.20150314]
+-----------------------------+------------------+----------------------------------------------------------------+----------------------------------------------------------------------------------------+------------------------------------------------------------+--------------------------------+--------------------------+
|              time:timestamp | log_level:string |                                                 message:string |                                                                     second_line:string |                                          third_line:string |               caused_by:string | caused_by_message:string |
+-----------------------------+------------------+----------------------------------------------------------------+----------------------------------------------------------------------------------------+------------------------------------------------------------+--------------------------------+--------------------------+
| 2015-03-14 11:12:22.123 UTC |            ERROR |                                              Book reader error | Exception in thread "main" java.lang.IllegalStateException: A book has a null property | at com.example.myproject.Author.getBookIds(Author.java:38) | java.lang.NullPointerException |                          |
| 2015-03-14 11:13:34.456 UTC |             WARN | Suspected SQL injection attack (input="; DROP TABLE USERS --") |                                                                                        |                                                            |                                |                          |
|     2015-03-14 11:16:00 UTC |            ERROR |                                            Indescribable error |                                                                                        |                                                            |                                |                          |
| 2015-03-14 11:16:45.183 UTC |            ERROR |                                              Book reader error | Exception in thread "main" java.lang.IllegalStateException: A book has a null property | at com.example.myproject.Author.getBookIds(Author.java:38) |          java.sql.SQLException |           Unknown reason |
+-----------------------------+------------------+----------------------------------------------------------------+----------------------------------------------------------------------------------------+------------------------------------------------------------+--------------------------------+--------------------------+

embulk runの実行

今回は、ログに対してSQL関数を使うことを想定して、PostgreSQLに出力してみます。まず、embulk-output-postgresqlプラグインをインストールします。

happyturn% embulk gem install embulk-output-postgresql
2015-03-21 17:49:18.942 +0900: Embulk v0.5.2
Fetching: embulk-output-postgresql-0.2.1.gem (100%)
Successfully installed embulk-output-postgresql-0.2.1
1 gem installed

適当にPostgreSQLサーバを用意してから、config.ymlにoutputプラグインの設定を書きます。

in:
  type: file
  path_prefix: /Users/myoshiz/devel/embulk-plugins/testdata/myproject.log.
  parser:
    charset: UTF-8
    newline: LF
    type: multiline-log-sample
    log_levels: [ "WARN", "ERROR" ]
exec: {}
out:
  type: postgresql
  host: localhost
  user: embulk
  password: ""
  database: embulk_test
  table: multiline_logs
  mode: insert

そして、embulk runを実行すると、PostgreSQLにデータが書き込まれます。CREATE TABLE IF NOT EXISTSのあたりでテーブルが自動生成されていることもわかります。

happyturn% embulk run -I embulk-parser-multiline-log-sample/lib config-psql.yml
2015-03-22 21:40:50.830 +0900: Embulk v0.5.3
2015-03-22 21:40:51.939 +0900 [INFO] (transaction): Listing local files at directory '/Users/myoshiz/devel/embulk-plugins/testdata' filtering filename by prefix 'myproject.log.'
2015-03-22 21:40:51.943 +0900 [INFO] (transaction): Loading files [/Users/myoshiz/devel/embulk-plugins/testdata/myproject.log.20150314]
2015-03-22 21:40:52.068 +0900 [INFO] (transaction): SQL: SET search_path TO "public"
2015-03-22 21:40:52.070 +0900 [INFO] (transaction): > 0.00 seconds
2015-03-22 21:40:52.072 +0900 [INFO] (transaction): SQL: CREATE TABLE IF NOT EXISTS "multiline_logs" ("time" TIMESTAMP, "log_level" TEXT, "message" TEXT, "second_line" TEXT, "third_line" TEXT, "caused_by" TEXT, "caused_by_message" TEXT)
2015-03-22 21:40:52.077 +0900 [INFO] (transaction): > 0.01 seconds
2015-03-22 21:40:52.123 +0900 [INFO] (transaction): {done:  0 / 1, running: 0}
2015-03-22 21:40:52.144 +0900 [INFO] (task-0000): SQL: SET search_path TO "public"
2015-03-22 21:40:52.145 +0900 [INFO] (task-0000): > 0.00 seconds
2015-03-22 21:40:52.146 +0900 [INFO] (task-0000): Copy SQL: COPY "multiline_logs" ("time", "log_level", "message", "second_line", "third_line", "caused_by", "caused_by_message") FROM STDIN
2015-03-22 21:40:52.232 +0900 [INFO] (task-0000): Loading 4 rows (623 bytes)
2015-03-22 21:40:52.234 +0900 [INFO] (task-0000): > 0.00 seconds (loaded 4 rows in total)
2015-03-22 21:40:52.234 +0900 [INFO] (transaction): {done:  1 / 1, running: 0}
2015-03-22 21:40:52.250 +0900 [INFO] (main): Committed.
2015-03-22 21:40:52.250 +0900 [INFO] (main): Next config diff: {"in":{"last_path":"/Users/myoshiz/devel/embulk-plugins/testdata/myproject.log.20150314"},"out":{}}

PostgreSQLに入ったので、こんな感じで集計できます。

embulk_test=# SELECT
embulk_test-#   to_char(time,'YYYY-MM-DD') AS day, log_level, message, count(*) AS count
embulk_test-#   FROM multiline_logs
embulk_test-#   GROUP BY day, log_level, message
embulk_test-#   ORDER BY day;
    day     | log_level |                            message                             | count
------------+-----------+----------------------------------------------------------------+-------
 2015-03-14 | ERROR     | Book reader error                                              |     2
 2015-03-14 | ERROR     | Indescribable error                                            |     1
 2015-03-14 | WARN      | Suspected SQL injection attack (input="; DROP TABLE USERS --") |     1
(3 rows)

あとは今回の例で言うと、パースした結果を眺めてみて、WARNの"input="以下の内容は別のカラムに分けて分析したい、と思うかもしれません。その場合は、恐らく一度multiline_logsテーブルを消すなり、自分でカラムを足してからembulk runを実行するなりしないとエラーになると思います。そこまではまだ試してないので、そのうち試してみます。

まとめ

こんな感じでパース処理の部分だけ自分で書き足せば、

  • gzip圧縮されたファイルの解凍
  • 複数ファイルからの入力
  • パース後のデータベース出力

などは、Embulkのプラグインを入れて設定ファイルを編集するだけで簡単にできました。主要アプリケーションのログのparserプラグインは今後色々出てくると思いますが、この機会に、手元にある独自ログのためのパーサを書いてみても面白いんじゃないでしょうか。

あと、今回は独自ログを対象にしたのでguessの方はサボってしまいましたが、いずれ機会があればそちらの方も書いてみたいと思います。

参考文献

EmbulkのLocalThreadExecutorプラグインに最大スレッド数を指定する方法

2015年4月7日にリリースされたEmbulk 0.6.0から、Executorをプラグインで差し替え可能になりました。このニュースリリースの中に、

The built-in executor plugin is LocalExecutorPlugin that runs tasks using multiple threads. It has a shared thread pool and schedules tasks at most (number of available CPU cores) * 2 tasks in parallel. Number of threads is configurable using max_threads system parameter.
http://www.embulk.org/docs/release/release-0.6.0.html:Release 0.6.0 — Embulk 0.6 documentation

とあったので、

exec:type: local
  max_threads:1

とconfigファイルで指定すればいいのか!と思ってやってみたら、さっぱりスレッド数が制限されず、さんざん悩んでしまいました*1

で、結論としては、以下のように-Jオプション(JRubyJVMパラメータを渡すためのオプション)を使って、embulk.max_threadsに値を設定することで、最大スレッド数が有効になりました。

% embulk -J-Dembulk.max_threads=1 run config.yml > result.txt

file inputプラグインの場合、1スレッドで1ファイルを読むようなので、以下の例ではresult.txtをtail -fとかで見てみると、確かに1スレッドで動作しているのがわかります。

こちら、今回も開発者の古橋さんにtwitterで教えてもらいました。いつもありがとうございます。なお、古橋さんによると、LocalThreadExecutorは、プロセスにつき1個あるグローバルなスレッドプールを直接使う実装になっているとのことです。そのため、0.6.0以前から最大スレッド数の指定自体はできたようです。あとになって見つけたのですが、以下のような記事もありました。

Embulkでタスクを処理するスレッドの数を変更する方法 - Qiita

古橋さんの解説

僕が最初に試したような感じで、プラグインの設定で最大スレッド数を指定できた方が自然な気がしますが、以下の理由で難しいそうです。

*1:余談ですが、type: localは有効なExecutorプラグイン名なので、この指定自体はエラーにならないようです。

EmbulkのfilterプラグインをRubyで開発する話

Embulkにはfilterプラグインという仕組みがあり、これを自作することで、Embulkで入力およびパースした結果を色々と加工することができます。例えば、「すべてのログにホスト名を追加する」といった、ログの種類によらない共通処理を定義するのに向いた仕組みです。

ただ、いろいろ試してみた結果、以前の記事で取り上げたような特殊なログを処理する場合でも、

  • パース処理の中で、一度完成したらほとんど直す必要がない基本的な部分 → parserプラグイン
  • 試行錯誤しながら、何度か直す必要がある部分 → filterプラグイン

と使い分けた方が、コードの見通しが良くなりました。個人的には、parserプラグインと同じくらいfilterプラグインも自作することが多そうなので、作り方をメモしておきます。

特定カラムに含まれる文字列を置換するfilterプラグイン

一例として、以下のようなCSVファイルを読み込む際に、comment列に含まれる「: account=(数字)」や「 by alice」という文字列は集計の邪魔なので削除したい、という場合を考えます。

id,account,time,comment
1,,2015-01-27 19:23:49,"login failure: account=alice"
2,,2015-01-27 19:01:23,"login failure: account=bob"
3,alice,2015-01-28 02:20:02,"login by alice"

まず、embulk newコマンドでfilterプラグインのひな形を作成します。

happyturn% embulk new ruby-filter myapp
2015-05-30 16:38:29.963 +0900: Embulk v0.6.10
Creating embulk-filter-myapp/
  Creating embulk-filter-myapp/README.md
  Creating embulk-filter-myapp/LICENSE.txt
  Creating embulk-filter-myapp/.gitignore
  Creating embulk-filter-myapp/Rakefile
  Creating embulk-filter-myapp/Gemfile
  Creating embulk-filter-myapp/embulk-filter-myapp.gemspec
  Creating embulk-filter-myapp/lib/embulk/filter/myapp.rb

コマンド実行直後のembulk-filter-myapp/lib/embulk/filter/myapp.rbのひな形は以下の通りです。

moduleEmbulkmoduleFilterclassMyappFilterPlugin< FilterPluginPlugin.register_filter("myapp", self)

      defself.transaction(config, in_schema, &control)
        # configuration code:
        task = {
          "property1" => config.param("property1", :string),
          "property2" => config.param("property2", :integer, default: 0),
        }

        yield(task, out_columns)
      enddefinit# initialization code:@property1 = task["property1"]
        @property2 = task["property2"]
      enddefcloseenddefadd(page)
        # filtering code:
        page.each do |record|
          page_builder.add(record)
        endenddeffinish
        page_builder.finish
      endendendend

まず、transactionメソッドのなかで、フィルタを通したあとのカラムを定義します。カラムを増減させないなら、in_schemaをそのままout_columnに代入するだけでOKです。initメソッドは、今回のプラグインはパラメータを取らないので空にします。

      def self.transaction(config, in_schema, &control)
        # 説明のために代入文を書いた。yieldメソッドにそのまま渡してもOK
        task = {}
        out_columns = in_schema

        yield(task, out_columns)
      end

      def init
      end

そして、フィルタのためのコードをaddメソッドの中に記載します。変数recordは配列なので、4番目にあるcomment列を参照したいなら、record[3]と指定する必要があります。

      def add(page)
        # filtering code:
        idx = 3
        page.each do |record|
          case record[idx]
          when /^login failure:/
            record[idx] = "login failure"
          when /^login by/
            record[idx] = "login"
          end

          page_builder.add(record)
        end
      end

あるいは、列の順序が変わる可能性があるのでどうしても列を名前で参照したい、という場合は、以下のようにpageからschemaを取り出して、comment列のインデックスを取得することもできます。

      def add(page)
        # find index of "comment" column
        columns = page.schema.select{|c| c.name == "comment" }
        idx = columns[0].index

        # filtering code:
        page.each do |record|
          case record[idx]
          when /^login failure:/
            record[idx] = "login failure"
          when /^login by/
            record[idx] = "login"
          end

          page_builder.add(record)
        end
      end

まあ、今度は逆にカラム名の変更に弱くなってしまうので、どちらが良いかは一長一短かと。大抵の場合はrecord[3]で良い気がします。

あとは、設定ファイルへ以下のようにfilterの名前を記載し、

filters:- type: myapp

embulk previewコマンドを実行すれば、フィルタが有効になっていることを確認できます。

happyturn% embulk preview config.yml -L embulk-filter-myapp
2015-05-30 17:20:16.564 +0900: Embulk v0.6.10
2015-05-30 17:20:17.351 +0900: Loaded plugin embulk-filter-myapp (0.1.0)
2015-05-30 17:20:17.365 +0900 [INFO] (preview): Listing local files at directory '/Users/myoshiz/devel/try1/csv' filtering filename by prefix 'sample_'
2015-05-30 17:20:17.369 +0900 [INFO] (preview): Loading files [/Users/myoshiz/devel/try1/csv/sample_01.csv.gz]
+---------+----------------+-------------------------+----------------+
| id:long | account:string |          time:timestamp | comment:string |
+---------+----------------+-------------------------+----------------+
|       1 |                | 2015-01-27 19:23:49 UTC |  login failure |
|       2 |                | 2015-01-27 19:01:23 UTC |  login failure |
|       3 |          alice | 2015-01-28 02:20:02 UTC |          login |
+---------+----------------+-------------------------+----------------+

サンプルコード

今回のサンプルはGistにもアップロードしました。

参考文献

Fluentd Meetup 2015 夏の参加レポート

イベントページ: Fluentd Meetup 2015 夏 - 2015/06/01(月) - dots.

Fluentd Meetupに参加してきました。6月上旬なのにタイトルは夏?と思わないでもないですが、まあすでに暑いですしね……。

イベント中にざっと講演内容をメモしてたので、せっかくですしブログに貼っておきます。

イベント全体の感想

v0.12の機能について断片的にしか知らなかったので、@repeatedlyさんの講演でそのへんの説明を改めて聞けて良かったです。Fluentdについては出たばかりの頃に使ったせいか、「複雑なことをやろうとするとタグの付け替えが大変」という印象が強かったのですが、FilterとLabelの機能がある今なら、だいぶ楽に使えそうですね。

あと、IoT向けFluentdであるFluent-Bitについて、開発者のEduardo Silva氏による講演もありました(今回の目玉?)。mBaaSのようなものは認識していたのですが、IoT機器からCPUなどのメトリクスやらsyslogやらを収集というのは個人的に盲点でした。まだvery early stateなプロジェクトということなので、今後に注目したいと思います。

他に気になった講演としては、@toyama0919さんによる、fluentdで収集したhttp requestをテストに利用する方法についての講演がありました。本番とテスト環境で全く同じデータベースを用意するのは難しいでしょうから、GETが多いアプリなど、うまく適用するにはいくつか前提条件がありそうに感じました。ただ、テスト自動化ができると便利なのは確かなので、更新系の少ないアプリなどで試してみたいところです。

以下、講演内容のメモです。

Fluentd v0.12完全解説 (@repeatedly)

  • はじめに

    • 今回は使っている人前提の話
  • v0.10 (old stable)

    • mainly for log forwarding
    • robust but not good for log processing
    • 日本だとほぼいろいろな分野に入った。Web、アドテク系、かなりの場所で使われている。
  • v0.12 (current stable)

    • v1を目標に開発している。今年の年末のリリースが目標。
    • v1のconfigurationをデフォルトでサポート
      • v1のすべての設定はサポートされていないので、ドキュメントを読んで欲しい
    • 4つの主要な追加機能
  • v1 configuration

    • hash, array, enum型の追加(JSONと同じ形式を受け付ける)
    • #{}でRubyコードを埋め込める
      • Socket.gethostnameでホスト名を取れる。これはよく使われている。
    • ログに、パスワードなどのセンシティブな情報を出力しないための :secret オプション
      • 通常は設定ファイルの内容がすべてログに出る
  • Filter

    • v0.10では、outputで擬似的にfilterを実装しなければいけなかった
    • TDの当初の利用では不要だったが、その後フィルタが多く必要になった
    • ユーザが多かったプラグイン2つと、追加で1つ入れている
    • Filter: record_transformer
      • record_reformerのfilter版
    • Filter: grep
    • Filter: stdout
      • ストリームをコピーしてout-stdoutに渡す必要がなくなった
    • ウィンドウの中の状態を管理したい場合、filterメソッドよりfilter_streamメソッドをいじると良い
  • Label

    • tagを駆使してmatchさせようとすると、tagの付け替えが煩雑になる
    • そういう場合はlabelでmatchさせることができるようにした
    • 注意事項:全プラグインは対応していない
      • Engine.emitをrouter.emitに書き換えれば、ラベル機能が使えるようになる
      • Engine.emitはdeprecated
    • Fluentdはchunkに分けてデータをまとめるが、output側に制約がある場合がある chunkの一部に失敗した場合、そのchunkをerror labelで抽出できる
  • at-least-one semantics

    • v0.10はat-most-onceをサポートしてた
      • 欠損の可能性あり
      • 広告など、パフォーマンスが求められる分野ではこちらの方が適していた
    • v0.12はat-least-onceをサポート(require_ack_response parameter)
      • 重複してでも、最低1回は送る
      • ユースケースに合わせて使って欲しい
  • HTTP RPC based management

    • CRubyで実装した関係上、いままではシグナルを使っていた。
    • Windowsなどに対応できるようにHTTP RPCを実装。
      • RESTにするかは議論があったが、難しかった
    • いまのところはFluentdで使っているシグナルを置き換えるRPCのみ持っている
    • inputプラグインだけを止めたい、という要望もあり、そのRPCが増えるかも
  • Almost ecosystems are v0.12 based

    • docs.fluentd.org はすべてv0.12ベースになっている
    • td-agentも最新版からv0.12ベース
  • Roadmap

    • v0.14を夏にリリース。ServerEngineで無停止で再起動できるようになる予定

fluentdで本番環境を再現する (@toyama0919)

スライド: 未公開?
関連記事: fluentdで本番環境を再現する - Qiita

  • 自己紹介

  • Shadow Proxy

    • productionのhttp requestを複製してバックエンドに送信するproxy
    • 目をつけた理由=Web系のテストが年々複雑化、本番のデータで落ちてしまう、等
    • 公開されているOSS
      • cookpad/kage の事例が目立っている
      • mod_mrubyを使う方法も考えたが、諸事情で断念した
    • kage:「クラウドセキュリティ」という本で紹介されている
  • Shadow Proxyを採用しなかった理由

    • フロントエンドにミドルウェアをあまり入れたくない
    • 事例が聞こえてこない(安全にやりたいが、難しい?)
  • fluent-plugin-http_shadow

    • 新たに開発した
    • フロントエンドに手を入れずにShadow Proxyを再現(http requestを復元)
  • パラメータについて、運用して分かった問題を交えて解説

    • rate
      • 本番と同じrequestを、stagingではさばけない
      • 最初は1%で運用し、徐々に上げていくのが安全
    • timeout
      • client側でbufferが詰まらないように設定
    • 並列数
      • http requestを並列で投げる
  • ユースケース

    • バグ発見機
      • 開発環境でとりあえず流しておくだけで結構バグが見つかる。重宝してる
      • ミドルウェアの更新時にも利用(OSSは特に更新頻度高い)
    • パフォーマンスの比較
      • 全く同一のhttp requestをbefore/afterの2台に投げる
  • 感想

    • 完全なshadow環境は難しい
      • POSTのパラメータ、Webブラウザ以外のアクセス
      • 意図しないデータの更新
    • それでも9割のアクセスはGETなので、テストとして有用

fluent-bit (Eduardo Silva氏)

  • 自己紹介

    • コスタリカから遠隔でTreasure Dataで働いている
    • 日本は今回が初めて
  • アプリケーションのログ収集は有用だが、今日は別の観点で話す: IoTs, embedded applications

  • New project: Fluent-Bit

    • 目的が違うので別の物が必要
    • IoTでもLoggingが必要
  • すでにFrameworkの標準化活動がある

    • IoTivity, AllJoyn, Brillo (by google)
  • Fluent Bitはまだvery earlier state

  • Requirement

    • lightweight, written in c, pluggable, integrate with fluentd
    • support custom inputs/outputs
    • binary serialization (MessagePack)
  • Features

    • Built-in system metrics
    • C API for developers (WIP)
  • システム構成

    • data source(センサ)から収集したデータを集めるraspberry piなどの中継点でfluent bitが動作する
    • fluent bitからstorage(s3など)に直接送信するパターンと、Fluentdを挟むパターン
  • 参考ページ

  • 質疑応答

      1. IoTデバイスでは、bi-directional communicationが必要では?
      1. Fluent-Bitはログ収集に集中。その先のロードマップは未定。

LT

fluentd対応MIDIキーボードを作ってみた (@kazunori_279)

  • 自己紹介

    • GCPのデベロッパーアドボケイト
    • Fluentdと連携するソリューションを社内外で提案した
      • Real-time logs analysis using Fluentd and BigQuery
      • GCPは本格採用し、google-fluentdというforkがある
  • Fluentd対応MIDIキーボードを作ってみた - Qiita

  • CPUを全く使っていない
  • FPGAで実装
  • TCPプロトコルを処理するチップを使用、1個2000円くらい
  • ハードウェアの設計をプログラミング言語でできるOSSがある(Javaで書ける)
  • FPGA触り始めて2年の素人だが、ここまで簡単につくれた

Docker and Fluentd (@tagomoris)

  • container、原則としてシャットダウンしたらデータは消える。外にデータを保存する必要がある。ネットワーク経由での転送が必要。
  • ログが混ざって、どこから来たかわからなくなるのは困る

  • Aggregation Pattern

    • 全コンテナから個別に集めるか、ホストごとに集約するか
    • どちらもメリット/デメリットあり
    • ホストごとに集約するほうが、変更に対してはロバスト(設定変更時は、ホストごとのfluentdを再起動すれば良い)
  • ホストごとに集約する方法

    • ネットワーク経由で送る
      • 性能面でのデメリットは特にない
    • container logger & tail
      • Kubernetesはこちらのモデルで設定が行われている
      • container自身のログも集約できるメリット
      • パース処理などがあるので性能面では遅くなる
    • Logging Driver "fluentd"
      • Docker v1.6からLogging Driverという仕組みが入った
      • --log-driver=fluentdのプルリクを送ってるのでv1.7.0には入るはず(syslogはすでにある)
  • Fluentdの入ったdocker imageを配布中

fluentdによる大規模キュー設計 (@edvakf)

  • 自己紹介

    • pixivの漫画事業の開発責任者(元々はpixiv自体の開発責任者)
  • 汎用ジョブキュー

    • Sidekiq, Resque, MySQL Q4M, MongoDB
  • pixivのアクセス解析で、キューに関する特殊な要件

  • 最初はMongoDBを考えたが、MongoDBがよく詰まる・落ちる

    • すべてのアクセスに挿入できるほど信頼出来ない
    • そこでFluentd(元々Apacheログの収集に使っていた)
  • ログをログファイルに出力し、Fluentdでバッチサーバに転送

  • 1分間に10万行のログ、とかあっても詰まらない

  • ファイルとしてバッチサーバに残っているので、やり直しが容易

    • 元に戻ってやり直せる
    • 失敗したら停止させて、バッチを直したらログの処理を再開
  • 転送先をACT/SBY構成にできる

大量のログをBigQueryに送ってみた (@catatsuy)

  • pixivでインフラを担当

    • 「pixivエンジニアが教えるプログラミング入門」という新書を出した
      • 誤解されがちだが、新人研修の内容をもとにしたもので、エンジニア向けではない
  • BigQuery

    • ストレージサーバーを容易する手間を省ける
    • すでにログを集めていたので、集約サーバーからBigQueryにログを送りたい→送れるようにした
    • データ送信の料金が問題
  • BigQueryにバッチでデータ送信すると、ストレージ料金しかかからない→バッチで安定的に送りたい

  • fluent-plugin-file-alternativeはgzip圧縮したファイルを保存できるので、これを使った
  • Jenkinsで送信。再送もJenkinsが担当
  • 1時間に1回うまく送るには、buffer_chunk_limitとbuffer_queue_limitの調整が必要

Norikra Meetup #2 参加レポート

Image may be NSFW.
Clik here to view.
f:id:muziyoshiz:20150604215341p:plain

イベントページ: Norikra meetup #2 : ATND

僕自身はNorikra使ったことないのですが、周囲には使っている人がいて評判も良いので、他の人のユースケースも聞いてみたい、あわよくば自分とこの業務にもねじ込みたい、と思い参加してきました。期待通り、ユースケースを色々聞けて面白かったです。

今回聞いたNorikraのユースケースには、大体以下のような共通点がありそうに感じました。

  • ステータスコードごとの件数を集計するために使っているところが多い
  • Norikraでの集計結果は、Fluentdを介して、他のツール(MackerelやらZabbixやら)に渡す
  • Norikraが落ちたら落ちたで諦めるという割り切りで使う(冗長化はcold standby程度)
  • 同じデータをデータベースにも流し込み、Norikraはあくまで速報値やアラート用に使う
  • メモリを大量に積んだマシンを用意すればなんとかなる
  • Gunosyの事例のように、分散型のシステム構成も取れる

個人的には@さんの、NorikraでSPAM検出する話が特に面白かったです。Webアプリケーション全般でこういうニーズはありそうなので、fluent-plugin-spam-reactor公開してくれたら嬉しいなあ……。

@さんの挨拶

  • 参加者へのアンケートの集計結果
    • Norikraを知っていますか? → 107 /119
    • 使ったことがありますか? → 34 / 119

メルカリでのNorikraの活用、Mackerelを添えて (@)

  • メルカリ1500万DL、急成長を支える運用

    • メルカリで運用系エンジニアとして働いている
    • PDCAをいかに早く、スムーズに回すかが重要
    • Zabbixも色々できるが辛い
  • DevOps

    • 開発者は直接SSH接続できないようになっている
    • 開発者は、過去のデータを見たい場合はtreasure data, bigquery, kibanaを見る
    • graphとalertも共有。Devも監視条件を指定したい
  • Norikra+Mackerel

    • Mackerel: 様々な時系列データを可視化・監視できる
      • MackerelのメインはAgentを入れてHost Metricsを取る(うちは使ってない)
      • clientからservice metricsを送ることもできる
    • Norikra+Mackerelで1ヶ月くらい使った感想として、しきい値設定が楽
    • アラートをSlackに通知(グラフも表示できる)
    • access_logとerror_logを、サンプリングせずに全件対象
      • 細かいエラーが取れなくなるのを避けるため
  • Norikraの運用

    • Jolokiaを有効にして、KuradoでJVMのmonitoring
    • Norikra自体は長い間使っているが、落ちる問題がある。同じようなことが起きてる人がいたら原因を教えて欲しい
  • 設定とクエリ

    • fluentdの設定で、Norikraのgroupを指定
    • HTTPステータスコード(2xxとか)ごとの件数を集計してグラフ化
    • Mackerelの良い所は、積み上げグラフのなかから、特定の種類のグラフだけ表示できる(件数が少ない5xxだけグラフ表示、など)
    • 特定UAからのアクセスをcountし、特定のUAからのアクセスがない場合にアラート発報している
    • 90,95,98,99パーセンタイルの平均値をグラフ化
      • flatten_hashに通してからでないと、mackerelが受け取れない
      • 平均値は、早いレスポンスに引きずられる。そのため、中央値やパーセンタイルで監視したほうが意味がある
  • Q&A

    • [Q] Kibanaと比べて良い所/悪いところ
      • [A] Kibana、グラフを多人数で見ると重たい。Mackerelは集約後のデータなので、グラフが軽い。エラーログの詳細はKibanaで見る。
    • [Q] Norikraの環境、オプション
      • メモリ64GBのマシン
    • [Q] クエリのタイムウィンドウはどれくらい?
      • [A] 基本1分。データとしてもう少し長く見たいのがあって、ログインのエラー回数。これはもう少し長くて5分とか。
    • [Q] Norikraの冗長化は?
      • [A] やってない

ログ解析にNorikraを導入する (@)

  • 自己紹介

  • Log ecosystem with Fluentd

    • Fluentdで集約→log storage→visualization/analytics(GrowthForecast, HRForecast, Spreadsheet, Tableau)
    • Pixivでの構成は、Google BigQueryのバッチ結果をHRForecastに書き込み、Jenkinsでバッチ実行
    • ログの転送はFluentdのおかげで綺麗になった
    • でも、データベースは1個に集約できず、複数使い分ける必要があるのが現状
  • 処理色々

    • バッチ処理
    • アドホック解析
      • Kibanaが流行って、みんなやり始めた
    • Offline Analysis
      • 日本にはExcel使いが多い
    • バッチ処理は時々重すぎる
    • 5分毎の傾向を知りたいとか、1日の傾向を知りたいとか、バッチに時間が掛かるので頻繁にクエリを投げられない
  • Norikra

    • スキーマレス、SQL-likeなクエリを書けるのが良い
    • status codeごとのカウント
    • ログをカウントして100件超えたら出力、とか
    • NorikraからBigQueryとかに渡すところをどうするか? → もう一度Fluentdに渡す → Fluentdに振り分けを任せる
    • Query Groupを、データを流す先ごとに作る。gfとかIdobataとか
  • Norikraの運用

    • NorikraのところがSPOF
    • Norikraのマシンは8GB以上のメモリを必要とする。メモリを大量に載せたほうが良い
    • CPUはさほどいらない
    • Supervisordでデーモン化
  • Norikra導入前

    • Fluentdの、似た名前のややこしいプラグイン2つ(fluent-plugin-numeric-monitor, fluent-plugin-numeric-counter)を使っていた
    • コンフィグに頑張って正規表現を書く、ということをやっていたが、fluentdの再起動が必要だったり、設定ファイルを配ったりが面倒
    • Norikraでやると楽
  • Norikraの監視

    • engine statisticsをMuninで出している
    • Norikraは安定しているので、監視にはあまり手が回っていない
  • Q&A

    • [Q] クエリ数は?
      • [A] 5〜6個。フィルタして秒間1000個くらいのイベントに適用(@kazeburoさんから、自分のところは11個あるとコメント)
    • [Q] タイムウィンドウは?
      • [A] 大きくするとメモリを食うので、すべて1分にしている

NorikraでWebサービスを守る (@)

  • 自己紹介

    • KAYAC
    • コミュニティ系のサービスにNorikraを入れている
  • Norikraの使い方

    • レスポンスコード別の件数をグラフ化
    • 10msec以下、100msec以下、ごとの件数をグラフ化
    • worker logsの集計
    • 元々はfluentdのプラグインで全く同じグラフを書いていた。裏側だけを切り替えて、見た目は全く変わっていない
    • ピーク時で10,000 msg/sec
    • ログ集計のためにログをRedshiftに入れて、それとは別にNorikra→Zabbixでグラフ描画
    • r3.xlarge (4core 30GB mem)を使い、75%くらいのメモリをNorikraに割り当てている
  • 背景

    • コミュニティ系のWebサービスだとスパマーが来る
    • 最初はスマホアプリonlyだったのであまり来なかった
    • Webバージョンをリリースしたらスパマーが来るようになった
  • 集計方法

    • 特定のURLに対してPOSTされた件数をhostごとにカウント
    • twitter経由でログインされた件数をhostごとにカウント
      • ペースを落としてログイン試行する奴がいるので、1分ごと、10分毎、1時間毎、で別のしきい値をかけている
  • fluent-plugin-spam-reactor

    • 内部で使っているだけなので未公開
    • 単純にNorikraに入れようとすると詰まることがある。aggregatorで詰まるとか
    • 詰まってからドカンと流れてきた時に誤検知しないようにしている
    • ログの最初の時刻と最後の時刻を差をとって、その時間内の1分あたりの件数を出すことで、誤検知を防ぐ
    • spam-reactorは、何度もひっかかったhostは、banする時間をexponentialに長くしている
      • 1時間banして、1分計測して再検知してban、とやっていると、再検知中の1分間は攻撃できてしまう
      • hostの情報はmemcachedに記録

GunosyのNorikraを用いたアドネットワークのリアルタイム集計 (@)

  • 自己紹介

    • Gunosy、ニュースだけじゃなくてアド事業もやっている
    • 広告配信関連の開発マネージャ
  • ユースケース

    • CTRや消化額、CPAの半リアルタイムフィードバック
    • 半リアルタイムの可視化・分析のニーズ(Kibanaとか)
  • 環境

    • アドネットワーク用アドサーバをgolangでリプレース
    • c4.large 1台あたり、1,000 req/sec
    • Norikraにはログの一部を投げている
    • Redshiftの運用は割と辛いので、定時バッチくらいにしたい
    • ログ量多くてKibana4に直接突っ込むと死ぬ→Norikraでさばきたい
  • 多段Norikra

    • 2〜3台のAPIサーバに対して、1台のfluentd+Norikraサーバ
    • でかいサーバを1台設置で良い? → アドテクでは、カジュアルに急にサーバが増えたりする。急激にトラフィックが跳ね上がる
    • 拡張性を残しておきたかった
    • 最終段のNorikraでサマった値を1分ごとにKibana4へ投入
    • 非エンジニアでも最低限必要な情報をドリルダウンできる
    • OpsWorksでNorikraのサーバ構築できる。設定はすべてcustom jsonにくくりだし
  • Norikra自体の監視

    • Datadogで監視
    • Papertrailで、FluentdとNorikraのログを監視
    • Datadogはiframeを埋め込めるので、Kibanaの画面も埋め込めて便利
  • 速報値はNorikraで集計、正確な値はRedshiftでバッチで集計

  • Opsworksで、一人でもアドサーバの監視・運用はできてる

Norikra Recent updates & Ask me anything (@)

  • suspended queries

    • 一時的に止めたいときに使う機能
    • 再起動したら消えてしまうクエリなので注意
  • NULLABLE fields

    • 一部のマシンのみカラムを増やした(バージョン混在)、といった場合に使える
    • そのカラムがNULLでも、集計対象にする、というための指定(デフォルトだとクエリに入っているカラムがないと、そのデータは無視される)
  • Listener

    • memory poolにためたものを、clientがfetchして使う
      • fluent-plugin-norikraはこれを自動で行うので、意識してない人が多いかも
      • 自動で行われているが、それでもfetchするぶんだけ性能に効いてくる
    • STDOUT() → Norikraのログに出てくる。クエリを試したいときに便利
    • fluentdに渡すlistenerの例
    • 自社のシステムに渡したいような場合も、listenerを書けば良い
  • Dynamic plugin reloading

    • 1.3からSIGHUPを受け取ると、gemでインストールしたプラグインをリロードしてくれる
  • Q&A

    • [Q] クエリ対象の文字列に "や 'が入っているとLIKE検索に失敗した
      • [A] Esperの検索は、Javaの文字列として解釈されるからかも? Issueに登録して欲しい
    • [Q] Dynamic plugin reloadingはFluentdに入らない?
      • [A] Fluentdの設定ファイルを安全にロードするのは難しそう。match節の増減などもあるため。範囲を限定すればできるかも

カラムを好きな場所に挿し込みたい人のためのembulk-filter-insertプラグイン

EmbulkのFilterプラグイン「embulk-filter-insert」を本日リリースしました。Filterプラグインと言いつつ、このプラグインを適用してもレコードは減らず、むしろカラムが増えます。Rubygemsで公開済みのため、インストールは以下のコマンド一発です。

$ embulk gem install embulk-filter-insert

このプラグインのインストール後に、例えば設定ファイルに

filters:- type: insert
    columns:- host: host01
      - service: service01
    at: top

と書くと、読み込んだレコードすべての先頭に、カラムが2個追加されます。カラムに含まれる値は固定値(この例では文字列"host01"と"service01")です。

ソースコードに興味のある方は muziyoshiz/embulk-filter-insertをどうぞ。

一体どうしてこんなプラグインを?

Embulkを使って突貫でデータを取り込もうとしたときに、ホスト名やログの種別を固定で付けたいことが、過去に何度かありました。例えば、Input Fileプラグインを使って myapp.log.2015-06-25.gz みたいなファイルを、あちこちのサーバから読み込むようなケースです。

いままでは、そのようなカラムの挿入処理をparserプラグインのなかに埋め込んでしまっていたのですが、再利用性を考えてFilterプラグインに切り出しました。インストールしておくと、たまに便利な場面があると思います。

Fluentdにも似たようなプラグインありませんでしたっけ?

@さんが公開してるfluent-plugin-record-modifierですね。僕は使ったことがないのですが、設定ファイルに

<filter pattern>
  type record_modifier

  gen_host ${hostname}
  foo bar
</filter>

のように記載すれば任意のkey-value pairを追加できて、

<filter pattern>
  type record_modifier

  # remove key1 and key2 keys from record
  remove_keys key1,key2
</filter>

のように記載すれば、指定したkeyの削除もできるみたいです。

ただ、EmbulkがFluentdと違うところとして、カラムの順序が重要なことと、型定義が必要なことがあります。

1. カラムの順序が重要

Embulkのinputプラグインで取り込んだデータは、配列としてparserプラグインやoutputプラグインに渡されます。そのため、例えば独自のparserプラグインを書いたあとで、大本のデータのカラムの順序が変わったりすると、(普通に書いてると)parserプラグインが動かなくなったりします。

また、この並び順は、最終的に人間がデータを見るときの並び順にもなります(output fileプラグインの場合など)。

そのため、カラムの挿入位置は、後続のプラグインや、見た目のことを考慮して変更できたほうが、色々便利そうです。

2. 型定義が必要

Fluentdの場合と違って、Embulkのカラムには型があるので、カラムを挿入するFilterプラグインには、挿入したいカラム名と値に加えて、型の情報も与える必要があります。かといって、挿入したい値を{ name: service, value: service01, type: string }のように書くのも冗長でめんどくさいし……。

そんなわけで、上記の情報を設定ファイルに書くことができて、なおかつ設定が自然文に見えてわかりやすくなるように、今回は挿入のみの単機能のプラグインとして実装しました。

例えば、カラムの順序と型定義を明示すると、以下のような感じになります。末尾に挿入する場合は順序(at)を、文字列を挿入する場合は型定義(as)をそれぞれ省略できます。

filters:- {type: insert, column:{user_id:1234567, as: long }, at: top }

余談

embulk-filter-insertと対になるプラグインとして、embulk-filter-removeプラグインというのも考えていました。以下のような設定で、指定したカラムを削除できるプラグインです。

filters:- {type: remove, column: user_id }

しかし、Ingressのイベントに参加するために仙台行ったりしてモタモタしてるうちに、@さんがsonots/embulk-filter-columnされていて、出遅れた感が……。

あと、前回のparserプラグインやらfilterプラグインを書いていて疑問だったのですが、プラグインのテストってどう書くのが流儀なんですかね。誰か知ってたら教えてください。

参考文献

テストコードのついているEmbulkプラグイン一覧(2015年6月版)

自分でも公開/非公開含めて何個かプラグインを書いてみたのですが、今のところテストコードは一個も書いたことないです。

とはいえ、Embulkのバージョンが上がったりしても使えるように、できるだけテストコードは書いておきたいし、どうせ書くならそのコミュニティの流儀に従って書いておきたい、と思って、GitHubにあるEmbulkプラグインのテストコードをざっと調べてみました。

以下は、現時点でList of Plugins by Categoryに掲載されているプラグインのうち、テストコードのついている全プラグインの一覧です。なお、embulk newコマンドが生成したままの、中身が空のテストクラスは除外しました。

最初はサンプルが見つかるまで探そうと思ってたんですが、予想外に(特にRubyの)テストコードが全然無かったので、つい全部調べて一覧にしてしまいました……。僕はRubyでプラグイン書いてるので、とりあえずembulk-input-sfdcを参考にすることにします。

Input plugin

Output plugin

Filter plugin

File parser plugin

File decoder plugin

File formatter plugin

  • なし

Executor plugin

  • なし

Pebble Timeは実用品かわいい

Image may be NSFW.
Clik here to view.
f:id:muziyoshiz:20150729001758j:plain

Pebble Time(白)買いました。1週間ほど使ってみて、ものすごく気に入ったので、勢いに任せて紹介記事を書いてみます。

Pebble Timeとは?

Pebble Timeはスマートウォッチです。Android WearやApple Watchみたいなものですが、それらとは違う、独自のOSで動いています。と言っても、AndroidとiOSの両方とペアリングできるようになっており、スマホにインストールしたPebble Timeアプリから、Bluetooth通信で、各種通知やスケジュールをPebble Timeへ飛ばすことができます。

Pebble TimeはPebbleというシリーズの最新機種で、その前には、2013年発売のPebbleや、2014年発売のPebble Steelがありました。ただ、これらの画面は白黒で、見た目がしょぼかったので、個人的には買う気になれずにいました。しかしPebble Timeからはカラー液晶になり、見た目がだいぶよくなったので、今回思い切って買ってみた次第です。

Pebble Timeは実用品

1週間ほど使ってみた感想としては、Pebble Timeはとにかく実用的だなあ、と関心しました。そう感じた点をいろいろリストアップしてみます。

Image may be NSFW.
Clik here to view.
f:id:muziyoshiz:20150728233648j:plain

Pebbleのボタンとその名称(Introduction To Pebble Timeより引用)
  • 「時計」と「スケジューラ」を中心に置いた基本機能

    • Pebble Timeは、通常時は、カスタマイズ可能な時計である「Watchface」を常に表示しています。
    • そして、downボタンを押すと未来のスケジュールを表示し、upボタンを押すと過去のスケジュールをさかのぼって表示します。このスケジュール表示機能は「Timeline」と呼ばれています。
    • 考えてみると、時計を見る時って、「次の打合せは何時だっけ?」とか何かの予定のことを意識してるんですよね。手元の時計でボタンを1個押すだけで、次の予定を確認できるというのは、思った以上に便利でした。予定の横にお天気マークが表示されてるのも、利用シーンに合っていて便利なポイントです。
  • Up/Downボタンの長押しによるQuicklaunch機能

    • Selectボタンで表示されるアプリ一覧とは別に、Upボタンの長押しと、Downボタンの長押しにアプリを設定できます。僕はDownの長押しに「Series Timer」という高機能なタイマーアプリ(ポモドーロタイマーとかを自分で定義できる)、Upの長押しに「Misfit」という歩数計アプリを設定しています。
    • スケジュールはUp/Downボタンを押せば見れるし、通知は勝手に画面表示されるので、自発的に起動するアプリってあまり無いんですよね。だから、登録できるアプリが2個でも、特に不便は感じないようです。
  • 明るいところでもよく見える画面

    • Pebble Timeは反射型カラー液晶を採用しており、ボタンを押したり、腕を動かした時だけバックライトがオンになります。時間を見るにはバックライトがオフの明るさでも十分で、野外だと(当たり前ですが)バックライトがない分、スマホとかより明るく見えるくらいです。
    • 僕はモーション機能をOFFにして使っています。消費電力が減りますし、ボタンを押せばバックライトが付く、という方がわかりやすくて好きなので。
  • 電池の保ちのよさ

    • これは使い方によるところが大きいと思いますが、Pebble Timeのページでは "Battery life up to 7 days"と公称されています。僕が使ってみている感じだと1日の電池消費は15〜20%で、少なく見積もっても5日は充電なしで保ちそうです。
    • ちなみに、Pebbleといえば電子ペーパー、という印象が強かったのですが、電子ペーパーじゃなかったんですね……。買って使ってみるまでずっと誤解してました。

その他にも、マグネット吸着式のケーブルで充電できる点や、リストバンドの手触りが良い点、防水(水深30m耐圧防水)である点なども、日常的に使う上ではかなりプラスに感じています。余談ですが、アプリはC言語で書けるらしいので、それもあとで試してみようと思ってます。

Pebble Timeはかわいい(確信)

まず、Pebble Timeの見た目から、他のスマートウォッチと比べて、丸みを帯びていてポップな印象を受けます。Pebble Timeは白、黒、赤の3色展開なんですが、赤もなかなか良さそうな感じです。

そんな見た目に加えて、Pebble Timeは、ボタンを押してメニューを表示したり、スケジュールを表示したりするたびに、いちいちアイコンがアニメーションします。文章だと説明しづらいので、とりあえずwatchfaceとtimelineを表示してるところを撮影した動画(1分弱)を作りました。動画中でも喋ってますが、「DIN Time」というwatchfaceを使っています。

こんな感じで、このビット絵のアニメーションがまた、なんか良い意味でチープでかわいいというか、まあ、うまく言えませんがかわいいんですよね……。使っているだけでちょっとテンションが上がります。個人的には、Pebble Timeの魅力の半分くらいが、このUI/UXなんじゃないかと思ってます。

でもお高いんでしょう?

Pebble Timeは日本未発売なので、欲しければ個人で輸入するか、Amazonなどで並行輸入品を買うことになります。お値段は、Kickstarterのキャンペーン時に買えばもう少し安かったのですが、僕は後からPebbleのサイトで買ったので $199 + $25(速達の送料、オプション)でした。これに、1,000円(輸入内国消費税等) + 1,080円(立替納税手数料)が追加されて、実際に払った金額は3万円くらいでした。

42,800円〜のApple Watchよりは安いですが、Android WearはMoto 360とかなら2万円台で買えてしまうので、値段だけで言うとちょっと負けますね。まあ、そのへんは実用性とかデザインとかIngressができるかどうかとかの好みで判断でしょうか。

日本のAmazonでも買えるようなのですが、本日時点で白/黒が36,800円、赤だけ37,800円とのこと。面倒事が嫌い or すぐ欲しいなら、Amazonも選択肢に含めて良いかもですね。ただ、Pebbleのサイトで直接買うのもそんなに難しくないので、急がなければそちらが良いかと。

最後に注意:実用品ですが完成品ではありません

最後に大事な注意点を1つ。Pebble Timeは日本語未対応なので、有志が公開してくれている日本語言語パッチを入れないと日本語を表示できません。なお、この日本語言語パッチは、Androidとペアリングしないとインストールできません。iPhoneと一緒にPebble Timeを使う場合も、一度Androidとペアリングして、日本語言語パッチをインストールする必要があります(一度インストールしてしまえばAndroidは不要)。

また、7月にリリースされた最新のファームウェア(3.2)と、日本語言語パックの相性が悪いようで、非公式日本語コミュニティなどで不具合が報告されています。実際に、僕も以下のような不具合に遭遇しました。

  • 標準のアラームアプリ(Alarms)で、設定した時間になると、本来ならバイブするはずのところが、OSが再起動されてしまう。
    • Alarms++というアプリを代わりに使うことで回避可能。
  • 設定画面で、「QUICK LAUNCH」を選んだ瞬間に、OSが再起動されてしまう。
  • Morpheuzという睡眠管理アプリを起動して数秒後に、OSが再起動されてしまう。
    • 僕が使った範囲では、強制再起動するアプリはこれだけだったけど、他にも同様のアプリがあるかも?

色々書きましたが、いくつかのアプリを避ければ普通に使えてます。とはいえ、人柱になる可能性もある製品なので、その点がどうしても気になる人は避けたほうがよいと思います。ちょっとでも面白そうだなーと思った人は、是非Pebble Timeを買って、僕と一緒に人柱になりましょう!

まだ日本のユーザ少なそうなので、ユーザがもっと増えて、公式に日本語対応して欲しいんですよねえ……(本音)。

あわせて読みたい

Pebble Timeのアプリをもっと知りたい方はこちらもどうぞ。
Pebble Timeのswim.comアプリで泳いだ距離を自動的に記録する - 無印吉澤

GitHub Wiki (personal plan)を個人用Wikiとして使ってみた話

Image may be NSFW.
Clik here to view.
f:id:muziyoshiz:20150819012711p:plain:w600

個人的な読書や勉強のメモを取るツールとして、かなり昔は紙copiEvernote、ここ数年はEmacs + howmを使っています。howmはとても良いツールなのですが、いくつか不満があり、有料でもよいからクラウドサービス型のWikiに移行できないものかと考えていました。

僕は元々GitHubのpersonal plan (micro)を契約していたので、とりあえず夏休みに一週間ほどGitHub Wikiを試してみました。これからまだ使い込んでみるつもりですが、まずはファーストインプレッションを公開してみます。同じようなニーズを持っている方の参考になれば幸いです。

ちなみに、GitHub Wikiを評価するにあたっての比較対象は、自分がそれなりに使ったことがあるPukiWiki、MediaWiki、Redmine内蔵のWikiなどになります。

今までの方法

Emacsと、howm(Hitori Otegaru Wiki Modoki)というEmacs-lispの組合せでメモを取っています。また、howmフォルダをDropboxで共有することで、自分が持っているすべてのPCやスマホでメモが同期される状態にしています。

howmの長所はとにかく、以下のように、Emacsから離れずにメモの新規作成から閲覧・検索までできるところです。

  • C-C , cでメモ用の新しいテキストファイルを新規作成できる。メモをすぐ書き出せる。
  • C-C , sですべてのメモに対する全文検索を実行できる。
  • 文中に[[タイトル]]のように記載すると、自動的にハイパーリンクのような表示に変わる。この上でEnterキーを押すと、そのタイトルを持つテキストファイルに移動できる。
  • テキストファイルなので(基本はRDですが)記載の形式は自由で、他のツールへの移行も容易。

その一方で、Emacs + howmの組合せで長年使っているうちに、以下のような短所が気になってきていました。

  • 全文検索がどんどん遅くなる
    • 当たり前の話ですが、テキストファイルの数が増えるにつれて、全文検索は遅くなります。
    • [[タイトル]]のリンクも内部的には全文検索なので、同様に遅くなります。そのため、ある時期から、Wikiのような使い方は全くしなくなってしまいました。
  • 一度書いた情報の再利用・再発見が難しい
    • テキストファイルをカテゴライズしたり階層構造を持たせることができないため、メモ全体を俯瞰するのが難しいという欠点があります。
    • howmはテキストファイルの作成は容易ですが、その分、単なる走り書きと、まとまった内容の文章が混在しやすいという傾向があり、これも問題を悪化させています。
  • スマホからの閲覧性が低い
    • Dropboxアプリをインストールすれば、スマホからメモを見ることはできます。しかし、「2015-08-18-084840.txt」のようなファイル名が並んでしまうので、過去のメモを探すのはほぼ不可能です。
  • Emacsに依存しすぎている
    • 長所と表裏一体なのですが……。比較的新しいテキストエディタ(AtomやSublime Textなど)を試そうと思っても、最も頻繁に使っているツール(howm)がEmacs依存なので、あまり真面目に試す気になれずにいました。

そこで、走り書きのようなメモは今まで通りにhowmを使うとしても、ある程度まとまった内容のメモは個人用Wikiに移行できないか、と考えました。

GitHub Wikiを個人用Wikiとして使うための設定

リポジトリの作成

まず、GitHubでMicro以上のpersonal planを何かしら契約して、privateのリポジトリを作成します(参考:料金プラン)。非公開にする必要がなければ、Freeで大丈夫です。その上で、このリポジトリのソースコード管理機能は無視してWikiだけを(ToDoが欲しければIssueも)使う、というのが基本的な使い方です。

サイドバーおよびフッターの編集

GitHub Wikiは、そのままでは若干使いづらいので、サイドバーとフッターをカスタマイズします。それぞれ、鉛筆アイコンをクリックすれば編集画面に移動します。

まず、サイドバー(_Sidebar というファイル)に、よく使うページへのリンクと、それぞれのページの編集画面へのリンクを追加します(参考:【知ってた?】GitHubのWikiにサイドバーを追加する方法 | Act as Professional)。GitHubは絵文字に対応しているので、編集画面へのリンクは、文字で書くよりも鉛筆の絵文字などにしたほうが見栄えが良いでしょう(参考:Emoji cheat sheet for GitHub, Basecamp and other services)。

例えば、サイドバーに以下の内容を記載すると、

* [[Home]] [:pencil:](https://github.com/muziyoshiz/docs/wiki/Home/_edit)
* [[Reading]] [:pencil:](https://github.com/muziyoshiz/docs/wiki/Reading/_edit)
* [[Piled Books]] [:pencil:](https://github.com/muziyoshiz/docs/wiki/Piled-Books/_edit)
* [[Event Reports]] [:pencil:](https://github.com/muziyoshiz/docs/wiki/Event-Reports/_edit)
* [[Tips]] [:pencil:](https://github.com/muziyoshiz/docs/wiki/Tips/_edit)
* [[Problems]] [:pencil:](https://github.com/muziyoshiz/docs/wiki/Problems/_edit)
* [[Impression of GitHub Wiki]] [:pencil:](https://github.com/muziyoshiz/docs/wiki/Impression-of-GitHub-Wiki/_edit)

以下のような表示になります。

Image may be NSFW.
Clik here to view.
f:id:muziyoshiz:20150819012146p:plain

GitHub Wikiは画面上部にしかメニューを表示していないので、フッター(_Footerというファイル)には、画面上部に戻るためのリンクを記載します。以下のように記載すると、画面先頭へのリンクになります。

<ahref="#start-of-content">Back to Top</a>

本当は「Edit」ボタンなどをフッターに直接記載したかったのですが、方法が分からなかったため、今回はこのような方法で妥協しました。

検索プラグインの導入

詳細は後述しますが、なんと、GitHub Wikiには全文検索機能がありません。そのため、全文検索を行いたい場合は、WebブラウザにGitHub Wiki Searchをインストールする必要があります。Chromeの場合はWebストアからGitHub Wiki Searchをインストール可能です。 Firefoxの場合は、Userscripts.orgからGreasemonkeyスクリプトが配布されているようですが、このサイトはずっと落ちているようで、ミラーサイトからダウンロードするしかないようです。

ファーストインプレッション

一週間ほど使ってみて、元々期待していた機能に対する評価は次のような感じになりました。評価は、「満足」ならA、「不満だが実用になる」ならB、「実用にならない」ならCとしました。

期待していた機能 評価
全文検索 B-
情報の再利用・再発見 B-
スマホからの閲覧性 B-
Emacs依存からの脱却 A

まあ、全体的にギリギリ及第点という感じですね……。GitHub Wikiは、GollumというWikiエンジンを使って作られているようなのですが*1、Gollumよりも機能が削られているようです。細かく見ていきましょう。

全文検索

  • Wiki全体を対象にした検索ができない
    • GitHub Wiki自身に全文検索機能はない(!)ようです。ヘッダの検索バーで "This repository"を対象にしても、Wikiの文章はヒットしませんでした。
    • サイドバーで、ページタイトルに対するインクリメンタルサーチは使えます。
    • 前述のGitHub Wiki Searchを使えば全文検索可能ですが、1個ずつページを読み込んで検索しているようなので、ページの数に比例して遅くなると思われます。また、スマホからの検索には使えません。

情報の再利用・再発見

  • ページにカテゴリをつける機能がない

  • ToCを自動生成できない

    • 個人的にはサイドバーにToCを表示したかったのですが、そういうことはできないようです。
    • gollum Wikiによると、Gollum は [[_TOC_]]という文法で目次を埋め込めるようなのですが、GitHub Wikiでは使えませんでした。
  • 最近編集されたページ一覧を、サイドバーに自動表示できない

    • 割と一般的な機能ですが、GitHub Wikiには無いようです。サイドバーの"Pages"欄は、ページ名がアルファベット順に並んでいるだけです。
  • 画面左右の空白が大きく、入力・表示できる範囲が狭い

    • 閲覧性という意味では、これも気になりました。

スマホからの閲覧性

  • GitHubのスマホ版ページにはWikiへのリンクがない

    • これも使ってみてびっくりしたのですが、Webブラウザでアクセスした時のメニューに、Wikiが出てきません。WikiのURLを直接指定すると、PC向けのWebページが表示されます。
  • スマホから全文検索できない

    • スマホのWebブラウザに上記の拡張機能をインストールできないため、検索はできません。サイドバーからページタイトルに対する検索はできるので、見たい情報を探せないこともないですが……。

その他、一般的なWikiに期待する機能など

  • 画像やファイルを挿入するのが面倒

  • 特定の段落だけ選んで編集することができない

    • ページの末尾を編集したい場合に不便。
  • Issueなどから、WikiNameだけでリンクすることができない

    • GitHub内蔵のWikiなので、Issueなどとも連携していると思いきや、そんなことはないようです。
    • Adding links to wikis - User Documentationには [[Link Text|WikiLink]] 記法が使えるとありましたが、Issueのなかでは使えませんでした。
  • WikiからIssueなどに自動的にリンクされない

    • 逆も同様。例えば、 #2 でIssue 2番にリンクされることを期待したのですが、そうはなりませんでした。

まとめ

色々と書きましたが、GitHub Wikiを個人用Wikiとして使う場合、以下の点は大きなメリットとしてあると思います。特に、好きなエディタで編集できる、というのはGitベースのWikiならではのメリットです。

  • GitHubのアカウントさえあれば、自分でサーバを立てる必要もなく、すぐ使える。
  • インターネットに繋がりさえすれば、どこからでもアクセスできる。
  • git clone して、好きなエディタで編集できる。
  • デフォルトのMarkdown以外にも、選べる記法が多い。

しかし、自分でサーバを立てる気があるならGollumの方が良さそうだし、他に良いサービスがあれば乗り換えない動機はないかな、というのが正直な感想でした。まあ、GitHub Wikiは、あくまでレポジトリのおまけで、別にGollumのクラウドサービス版として提供されているわけではないですしね……。

個人用Wikiの代替案として、なにかよいサービスなどありましたら是非コメントお願いします。

*1:GitHub WikiとGollumの関係について、GitHubにもGollumのページにも、明確に書いてあるところが見つけられませんでした。この理解で正しいのでしょうか? 自分が探してみた範囲では、wicketstuff/core Wikiに "The Github wiki is implemented using github/gollum."との記載がありました。

embulk-filter-insert 1.1.0リリース(Liquidで環境変数を挿入できるようになりました)

Embulkが0.6系から0.7系にメジャーバージョンアップされて、色々と機能が増えたのに加えて、APIにも変更が加わったようです。

Twitterで↑のお達しがあったのを見かけて、過去に作ったembulk-filter-insertプラグインが動かなくなっていないか、一応確認してみました。何も変更しなくてもEmbulk 0.7系で動きました……。おそらく、大したことしてないプラグインだからだと思います。

プラグインのmigrate手順

とはいえ、一応念のため、リリースノートにあるembulk migrateサブコマンドを実行しておきました。

happyturn% embulk selfupdate
2015-08-19 23:28:14.239 +0900: Embulk v0.6.18
Checking the latest version...
Found new version 0.7.1.
Downloading https://dl.bintray.com/embulk/maven/embulk-0.7.1.jar ...
Updated to 0.7.1.
happyturn% embulk migrate embulk-filter-insert
2015-08-19 23:50:55.789 +0900: Embulk v0.7.1
Detected Ruby plugin for unknown Embulk version...
  Created /.ruby-version
  Modified /embulk-filter-insert.gemspec
Done. Please check modifieid files.

embulk migrateサブコマンドで変更があったのは、上記の2ファイルだけでした。非互換のAPIは特に使っていなかったみたいです。.ruby-versionはjruby-9.0.0.0の指定、gemspecは以下の行が追加されただけでした。

   spec.add_development_dependency 'embulk', ['~> 0.7.1']

あとは、以下の手順でRubyGems.orgに公開。

% bundle install
% bundle exec rake build
% bundle exec rake release

すでにインストール済みの方は、以下のコマンドでアップデートしてご利用ください。

% embulk gem update embulk-filter-insert

Liquid(テンプレートエンジン)の恩恵

Embulk 0.7系から、設定ファイルが Liquid(テンプレートエンジン)対応になり、設定ファイルに環境変数を埋め込めるようになりました。設定ファイルの拡張子を .yml.liquid にして、以下のように{{ env.環境変数名 }}と記載すると、その箇所が環境変数に置き換えられます。

Release 0.7.0 — Embulk 0.7 documentation

Enambed Liquid template engine. If configuration file name ends with .yml.liquid, embulk embeds environment variables using Liquid template engine.

embulk-filter-insertとの関係で言うと、例えば環境変数にホスト名を埋め込んでおけば、共通の設定ファイルで、そのホスト名をカラムに挿入できるようになりました。

filters:- {type: insert, column:{host:{{ env.HOSTNAME }}}, at: top }

以下のようにホスト名を設定し、

happyturn% export HOSTNAME=happyturn

previewコマンドを実行すると、先頭のカラムにホスト名が入ります。便利ですね!(僕は何もしてませんけど)

happyturn% embulk preview embulk-simple-test/config.yml.liquid
2015-08-20 00:51:22.255 +0900: Embulk v0.7.1
2015-08-20 00:51:23.330 +0900 [INFO] (preview): Loaded plugin embulk-filter-insert (1.1.0)
2015-08-20 00:51:23.346 +0900 [INFO] (preview): Listing local files at directory '/Users/myoshiz/devel/embulk-simple-test/csv' filtering filename by prefix 'sample_'
2015-08-20 00:51:23.351 +0900 [INFO] (preview): Loading files [/Users/myoshiz/devel/embulk-simple-test/csv/sample_01.csv.gz]
+-------------+---------+--------------+-------------------------+
| host:string | id:long | account:long |          time:timestamp |
+-------------+---------+--------------+-------------------------+
|   happyturn |       1 |       32,864 | 2015-01-27 19:23:49 UTC |
|   happyturn |       2 |       14,824 | 2015-01-27 19:01:23 UTC |
|   happyturn |       3 |       27,559 | 2015-01-28 02:20:02 UTC |
|   happyturn |       4 |       11,270 | 2015-01-29 11:54:36 UTC |
+-------------+---------+--------------+-------------------------+

embulk-filter-insertプラグインの詳細は、過去記事をどうぞ。

プラグインの使い方などの紹介記事:カラムを好きな場所に挿し込みたい人のためのembulk-filter-insertプラグイン - 無印吉澤@HatenaBlog

YAPC::Asia Day 1 レポート 〜 Miiverseのデプロイ手法、HTTP/2、Electronなど

Image may be NSFW.
Clik here to view.
f:id:muziyoshiz:20150822194424j:plain

YAPC::Asiaの2日チケット(Tシャツあり)を購入して、1日目参加してきました。僕が聞いたセッションはこちら。

ベストトーク賞(1日あたり2件、計4件投票できる)は、★付きの講演に投票しました。この2講演を中心に、メモしていた内容を共有します。

ちなみに、大規模でも小中規模サービスでも捗る microservices な Web サービスのつくりかた (Kazuhiro Osawa)スライド) も聞きたかったんですが、立ち見も含めて満席で入れませんでした。残念……。

講演内容

メリークリスマス! (Larry Wall)

  • Perlの生みの親であるLarry Wallが、Perl 5〜Perl 6の開発にかかったこれまでの15年について、J. R. R. トールキンがHobbitのプロットを一部踏襲・一部改善してLord of the Ringを執筆したことになぞらえて話したセッション。
  • セッションの最後には、Perl 6が、「今年の」クリスマスまでにはかなりの確度でリリースできると発表し、会場の喝采を浴びていた。

世界展開する大規模ウェブサービスのデプロイを支える技術

  • Miiverseのインフラを担当するはてな社員、アプリを担当する任天堂社員による共同発表

    • 中澤亮太(はてな): これまでにはてなブログ、はてなブックマーク、Miiverseを担当
    • 灘友良太(任天堂): Miiverseを担当
  • Miiverse

  • 今日の講演は、Miiverseをデプロイする、主に「成果物配布」の部分

  • 従来の成果物配布方法

    • Capistrano2系+Gitを利用。いわゆるPull型の手法
    • 複数リージョン:海を超えたgit pullつらい問題
      • 対策:各リージョンにgit slaveを立てる
    • デプロイ対象が数百台規模:Gitサーバがgit pullに耐えられない問題
      • 対策:ロールごとにデプロイ、かつホストごとにランダムスリープ。MackerelとCapistranoで同じロールを設定し、デプロイ対象はMackerelのAPIで取得
    • それでも残る問題
      • lsyncdのディレクトリ監視数上限
      • rsync途中でgit fetchしてしまい、ホストごとにリビジョンが異なる
  • 2つのGitリポジトリ

    • Redmine+GitHub Enterprise (GHE)(主にコードレビューと、一部のタスク管理)
    • デプロイ用とコードレビュー用、2つのリポジトリを使っている
      • git push先が2個
        • git push ghe master
        • git push origin master
      • GHEのmerge pull requestは使わない運用
    • 不便なので、2つのGitリポジトリを同期したい
  • 既存の同期ツールの検討

    • google/hesokuri
      • Google製のGit同期ツール。リリース時に同期を止めたい、などのMiiverseでのニーズに合わない
    • ghm
      • 3年くらい前からはてな社内で開発・運用しているツール(第2回 GREE Tech Talkで紹介)
      • ghmの導入を当初検討したが、問題あり。Miiverse開発チームで使うには、並列化されていなく、遅い。同期が終わる前にデプロイして、古いリビジョンがデプロイされたことがあった
    • そして、新リポジトリ同期システムを開発することに
  • 新リポジトリ同期システムの開発

    • JSON over HTTPなAPI群
    • 改善された同期アーキテクチャ
      • スケーラビリティ
      • 保守性
    • masterからgit pullし、その結果をslaveにgit pushするシンプルなツール
    • GHEのWeb Hookで、同期開始。どのリポジトリを同期するかは、DBから取得
    • 同期ジョブは、複数リポジトリに対して同時に実行可能
  • これから採用しようとしている新しいデプロイ手法

    • Git によるPull型の一般的な問題
      • リリースするものをすべてcommitする → リポジトリの肥大化
      • 各マシンでコンパイルする → 細かい環境の差異で成果物が異なる、ビルドが長くなる
    • Consul + stretcher
      • Amazon S3経由で成果物を配布し、そのイベントをconsulが拾って、stretcherがデプロイ
      • Gitサーバの負荷が解決すると共に、Gitリポジトリには成果物を含めなくて良くなる
      • 成果物の作成〜S3へのアップロードはJenkinsで実行
      • 従来 1142秒 → 29秒に短縮(carton installありで210秒)
  • Q: 新同期システムの言語は?

    • A: Perl5。MiiverseおよびサブシステムもPerl5で開発されているため。手慣れた言語を選択。
  • Q: デプロイの頻度
    • A: 基本的に2週間に1回の頻度。バグがあればhotfix。
  • Q: 新同期システムで、1台だけ失敗した場合の検出はどうする?
    • A: stretcherでは、成功時に実行するコマンド、失敗時に実行するコマンドをマニフェストに書ける。評価時は失敗したらikachanを通じてIRCに通知していた。
  • Q: AWSのCodeDeployを検討した?
    • A: してない。開発時にはまだリリースされてなかった。時期的な問題。

[★]HTTP/2時代のウェブサイト設計 (奥 一穂)

DeNAで、HTTP/2対応のWebサーバ "H2O"を開発している奥 一穂さんによる講演。

  • 背景:バンド幅を増やしてもページロード時間は短くならない。しかし、レイテンシが短いほど短くなる。

    • その裏付けとなるデータ
    • バンド幅の時代は終わった。今はレイテンシが問題
    • HTTP/1.1は多重性がない。同時6本のTCP接続を使うのが一般的だが、それでも遅い。
    • HTTP/1.1パイプラインがある→問題があって使われていない
    • 国内で東京につなぐだけなら10msくらいのレイテンシだが、LTEだと50msくらいのレイテンシ。アメリカまで光ファイバーで往復して80ms。遅くなっている。というのが背景知識
  • HTTP/2の技術要素

    • バイナリプロトコル
    • 多重化
    • ヘッダ圧縮
    • 優先度制御
    • サーバプッシュ
  • バイナリプロトコル

    • 第1の目標は脆弱性を防ぐこと。パーサによる解釈の差(プロクシとサーバで違う、など)を突く攻撃がいっぱいあった。
    • 転送データ量の低減。ヘッダ圧縮する→ボディも圧縮していいのでは。
    • h2iコマンドを使ったデモ(HTTP/2を手で試すことができる)
  • 多重化

    • 同時に100以上のリクエストを発行可能。
    • DATAのstream ID
  • ヘッダ圧縮

  • 優先度制御

    • 重み付け、依存関係をクライアントが指定する。
    • Firefoxの場合:first-paintが大幅短縮。headタグにあるCSS、JSを優先取得したあとで、画像などを取得。body末尾のJS(Google Analyticsなど)は別の優先度で取得。
    • Chromeの場合:依存関係を使っていないので、あまり速くならない。
    • Edge、Safariは優先度制御を全くしていない。サーバ側でなんとかしないと速くならない。
    • サーバ側のreprioritize-blocking-assetsオプションで、優先度制御対応。
    • 実装が悪いために、パイプラインのように実用にならない可能性がある。
  • サーバプッシュ

    • サーバ側から事前にプッシュすることで0 RTTに。H2O, nghttpxが対応。
    • 問題:CSSやJSをプッシュしたい。しかし、ブラウザキャッシュにある場合はプッシュしたくない(プッシュしたら却って遅くなる)。
      • cache-aware server-push: H20 1.5の新機能。fingerprint(キャッシュしてそうか)をクッキーに添付。
  • HTTP/2ではレイテンシではなく、バンド幅が再びボトルネックになる → Webサイトの作り方が変わる。

  • HTTP/2でオワコンになる最適化

    • アセットの結合(asset pipeline)
    • expiresの利用
    • ドメインシャーディング
      • 優先度制御できなくなるので、かえってfirst-paintが遅くなる。
      • CDNを使う場合→最近はHTTPレスポンスもCDN経由で送れるようになっている。
  • HTTP/2にするとクリック数が増える→収益が上がる!

  • H2OはHTTP/2が一番速いWebサーバ

    • HTTP/2はHTTPS前提。Let's Encryptが11月16日からサービス開始予定(無料でサーバ証明書をもらえるようになる)
    • Forward Secrecy
      • session ticketをオフにしないと、forward secrecyにならない
      • H2OはWebサーバクラスタでのresumptionに対応。session ticketの秘密鍵の自動更新機能を実装。
  • Q: REST APIなどのサーバ間通信のためにHTTP/2を使うなら、クライアントライブラリは何を使えばいい?

    • A: HTTP/2に対応したクライアントライブラリは、現状1個しかない。libcurl。
  • Q: first-paint timeの計測方法。Chromeについては計測機能がないのでは。
    • A: timing chartで、最後のCSS、JSを受信した時刻をfirst-paint timeとみなした。
  • Q: HTTP/2はいつ頃普及するか?
    • A: Webブラウザ、Firefox、Chrome、Edgeは対応済み。Safariも秋には対応する。サーバ側も秋にはNginxが出る。HTTPSにならないと恩恵が得られない。LetsEncryptが出てこないと。今はHTTP/2が(なにの?)1割。うまくいけば、年末までに3〜4割に上がるのではないか。

Conway's Law of Distributed Work (Casey West)

  • PivotalおよびCloud Foundryに所属。10年ほどリモートワークをしている。

  • Effective Communication Saves Time

    • リモートワークでは透明性がデフォルトで必要
    • 意思決定の理由を記録する必要がある
      • 例えば、boolean型を使うべきと思われるところで、なぜboolean型を使っていないのか? 意思決定されたときの当時の情報が必要になる。
  • Tools

    • Text Chat
      • 4 primary aspects: archive, group chat, private chat, full participation
      • Slack, HipChat, IRC, Google Hangout
    • ChatOps
      • lita (ruby), hubot (nodejs)
    • audio/video communication
      • headphones, microphone, camera: よい設備重要
      • HipChat, Google Hangouts, appera.in, sqwiggle
    • screen sharing
      • Screenhero (for Slack), HipChat, join.me, WebEx
  • Development Tools

    • Commit Messages
      • critically important
    • Code review
      • Pull Requests, Gerrit, ReviewBoard, Code Collaborator
      • 一時的に開発のスピードを落とすが、欠陥を見つける、などの理由で結果的に早くなる
    • Digital Progress Board
      • Pivotal Tracker, Trello, GitHub Issues, waffle.io for GitHub Issues
      • カンバンスタイルが好き。Jiraは好きじゃないのでリストに入ってない。
        • 質疑応答の時間に、個人的な経験上、Jiraを使う場合はワークフローをカスタマイズしすぎないほうがよい、という話があった。
    • Collaborative Writing
      • Etherpad, Hackpad, Google Docs, GitHub Wiki
      • 共同編集できて、編集結果を誰でも見られるツール
    • File Storage
      • Dropbox, Box.net, Google Drive, NFS
    • Shared Calendar
      • Google Calendar, Exchange
    • Email
      • collaborationのための良いツールではない。非同期。テキストチャットのほうが良い選択肢。
  • Techniques

    • Everyone should experience remote work. 経験してみないと、リモートワークする人の気持ちはわからない
    • Have on-site meetups.
    • Invite remote workers into hallway conversations.
      • 難しい。ビデオチャットなどで。
    • Over communicate.
      • 情報を定期的に共有することが必要
    • Share your personality.
      • テキストチャットでoff topic areaを作って、そこでやりとりする。音楽を共有する、など。
    • Exhibit a "visible pulse."
      • リモートワークでは、自分の状態を知らせる必要がある
    • Pick a timezone. Standard Operating Time (SOT)
      • チャレンジング。打合せの設定などが困難。
  • Sociotechnical Theory

    • リモートチームにもCAP定理が成り立つ。2つしか選べない。
  • Conway's Law

    • Organizations produce designs which are copies of the communication structures of these organizations.
    • The health and quality of your product will be a direct reflection of the health and quality of your organization.
    • 組織構造がプロダクトデザインに反映される、というコンウェイの法則。組織の健康や品質も、同様にプロダクトに反映される。

[★]Electron: Building desktop apps with web technologies (Ben Ogle)

  • GitHubでAtomを担当。最近Atom 1.0をリリースした。

  • Atomはデスクトップアプリ

    • 操作は通常のデスクトップと同様だが、Web技術のうえに作られている。
    • AtomはWebブラウザで出来ないことができる。ファイルアクセスやクリップボードアクセスなど。これは、Electronの上に作られているから。
    • Electron = Chronium + io.js (node.js)
  • Electronで作られているアプリ

    • GitHub Atom, VS Code, Facebook Nuclide
    • JIBO: 家庭用知能ロボ
    • Slack (for Windows)
    • Mojibar (絵文字を検索するツール)
    • その他の事例は http://electron.atom.io/にあり
  • Electronアプリの構成

    • コマンドでひな形を作成できる
    • Browser Process, Render Process, Entry Pointから構成される
    • KittyDetect: 猫の顔を検出するアプリ。既存のライブラリ(kittydar)を流用して、2時間で書いた。
    • Curve App: ベクタ描画のアプリ。
  • perlでElectronアプリを作ることもできるはず。perlをJavaScriptに変換するツールがあるので、これでできるか試してみてほしい。

    • すでにPerl challengeでやってみた人がいる?

esa.io - 趣味から育てたWebサービスで生きていく (Atsuo Fukaya)

  • esa.io - Expertise Sharing Archives for motivated teams.
  • Qiita Teamのトライアル期間が終了して、有料になったのをきっかけに開発開始
  • 自分のプロジェクトや、知人のプロジェクトで使ってもらって手応え。去年の11月にesaの運営会社を作った。
  • esaじゃない方の会社を今年2月にやめた。有料で使ってくれる人が思ったより増えたので。
  • weekly active members(1週間に何かしらの編集をしたユーザ): 1583

Lightning Talks (Aug 21)

  • Norikraで作るPHPの例外検出システム (Masahiro Nagano)
    • (※TODO: スライド公開されたらリンク張る)
    • PHPではエラーと例外は別物
    • 補足が難しい例外がいくつかあり、設定が必要
    • 発表者の環境では、最終的に4つのログファイルにエラー出力されるようになった
    • ログファイルを fluentd → Norikra → Slack で表示

YAPC::Asia Day 2 レポート 〜 Mackerelの開発言語、ペパボのデプロイ高速化など

Image may be NSFW.
Clik here to view.
f:id:muziyoshiz:20150822194440j:plain

引き続きDay 2も参加してきました。昨日のDay 1のレポートはこちらをどうぞ。

Day 2に聴講したセッションはこちら(↓)。ベストトーク賞(1日あたり2件、計4件投票できる)は、★付きの講演に投票しました。

全体通しての感想

YAPCは今回初参加だったのですが、Perlは全然使ってない僕のような人間でも十分楽しめました。講演の採択率が非常に低いということもあって、どのセッションもレベルが高かったですね。

全体を通して、ソフトウェアを活用した効率改善・自動化を全力でしている、という事例が多くて、とても刺激になりました。最近、自分が担当した業務では、引き継いだシステムをなんとか解析して、手順書を作るくらいで力尽きてしまっていたのですが、もっと自動化していかないとダメだなあ、と。特にペパボの事例は、既にあるシステムをガンガン改善していく事例として、見習わなきゃいけない、と思いました。

あと、YAPCで一番驚いたこととしては、Consulってこんな普及してたんですか? いや、完成度の高いツールだとは思っていたんですが、利用する場面ってもっと限られたツールだと思い込んでいました。一度使ってみたほうがよさそうです。

以下、個人的に興味を持った講演中心のメモです。

講演内容

[★]Mackerel開発におけるScalaとGo、そしてPerl (Masayuki Matsuki, @songmu)

スライド: Mackerel開発におけるScalaとGo、そしてPerl

Mackerelで複数のプログラミング言語を、どのような基準で使い分けているか、という講演。タイトルからして大人気講演だと思うけど一番狭い部屋か……と思って行ってみたら、案の定大混雑でした。

  • Mackerel

    • はてなの社内ツールを前身に、作りなおしたツール
    • エンジニア6人、デザイナー1人、インフラ1人、スクラム開発(1スプリント2週間)
  • 技術スタック

  • Scala

    • サーバサイドにPlay! Framework (Scala)
      • ゲームほど頻繁にリリースするわけではないので問題ない(それでも毎週リリースはしてる)
      • Javaも良いと思うが、社内で関数型を書きたい人がいた
    • Optionは便利
    • コンパイルは遅くて、だいぶストレスはある。ただ、クリティカルなバグは混入してないので、固いものを作れるのでは
    • テストが遅い。型チェックがあるので、小さいテストを書き過ぎない、マインドチェンジが必要
    • 仕様が膨大すぎて破綻しかかっているのが可愛い → 無限に勉強できる
      • 例:アンダースコアを省略できる箇所とできない箇所
    • 大きい物をかっちり作るには向いている(はてブもリニューアル部分はScala)
  • Go

    • シングルバイナリが吐ける → セットアップが簡単
    • マルチプラットフォーム対応が比較的容易
    • フットプリントが小さく、監視対象のパフォーマンスへの影響小さい
    • 事例
      • mackerel-agent(ユーザのホストにインストールする常駐プロセス)
      • mkr(Mackerel操作のためのコマンドラインツール)
      • URL外形監視ワーカー(URL外形監視用のジョブキューシステム。キューにはRedisを使用)
      • blogsync(はてブロ用のクライアント。はてブロの記事をMarkdownしてgitで管理)
    • 利用しているモジュール
      • Graceful Restart: 2つのモジュールの組み合わせで、perlアプリと同様の運用ができる
      • Goの常駐プロセスの監視: golang-stats-api-handler
    • 開発中に、Perlモジュールの思想を受け継いだモジュールをいくつか書いた
    • 型がありつつ、コンパイルが早いので嬉しい。LL的なリズムで開発できる
    • 並行・並列処理が言語組み込み
    • クロスコンパイルが良いと言われる → 実際ちゃんとやろうとすると大変
      • 特にWindows
      • パッケージングが大変(rpm/deb/msi)
    • Goの使いドコロ
      • コマンドラインツール
      • microservicesを実現するコンポーネント
      • 大きすぎるものにはマッチしない。書き捨てにも向いてない印象
  • JavaScript

    • (スライドすっとばし)
  • Haskell

    • 入社していきなりJavaScriptのプロファイラ書いたitchyny++
  • Perl

    • 事例
      • mackerel-agentのリリースの自動化
        • GitHub, Travis, AppVeyor(msiのコンパイルに使用)
      • Mackerelに関するCPANモジュール
        • MackerelのAPIクライアント、Mackerelのwebhookを受け付けるWebサーバ
    • プロジェクトの渋い脇役:だいたいどこでも入っている
      • シェルスクリプトで書くくらいならperlでやりたい
    • 自動化スクリプトにツールを同梱できる
    • 柔らかいアプリケーションサーバレイヤとしても使える(はてブの顧客向けAPサーバ部分に使おうとしてる)
      • 全部Scalaでやるのは大変。修正からデプロイまでに時間がかかってしんどい
  • 自動化機構は負債になりやすい

    • 肥大化してブラックボックス化 → メンテ不能
    • 1枚のperlスクリプトの中にテストも書く(perlから起動したら実行、proveから起動したらテスト)
    • ツールの責務を分ける(1個のツールは1個のことを)
  • リリース自動化の解説

    • GitHubのtoken(権限大きすぎる)を使う代わりに、Deploy Keyを使った
  • OSSにするときに気をつけていること

    • ユーザーがハックする余地を残す
    • pull requestは公平に扱う
    • レビュー体制、CIやリリースをオープンにする
  • Ruby

    • 事例
      • fluent-plugin-mackerel
      • cookbook-mackerel-agent
      • Capistrano
      • git-pr-release
    • fluentdやChefなどのエコシステムに乗っかるときに使う
  • We are hiring

    • 東京オフィスのエンジニアがここ1年で 1人(songmu) → 6人 に。

我々はどのように冗長化を失敗したのか (Kenji Naito, @kenjiskywalker)

  • 挑戦したこと:式年遷宮インフラストラクチャ
  • ACT/SBY → いざというときに入れ替えても動かないことが多い
    • 普段から定期的に入れ替えておけばどうか?
  • RedisのMaster/Slave構成で式年遷宮
    • Redis Sentinel:3台必要
    • マシン3台用意したくない → Masterに1プロセス、Slaveに2プロセス動作させて回避
    • Redisが遷移したことをWebサーバに知らせるには? → ConsulでDNSレコードを返すルールを設定
  • MySQL: MySQL 5.6(GTID) + mysqlfailover
    • mysqlfailoverで、遷移実行前後に処理実行 + Consul
  • Consulに重要な情報が集まりすぎてSPOFに → consul-template でConsulが管理する情報をhostsに書き出し
  • 失敗
    • ステージング環境で発生せず、本番環境のみ発生した問題いくつか → ステージング環境と本番環境は同一にしよう
    • 開発スケジュールがタイトだったこともあり、自動切り替えの仕組みは止めてからリリース(いざとなったら手作業で切り替え)

MySQLで2億件のシリアルデータと格闘したチューニングの話(斉藤 健二, @saiken3110)

  • DNPデジタルコムに3年ほど所属
  • システムの概要
    • 商品を買うとシリアルコードがついてきて、それをWebから入力するとポイントが付く
    • CPU 2コア、メモリ 8GBのマシンで2億件(顧客要望で、マシン増強はできず)
  • データ登録、負荷テストは70分で終わったが、本番は75時間かかった
    • 負荷テストのときは主キーはシーケンスで連番、本番はランダムな数字
    • innodb_buffer_poolが枯渇した時点からinsertが大幅に遅延
    • 本番でも、主キーをシーケンス、ユニークキーをシリアルに変更してみた。75時間 → 30時間 に短縮
  • update(ステータスの更新)も遅かった
    • indexの再構築がかかるため、遅くなったと思われる
    • ステータス管理をやめ、管理テーブルを作成し、update自体を不要にした。60分 → 45秒に改善
  • まとめ
    • 負荷テスト大事(実際のデータに近いデータで)
    • 負荷テストは開発のなるべく早めにやっておいたほうがよい

[★]3分でサービスのOSを入れ替える技術 (SHIBATA Hiroshi)

  • 自己紹介

  • 2014-11-xx

    • @antipop: 3ヶ月後に某サービスでCM打つのでバーンとやってほしい
    • Railsでできたサービス。当時はAPサーバ6台。用途不明のサーバもあるような状態。
    • 元々担当者じゃなかったけど、なんとかしてほしいと頼まれた。
    • Golden ImageからVMを作り、設定変更すればVM増やせるが、1台あたり4〜6時間かかる状態
      • 刺身たんぽぽ
    • 課題:これをスケールアウトできるように
    • チームメンバ: hbst, udzura, yano3(全員フルスタックエンジニア)
  • 最初はPuppet化

    • Puppetを使い始めていたが、本番投入されてなかった
    • まず、Puppetを本番投入できるように、サーバマニフェストを全部書き換えた。ppファイル約5500行。
    • puppetmasterdを構築
      • ペパボでは7〜8割のサービスでPuppetを利用中
  • Check point 0

    • ここまで実施して、状態をコードで表現できるようになった
    • この時点では bootstrap time = 4-6hr
  • SSHを使わない(No SSH)

    • SSHを使わない、という方針にすることで、自動化が進む
    • 無駄が起こりうる作業を一旦やめてみることで、イノベーティブな方法が思いつく
  • cloud-init

    • AWS、OpenStackなどを使っている人は、絶対使っているはず
    • ドキュメントが非常に少ない。コードを読んだりして対応。
    • cloud-initでできることはなるべくここに入れる
    • ただし、runコマンドは使わない(runコマンドでやりたいことはpuppetでやる)
  • Rails

    • Rails 3から4にアップグレード
    • キャンペーンを打ってからではアップグレード難しいため、準備段階でアップグレード(アップグレードだけで30分話せるくらい話題がある)
    • Railsのコードから、IPアドレスやホスト名が埋め込まれている箇所は、IaaSが提供するAPIから取得するように変更
  • Check point 1

    • まずは目の前にある手作業を、自分たちの使えるツールに置き換えて自動化する(いきなりDockerとか入れたりしない)
    • bootstrap time = 1hr
  • Capisrano 2をCapistrano 3に書き換え

    • rails bundle。これを使いたいからCapistrano 3にした。
    • build serverで作ったtarballをS3にアップロードし、アプリケーションサーバはS3からダウンロード
  • cloud-initのなかで、ダウンロードしてインストール

  • Consul + consul-alertで死活監視

    • 元々Nagiosを使って死活監視をしていた。Nagiosは人間が入力した設定に基づいて動作する前提。オートスケールに向かない。
  • Mackerel

    • muninはdynamic scaleに対応していない
    • ペパボはmackerelを全社で使っている
    • 開発時には、Mackerelから自動でleaveする機能がなかった。initscriptのなかに、shutdown時にleaveするAPIを呼び出すようにしていた。
      • 2015-07-31にmackerel-agentにauto-retire実装された。
  • td-agent

    • access_logの収集
  • thor

    • メインコマンド、サブコマンド、引数、という構造のCLIを簡単に作るためのライブラリ
  • Check point 2

    • cloud-oriented architecture
    • 手作業でやりそうになったら、それをすべてコードに落とす
    • Bootstrap time = 20-30min
  • bootstrapを遅い作業と早い作業に分類して並列化

    • フェーズを分ける: Minimal image (phase 1), Role specified (phase 2)
  • Packerを使った

    • JSON形式でインスタンスの設定
    • cloud-initも入れられる
    • provisionerというシェルスクリプトも
    • 工夫した点:provisionerを複数実行できるので、最後にserverspecの実行を入れている
  • Infra CI

    • Drone CIというものを使って実現している
    • "nyah"で構築したOpenStack環境上で、Drone CIを動作
  • Puppet manifestのリファクタリング

    • serverspecで自動化されているので、不要なパッケージを削除したりできるようになった
  • Scientific Linux 6からCentOS 7への移行

  • Check point 3

    • Bootstrap time = 3-5min
  • Blue-Green Deployment

    • 超つよいルータの下にぶら下がる→超重要→ELB
    • データベースはまだBlue-Greenにしてない
  • Check point 4

    • Bootstrap time < 3min
  • 次のステージ

    • イメージ作成の全自動化
    • mutableなサーバをどうするか?
    • イメージ作成にはどうしても15分くらいかかるが、10分間隔など頻繁にリリースする場合はどうするか?
    • ここまで来たらDockerを使ったほうが良い?
  • Q: データベースをBlue-Green Deploymentにするアイディアについて

    • A: ノーアイディア。開発者が開発しやすい環境にするほうを重視したほうが良いかと考えている。
  • Q: スケールアウトの指標は?

    • A: 人間が判断して、余裕のある台数まで増やしている。将来的には自動化したい。

ソーシャルゲームにおける AWS 移行事例 (@tkuchiki)

  • 事例1:冒険クイズキングダムの移行

    • RDS for MySQLの検証に一番時間がかかった
      • タイムゾーン設定
      • failover
    • レプリケーションは、time関数を使っていると9時間ずれる→mysqldumpで対応
    • レプリケーションが遅かった。209分。これを90分まで時間短縮
  • 事例2,3:僕らの甲子園!熱闘編のMobage版とiOS/Android版の移行

    • Kyoto Tycoonをyrmcdsに移行
      • yrmcds(よるまくど)はサイボウズが作っているセッションストレージ
    • Consul lockを活用したフェイルオーバーシステムを作った
      • ネットワークの問題かなにかで、Consulが数秒Leader lostすることがあった
      • 移行後2日連続でfailoverした → 現在この機能は使っていない
    • ZabbixとJenkins Slaveの話は時間の都合でスキップ
  • 知見

    • 10数台規模のシステムであれば、2〜3名程度で1ヶ月程度あれば移行できることがわかった
    • AWS移行は、構成を見直す良い機会になる

辛いことをやめる!から始まる業務改善とInfrastructure as Code (Yuichiro Saito, @koemu)

個別具体のツールに関する話ではなくて、組織内で新しいツールを導入する際の、アプローチに関する話でした。

  • 自己紹介

    • ハートビーツのなかで、組織全体の効率向上する仕事を担当
    • 大企業で言うと、標準化部門のようなところ
  • 背景

    • 2012年、AWSなどのIaaSを使う案件が増えてきた
    • リードタイムがほぼ0になり、サーバの調達が劇的に変わってしまった
    • それでも手作業で構築していた → 残業時間がこの時期はとても長くなっていた
    • 日々の仕事に忙殺されて、新しい仕事もできなくなってきた
  • 改善に向けた活動

    • まずは、現状と理想のギャップを認識する("goal-reality" pairs)
    • 敵を作ってでも問題解決しよう、という態度は取らない
    • "Inner Work Life": 知的労働者はモチベーションによってパフォーマンスが左右される
    • 作業の種類:頻繁に繰り返す短い作業、1回だけ行う長い作業
    • ストレス(難易度)とコストの2軸で、作業を分類
      • アンケートやヒアリングの結果を元にまとめた
      • 例えば、監視設定はストレスもコストも高い
  • 必要な人を巻き込む

    • hb-agent, hb-gendoc, Cacti bulk configuration tool, hb-acnsを作っている
      • 今日はhb-acnsの話をする
    • Nagios, Cactiに設定を自動登録するツール
    • 設定はpythonスクリプトとして書くように、わざとしている。プログラム経験が低い人に、プログラムに慣れさせるため
    • CTOの了解をもらって、パイロットプロジェクトを開始。お墨付きがあることをCTOから全社に宣言してもらう
    • 少しでも動かない、「あれっ」という声が聞こえたら飛んで行く
    • ドキュメントを残す、Tipsを大量に書く(Sphinxで書いている)
    • ハンズオンを何回もやる
  • 効果測定する

    • 開発3ヶ月
    • 監視設定のリードタイムは、10分の1以下に下がった
    • 自分の毎月の残業は10時間以下 → 新しい案件、新しい技術に向かえる → モチベーションが上がる
    • プログラミングによって業務効率を向上できることを、作業者に実感してもらう

Lightning Talks (Aug 22) | 8月22日のLT

  • 吉祥寺.pmというイベントを作った話〜聞きたいトークが有れば自分でイベントを作ろう!〜

    • @magnolia_k_
  • 命名の話をします

    • @studio3104
    • 名前以上の機能を持たせない → 「◯◯するやーつ」という名前を日本語で付けてから英訳
  • botになる技術

    • id:debility
    • 自分の発言を記録していた後輩がbot作った
  • モダンなクライアントサイド JavaScript に追い付くためのための小さな(しかし大変な)一歩の話

    • JSのリファクタリング
  • Evaluating your stylesheets

    • @t32k, Kaizen Platform
    • こいつは良いCSSを書けているのか? → StyleStatsというツールを作った。Web版もある
  • Thank you for ${^ENCODING} variable

  • 本物の "ロック"ってやつを魅せてあげますよ - 分散排他ロック篇

    • @moznion
    • 電話 = 排他
    • Twillo, 使いすぎて expire してデモできない
  • コミュ力あげてこ

    • cho45
    • モールス信号 → 情報量少ない → 伝わると嬉しい → コミュニケーションの喜びを取り戻す
  • CONBUの道具箱

    • 舞台上でいきなり設営〜撤収
  • Vim script静的解析の光と闇

    • kuniwak
    • 闇しかない

クロージング

第3回ペパボテックカンファレンス レポート 〜 ポエム駆動開発、YAPC::Asia Tokyoでベストトークを取る方法など

Image may be NSFW.
Clik here to view.
f:id:muziyoshiz:20150829233028p:plain

第3回ペパボテックカンファレンス - connpass

最近、採用目的(YAPCで流れまくってた動画)の技術者向けアピールに積極的な印象のある、GMOペパボの第3回ペパボテックカンファレンスに参加してきました。第1回は4/19に東京開催、第2回は7/4に福岡開催。そして今日8/29が第3回。2ヶ月に1回ペースで、クオリティの高い発表を揃えた会を開催できるパワーはすごいですね。

全体的にクオリティの高い発表続きでしたが、個人的にはカラーミーショップのデザイナー @さんの講演と、YAPC::Asia Tokyo開催スタッフ @さんによる3回もの(!)のLTが特に良かったです。

@uzullaさんの3件目のLT「YAPC::Asia Tokyoでベストトークを取る方法」は、エンジニアがいかに自分を売り込んでいくか、という話として非常に考えさせられました。僕自身、1月に転職して以来、自分の売りをどうやって作っていくべきか迷走しているところなので……。

上記のおすすめ発表2件は、まだスライドが公開されてないのですが、YouTube(第3回ペパボテックカンファレンス #pbtech)で動画を見られるみたいです。

  • 動画の2:46:00頃〜:今夜、インターネットの片隅で。 〜ウェブサービス開発ちょっといい話〜 (@shikakun)
  • 動画の4:40:00頃〜:LT YAPC::Asia Tokyoでベストトークを取る方法 (@uzulla)

以下、僕が特に興味のある分野の、運用自動化や開発技法に関する講演中心のメモです。

10年動き続けているブログサービスのエンドツーエンドを書いた記録 (@kenchan)

  • 自己紹介

    • プロダクトオーナーシップの社内勉強会
    • 論理削除 Casual Talks
  • テストを書くに至った理由

    • Nyah(プライベートクラウド)の、JUGEM移設のPO的なポジションで活動している
    • JGセット(WebサーバとDBサーバのセット)が39セット → 移設のボトルネック
    • 新規構築のリリース前に使用するチェックリスト:手動・目視確認用の350項目 → 手作業は無理
  • テストに使ったライブラリ

    • Turnip, Capybara, Poltergeist (PhantomJS1系)
    • Turnip -> Cucumberより軽い書き方ができる
  • 前提

    • ステージング環境、もしくは本番環境に対してエンドツーエンドテストを実行
    • だからDBを直接触らない
  • Tips

    • デバッグ用のstepを定義(スクリーンショット取る、など)→既存アプリにテストを足すときは便利
    • 「◯◯の中にある××」という表現でサブステップを定義
    • 副作用のある作業はタグを付けて、必要な時しか実行しない
    • WYSIWYGエディタのテスト → JavaScriptを直接叩いてテスト
  • PhantomJS vs capybara-webkit

    • どっちもどっち。両方試して、そのときに安定している方を使うと良い。
  • 現状

    • 100/350 の自動化完了。無料ユーザ向け機能から順に移設中。
  • Q: Turnipのような抽象化されたツールでテストを書くと、テストを書ける人が限定されないか?

    • A: プログラミング言語で書くとなんでもできてしまう。日本語で書くと、あまり変な書き方はできない。まずはテストの外側を、他の人でも読めるようにしたかった。

MogileFSをバックエンドとしたPrivate S3の作り方 (黒田 良, @lamanotrama + 伊藤 洋也, @hiboma)

  • 30days Album

    • perl製のMogileFSを使用
    • 750TB, 22億オブジェクト
    • 7年間でデータロストは1回(4個) ← 不幸なレア条件が重なったため。改善中
  • vs OpenStack Swift

    • S3互換APIを備えているのはメリット
    • 十分安定しているMogileFSから移行するほどのメリットではない
  • vs AWS S3

    • コスト面で、オンプレミスのほうが有利
  • オブジェクトストレージ: Bayt

    • 30daysのMogileFSに、Baytのコンポーネントを追加
    • gateway: Nginx + ngx_mruby
    • api: Rails + Unicorn
    • Munin, Kibana, BigQueryでメトリクス可視化
  • 問題

    • mogilefsdがスケールしない(子プロセスが700を越えたあたりでスループットが大幅低下)
      • nice値を調整して解決
  • ngx_mrubyでreproxy処理を実行

    • APIがX-Accel-RedirectとX-Reproxy-URLというヘッダを返す
    • X-Reproxy-URLに含まれるURLのうち、片方へのアクセスが失敗したら、もう片方に取りに行く
    • オーバヘッドは問題にならない程度
  • データ移行

    • S3互換のため、S3クライアントで移行できた
    • 3TB、1億オブジェクトを移行済み
    • 今後:全サービスのデータの移行、マルチロケーション化、動的画像リサイズ機能の追加

後半:API編

  • S3互換API

    • S3互換APIを作る ≠ S3を作る
    • OSSクライアントを利用可能なように作る
    • SDKは仕様が大幅に変わることがあった(Rubyのv1 -> v2)ので、SDKをいじって開発するのは難しい
    • 最初はさくらの「オブジェクストレージAPIリファレンス」(これもS3互換)を読んで参考にした
  • APIサーバの実装

    • mogilefs-client: RubyのMogileFSクライアント。Unicornの作者製。読みやすい
    • MogileFS側がボトルネックになっており、APIサーバをRailsで実装しても問題無いと判断
    • rails-api でダイエット
    • XMLパーサはoxが速い
    • New Relicで性能測定
  • Q: ngx_luaではなくngx_mrubyを採用した理由は?

    • A: 自社に開発者がいること、Rubyで書きやすいこと

LT H2OとPHPの話 (@uzulla)

  • H20は、try_files (Nginxの設定)的なものを簡単に書ける
  • Nginxのtry_filesは大変
    • include fastcgi_params; のなかに設定がたくさん書いてあって、読んでみると闇があるのがわかる。
  • Mozillaの提供する、SSL設定を作成するサイト: Generate Mozilla Security Recommended Web Server Configuration Files

歴史あるwebサービスに携わって2年半の間に起きた事やった事 (Masataka Kono, @mapyo)

  • 自己紹介

    • カラーミーショップのエンジニア。アダ名は「ぼいらー」
    • カラーミーショップは10年以上の歴史があるサービス
    • 2年半前に入社。今回の発表は、入社してからいままでのサービス改善の内容
  • GitHub Enterprise導入

    • PHP-CS-Fixer使ったり不要なコメントを消して、プルリク、レビュー、マージ
    • 本来のコード修正はそのあとで実施
    • 順番に毎日交代でレビュー当番
  • テストの導入

    • RSpec + Capybara
      • E2Eテストの正常系だけでも作るのおすすめ
    • PHPUnit
  • Composer導入

  • Wheneverでcronの変更を自動化

  • MySQLバージョンアップ → Eloquent ORMの導入

    • LaravelについてくるORM

尋常じゃない速度でドッグフードを食べる方法 (Yuta Kurotaki, @kurotaky)

今夜、インターネットの片隅で。 〜ウェブサービス開発ちょっといい話〜 (@shikakun)

(※スライド未公開)

  • 自己紹介

    • カラーミーショップのデザイナー
    • 新卒4年目
  • 「ショッピングカート」の画面を作りなおすプロジェクト

    • 去年の9月から開始
    • 「最高のショッピングカートを作って」
  • 動画を作った

    • 動画を作ってデモしたことで、実現方法などの議論が深まった(Angular.js使おうとか)
    • 動画を作った後でprottやinvisionがあることを知った。これを使えばよかったかも
  • 詩を書いた

    • 後輩が提唱している「哲学駆動開発」
    • エモすぎたとか、自分の思いだけで書いたとかで、チーム内でのジャッジには使えなかった
    • チームメンバで詩集を作ってはどうか
  • CSSの改善

    • 2万行に至るCSSファイルになっていた
    • Hologramでスタイルシートを書き直した
    • スタイルガイドを自動生成した → 開発者が自分でCSSをいじれるようになった
  • ユーザーテスト

    • コレじゃない感があった
    • 動画を撮影
    • 半年かけて開発したのに、買えない人が出てきた
    • 開発チームみんなで見た → 改善点をリストアップ
    • 改善後のものを以下のURLで公開中(実サービスにはまだ載っていない)
  • ポエムも一種のプロトタイプ

LT やぷしぃおもしろきかく! 銅鑼パーソン総選挙 (@uzulla)

  • 銅鑼パーソン総選挙の投票サイト
    • Conoha(VPS)の一番安いプラン
    • devブランチのPHP7
    • 最大1200万投票/日
    • 投票自動化するゲームと化した → SNS認証が必要なので犯人は割れている → 1to1対応

OS X アプリケーション 開発普及活動 (@nakajijapan)

  • 自己紹介

    • iOSエンジニア、以前はWebエンジニア
    • minne
    • 今日はOS Xデベロッパとして話す
  • CocoaでのOS Xアプリ開発について

  • Teitenというアプリを開発した

    • Macのカメラを使って、ずっと写真を取り続けるアプリ。最終的に動画にする
  • Cocoaの機能説明色々

    • (※自分に知見がない分野なので、さっぱり頭に入ってきませんでした……)
  • YAPCで発表されていたElectronをこれから試してみる

仮想通貨自動トレード記 Part1 〜 国内Bitcoin取引所で裁定取引したら儲かるの? 〜 (yuma300)

仮想通貨自動トレード記 Part1 ~ 国内Bitcoin取引所で裁定取引したら儲かるの? ~ | Stobo

  • 自己紹介

    • 脇本佑磨(わっかむ)
    • JUGEMの開発
  • hubotで裁定取引するボットを開発

    • hubotの開発はcloud9がすごく便利
  • rippleにも注目

LT YAPC::Asia Tokyoでベストトークを取る方法 (@uzulla)

  • スピリチュアルトーク枠

  • トークの内容に必要なもの

    • 前振り(お題、コンテキスト共有)
    • 対象者の想定
    • 実際に話すトピック
    • 予習できるなにか → おもてなし
    • 意気込み
  • ブランドについて

    • その日いきなりは無理
    • ちまちまとブログや発表、1年くらいあればいける(?)
    • 「無駄な時間にはならないかな?」程度に「信用」されよう
    • 自分をどういうブランドに見せるかは考えたほうがよい
  • 自分が面白いと感じるトークをとりあえず真似てみる

  • トーク以外も、エンジニア人生もこういう感じなのでは?
    皆さん「面白い事」をやって、周りから「面白い事やってるな!」って思われてます?面白い人はやっぱり強いですよ!

AnsibleのGroupごと&状態ごとにServerspecのテストを書く

最近開発に参加したサービスで、サーバの構築はAnsibleのPlaybookで自動化されているのですが、構築後のテストは手作業で行っている環境がありました。それだけなら、正常にサービスが動作している状態をServerspecで定義すればテストできそうですが、このサービスはアップデートの際にいくつかのプロセスを停止する必要がありました。この停止後のチェックも手作業でした。

一部プロセスの停止
    動作中 -----------------------> 更新可能
  (Running)                       (Updatable)
      ^                                |
      |                                |
      +--------------------------------+
              一部プロセスの再起動

このような状況で、Serverspecのテストをうまく書く方法について考えてみました。

要件

今回の事例について、以下のように要件を定義しました。ちなみに、片方だけ実現できれば良いという場合は、今回のサンプルコードを多少削れば実現できます。

  1. Ansibleのグループごとに、Serverspecでテストを書ける。また、一部のホストについて、そのホスト特有のテストを書くこともできる(Master-Slave構成の、Masterのみに実施したいテストなど)。
  2. 「動作中(Running)」、「更新可能(Updatable)」など、ホストの状態に応じた複数のテストを書ける。

最終的に構築される環境

Rakefileの書き方を工夫して、以下のいずれかのコマンドでServerspecのテストを実行できるようにします。

% bundle exec rake inventory=<inventory file> status=<status name> spec:all
% bundle exec rake inventory=<inventory file> status=<status name> spec:<group name>
% bundle exec rake inventory=<inventory file> status=<status name> spec:<host name>

テストの内容を記載した*_spec.rbファイルは、以下のディレクトリに配置します。ちなみに、Ansibleのインベントリファイルは、Serverspecのディレクトリの外にあっても問題ありません。

serverspec/
  ├ Gemfile
  ├ Gemfile.lock
  ├ Rakefile
  ├ <inventory file>
  ├ spec/
  │   ├ spec_helper.rb
  │   ├ <group name>/
  │   │   └ <status name>/
  │   │        └ *_spec.rb
  │   └ hosts/
  │        └ <host name>/
  │             └ <status name>/
  │                  └ *_spec.rb
  └ vendor/

構築手順

以下の例では、~/serverspec ディレクトリ以下に、必要なすべてのファイルを配置するものとします。bundler を使わない場合は bundle exec を省いてください。

1. ~/serverspec ディレクトリの作成
2. ~/serverspec/Gemfile を作成し、以下の内容を記載
source 'https://rubygems.org'
gem 'serverspec'
gem 'rake'
3. bundle installコマンドの実行
% bundle install --path vendor/bundle
Fetching gem metadata from https://rubygems.org/.......
Fetching version metadata from https://rubygems.org/..
Resolving dependencies...
Installing rake 10.4.2
Installing diff-lcs 1.2.5
Installing multi_json 1.11.2
Installing net-ssh 2.9.2
Installing net-scp 1.2.1
Installing net-telnet 0.1.1
Installing rspec-support 3.3.0
Installing rspec-core 3.3.2
Installing rspec-expectations 3.3.1
Installing rspec-mocks 3.3.2
Installing rspec 3.3.0
Installing rspec-its 1.2.0
Installing sfl 2.2
Installing specinfra 2.43.4
Installing serverspec 2.23.1
Using bundler 1.10.4
Bundle complete! 2 Gemfile dependencies, 16 gems now installed.
Bundled gems are installed into ./vendor/bundle.
4. serverspec-initコマンドで大枠を作成(サンプルが不要ならパスして良い)
% bundle exec serverspec-init
Select OS type:

  1) UN*X
  2) Windows

Select number: 1

Select a backend type:

  1) SSH
  2) Exec (local)

Select number: 1

Vagrant instance y/n: n
Input target host name: sample.example.com
 + spec/
 + spec/sample.example.com/
 + spec/sample.example.com/sample_spec.rb
 + spec/spec_helper.rb
 + Rakefile
 + .rspec
5. AnsibleのインベントリファイルからRSpecのタスクを自動生成できるように、Rakefileを編集

RakefileはGistにアップロードしたので、こちらをご参考ください。

Serverspec Rakefile for creating tasks from Ansible inventory file with server status

6. 上記のディレクトリ構成に従って、Ansibleのグループごと、またはホストごとの設定を *_spec.rb ファイルに記載

テストの作成方法は、普通にServerspecを使う場合と特に変わりないと思います。

7. rake -T コマンドで動作確認

インベントリファイルとしては、今のところ以下のような簡単なものだけをサポートしています。以下は Inventory — Ansible Documentationから抜粋した例です。

mail.example.com

[webservers]
foo.example.com
bar.example.com

[dbservers]
one.example.com
two.example.com
three.example.com

この状態で rake -T コマンドを実行すると、正しくタスクを生成できているか確認できます。設定に成功した場合は、以下のように出力されるはずです。

% bundle exec rake inventory=./hosts status=running -T
rake spec:bar.example.com              # Run tests for host 'bar.example.com'
rake spec:dbservers:one.example.com    # Run tests for group 'dbservers'
rake spec:dbservers:three.example.com  # Run tests for group 'dbservers'
rake spec:dbservers:two.example.com    # Run tests for group 'dbservers'
rake spec:foo.example.com              # Run tests for host 'foo.example.com'
rake spec:mail.example.com             # Run tests for host 'mail.example.com'
rake spec:one.example.com              # Run tests for host 'one.example.com'
rake spec:three.example.com            # Run tests for host 'three.example.com'
rake spec:two.example.com              # Run tests for host 'two.example.com'
rake spec:webservers:bar.example.com   # Run tests for group 'webservers'
rake spec:webservers:foo.example.com   # Run tests for group 'webservers'

Rakefileの解説

AnsibleとServerspecを組み合わせて使うための既存ツール

先行事例としては、@さんが作成されている「Ansibleの設定ファイルを使ってServerspecを実行するテンプレート作成用Gem(ansible_spec)」があります。できることは、リンク先のタイトルがほぼ完全な説明になってます。

今回はサーバの状態ごとにテストを分けたかった、すでに存在するAnsible Playbookと密結合にしたくなかった、などの理由で ansible_spec は使いませんでした。

Rakeに引数を渡す方法

Rakeに引数を渡す方法については、rake でのコマンドライン引数の扱いの説明がとても参考になりました。主に以下の2つの方法があるということで、今回は後者を採用しました。

  1. タスク毎に引数を定義し、受け取る。
  2. 環境変数経由で受け取る。

記法としては、(rake spec:all[running]みたいに)鍵括弧内に引数を記載する前者の方が簡潔なのですが、この方法は「直接実行されるタスクしか引数を受け取れない」という欠点があります。今回は、タスク名でグループを指定したら、そのグループに属するホストすべてにテストを実行する、という動作を実現したかったので、この方式は採用できませんでした。

インベントリファイルの読み込み

インベントリファイルはINIファイルに似た形式なので、INIファイル用のパーサで読み込めるかと思い、PythonやRubyでのINIファイルの参照 - Qiitaを読んで inifileを試してみました。

で、結果としては、key=value形式になっていない行(つまりkeyだけの行)を読み込もうとすると、以下のようなエラーが出て駄目でした。

irb(main):004:0> hosts = IniFile.load('./hosts')
IniFile::Error: Could not parse line: "web01"
    from /Library/Ruby/Gems/2.0.0/gems/inifile-3.0.0/lib/inifile.rb:578:in `error'
    from /Library/Ruby/Gems/2.0.0/gems/inifile-3.0.0/lib/inifile.rb:532:in `block in parse'
    from /Library/Ruby/Gems/2.0.0/gems/inifile-3.0.0/lib/inifile.rb:515:in `each_line'
    from /Library/Ruby/Gems/2.0.0/gems/inifile-3.0.0/lib/inifile.rb:515:in `parse'
    from /Library/Ruby/Gems/2.0.0/gems/inifile-3.0.0/lib/inifile.rb:400:in `parse'
    from /Library/Ruby/Gems/2.0.0/gems/inifile-3.0.0/lib/inifile.rb:128:in `block in read'
    from /Library/Ruby/Gems/2.0.0/gems/inifile-3.0.0/lib/inifile.rb:128:in `open'
    from /Library/Ruby/Gems/2.0.0/gems/inifile-3.0.0/lib/inifile.rb:128:in `read'
    from /Library/Ruby/Gems/2.0.0/gems/inifile-3.0.0/lib/inifile.rb:80:in `initialize'
    from /Library/Ruby/Gems/2.0.0/gems/inifile-3.0.0/lib/inifile.rb:31:in `new'
    from /Library/Ruby/Gems/2.0.0/gems/inifile-3.0.0/lib/inifile.rb:31:in `load'
    from (irb):4
    from /usr/bin/irb:12:in `<main>'

結局、今回のRakefileではインベントリファイルのパース処理を自前で作成しました。ただ、これは単純な構造のインベントリファイルしかサポートしていないので、導入先の環境によっては色々修正する必要がありそうです。ここの汎用性は今後の課題ですね。

参考文献


Ansible Meetup in Tokyo 2015.09レポート 〜Ansible 2.0の機能紹介、Ansible 2.0で組むKubernetesクラスタなど

Image may be NSFW.
Clik here to view.
f:id:muziyoshiz:20150914234606p:plain

最近、Ansibleを業務で使い始めて色々調べていたところに、ちょうどAnsible Meetupが開催されたので参加してきました。去年も9月に開催されていて、2回目みたいですね。

今回の目玉は、僕も参考にさせて頂いた「入門Ansible」著者の若山史郎さん(@r_rudi)による、Ansible 2.0の新機能・変更点紹介でした。Ansible 2.0についての情報をコンパクトにまとめて紹介してくれて勉強になったので、僕のように2.0系の予備知識ゼロの人にはスライド資料(と以下のメモ)をおすすめしたいと思います。あと、個人的には、Ansible+Kubernetesの無理矢理感や、LTでの「Vagrant環境のAnsibleを速くしたい」という話が面白かったです。

他は事例紹介が多かったのですが、Ansibleの使い方は現場によって結構マチマチな感じですね。昨日の記事で書いたような、AnsibleとServerspecを組み合わせて使う方法に関する講演もいくつかあったのですが、この組合せに関する、誰もが納得するベストプラクティスは無さそうですね……。日経の梅崎さん(@bungoume)の講演で出てきたディレクトリ構成は、ansible_specに似てそうでしたが、質疑応答で質問してみたところ、ansible_specのことはご存じなく、自前でhelperを作っているとのことでした。

※2015-09-15追記
ansible_spec作者の@さんのtweetを見て気付いたのですが、日経とansible_specのディレクトリ構成とは違っていたみたいです。volanja/ansible_specの方は、GitHubに載っている例を見ると、/spec以下にはspec_helper.rbを置いているだけですね。すみません、勘違いでした……。

考えてみれば、Ansibleを使ってシステムを構築している時点で、Playbookの実行がエラー無く終了していれば構築は成功しているはず(またはそうあるべき)で、Serverspecで検証するニーズはあまり無いのかもしれません。Ansibleと組み合わせてServerspecを使う理由としては、

  1. Ansibleを実行してから時間が経っても同じ状態が保たれていることを定期的にテストするために使う
  2. Ansibleをジョブ実行のツールとして使う場合に、実行前に事前条件を満たすこと、および実行後に事後条件を満たすことをテストするために使う

など、Ansibleの実行直後以外のタイミングでのテストが重要な場合なのかな、と思いました。Serverspecでのテストは、あまりやり過ぎるとモジュールのテストにどんどん近づいてしまいそうで、線引きが難しそうです。

以下、僕が興味をもった部分を中心にまとめた、講演内容のメモです。

講演内容

Ansibleを結構使ってみた (@bungoume, 日経電子版)

  • 自己紹介

    • デジタル編成局 梅崎裕利
    • 2014年入社(2年目)
    • 社内では基盤チーム、サーバ管理からアプリ開発まで
    • PythonとElasticsearch検索API開発を担当
  • Ansibleを使った目的

    • Excelからの脱却、AMIコピーだけでは駄目な細かい設定の違いがある、など
  • Ansibleを選んだ理由

    • Python, エージェントレス, Chefは過去に挫折
    • @r_rudiにサポートをお願いできた
  • 電子版のWebサーバ群

    • 約300台、30種類以上
    • AWS上に構築
    • サーバリプレースのタイミングでAnsibleを採用
  • 運用までのステップ

    • 2014/9-12:実験期間(@r_rudiさんに教えてもらいながら)
    • クラウドベンダと共同での設計、関係者へのAnsible教育などを経て、2015/8から運用開始
  • ディレクトリ構造

    • specディレクトリ内に入れたServerspecで、リリース結果をテストする
    • 共通設定のディレクトリを作成
  • 構成をクラウド向けに見直し

    • 1機能1サーバ
    • サーバに状態を持たない(ログの転送など)
    • サーバのrole名の統一(関係者間での呼び名の統一)
  • CI環境はCircleCI

    • 初期はansible-lintでチェック
    • Serverspecでテスト
    • Jenkinsで自動デプロイ
  • 運用してみての感想

    • Chefと比べて運用コストが高いと言われているが、今回やると決めたことの範囲では破綻していない
    • 設定変更が容易になった
    • サーバが状態を持たず、AMIバックアップの必要性がなくなったので、コスト削減につながった
    • 開発環境の追加が容易になった
  • 共同開発で困ったこと・事故

    • Ansibleを初期構築にしか使わない、Task化されていない変更 → 変更はすべてAnsibleで、と認識合わせ
    • 設定ファイルだけ違うコピペroles
    • Gitへの不慣れ
  • 苦労、工夫した点など

    • Windowsで、期待していたtemplateモジュールが使えなかった(入ったのが8月。構築に間に合わず)
    • サーバの夜間停止〜自動起動をAnsibleのEC2モジュールで実行している
    • HAProxyの振り分け設定をプラグイン(今回開発した)で自動生成
  • 監視

    • Zabbix-agentをAnsibleでインストール
    • FluentdでログをSentryに飛ばす
    • アクセスログ(Varnish、Apache、Nginx)をElasticsearchで可視化
  • Q: Ansibleで構築したサーバをServerspecでテストするのには、ansible_specを使っている?

    • A: 使っていない(知らない)。自分たちでhelperを作って、それを使っている。

Ansible 2.0 (@r_rudi, ツキノワ)

  • 自己紹介

  • Ansible 2.0とは

    • 現在の1系(最新は1.9.3)と平行して開発中
    • 少なくとも今年2月以降から開発されている
    • 内部構造をほぼ位置から書き直した
  • Ansible 2.0についてもっとも重要な点

    • 従来のPlaybookと100%互換性がある(を目指している)
  • 追加機能

    • block
      • タスクをまとめることができる。3つのタスクに対してwhenを指定、など
      • blockに対して例外処理を定義できる(rescue, always)
      • block内に限定して設定を上書きできる(ブロック内だけrootになる、など)
    • strategy plugin
      • Ansibleはホストごとに並列でタスクが動く。このタスク実行戦略を変えられる
      • プラグインなので、追加も可能
      • 全ホストの実行終了を待つ(linear, v1と同じ)、待たない(free)など
    • include に with が使える(引数を渡せる)
    • include内で変数展開ができる
      • include: included_{{ inventory_hostname }}.yml
    • meta: refresh_inventory が追加される
      • 従来は、inventoryファイルは最初の1回しか読み込まれなかった。dynamic inventoryなどで問題になることがあった
    • taskレベルで変数の定義、上書きが可能になる
      • やり過ぎると、どこで変数が定義されているのかわからなくなる
    • 140以上の新規モジュールの追加
      • openstack, docker, zabbix, vmwareなど
    • inventory, connection pluginも追加
      • serf, consul, dockerなどからインベントリ情報を取ってこれる
    • callbackプラグインの同梱
      • e.g. profile_tasks plugin 各タスクの開始・終了時刻を計測して、最後に集計
  • 変更部分

    • 内部構造の整理
      • 変数管理、変数展開の順序などに課題があった。分かりづらかった → VariableManagerで一括管理
      • plugin構造の整理 → 継承構造を整理
    • 内部APIの変更
      • 自作pluginには多少の変更が必要
    • Python 3対応は入らなかった(準備は進めている様子)
  • 宣伝

    • Pythonエンジニア養成読本のなかでAnsibleの話を書いた
    • 17日にAnsible章の読書会をやる http://pymook.connpass.com/
  • Q: Windows対応は?

    • A: すでに1系で着々と進んでいる。2系で入るモジュールにもWindows対応のものが6個くらいある。

Ansible 2.0を使って組むkubernetesクラスタ vol.1 (h-hirokawa, 株式会社リアルグローブ)

  • タイトル

    • 今回はKubernetesの話はせず、Ansibleの話に特化したので、vol.1とした
  • 自己紹介

    • 廣川英寿
    • Webエンジニア。メイン言語はPython、とりわけDjango
    • Ansibleは2012年から利用中:NiftyCloud C4SA, Deplow
    • 毎月の無料Ansible勉強会開催、CI導入支援、Playbook代書なども
  • 今回やりたいこと

    • Ansible v2でk8sクラスタを自動構築
    • CoreOSで作る
    • 複数IaaS対応(GCE & IDCF)
      • 1クラスタを複数基盤に載せるのは、非推奨構成だったためやめた
  • CoreOSではPythonが使えない

    • モジュールがどんな言語でも書けるというメリットがあるので、Ansible使いたい
    • Ansibleにはrawがある。かといって、全部scriptでやるのはつらい
  • PyPy

    • RPythonで書かれたPythonの実装系
    • CoreOSにPyPyを入れれば、いつもどおりにAnsibleが使えるようになる
    • ただし、どのAnsibleモジュールも使えるかは要確認
    • 最新のPyPy 2.6.1は現段階では動いてくれないので、PyPy 2.4.0を使う
  • Step 1. IaaS上にCoreOSノードを作成

    • IaaS操作はlocalhostからAPIを呼び出す → 普通にやると並列化が効かない
    • ほとんどのクラウド操作モジュールには待機しないためのパラメータがあるので、それを設定する
    • GCEモジュールがそういうパラメータがない → モジュール作る? → asyncで非同期処理できる
    • v2ではasyncでループが使える
      • インスタンスを作成し、全インスタンスの作成を待って次のタスクを実行、といったことができる
  • Step 2. CoreOSのセットアップ(1)

    • Ansibleでは、shebangがあって、JSONで入出力すればプラグインとして動く
    • raw_spec
    • Ansible v2では、モジュールへの引数を hoge=fuga foo=bar のような形式で渡せなくなっている
    • <<INCLUDE_ANSIBLE_MODULE_JSON_ARGS>>というリプレーサを使う
    • v1では、モジュール内にWANT_JSONというコメントを入れると、引数ファイルの形式がJSONに変わる → v2での引数処理と同様になり、汎用的になる
  • Step 3. CoreOSのセットアップ(2)

    • Cloud-Config: CoreOSで使われている設定の仕組み。yamlで書く
    • yamlの渡し方がクラウドベンダごとに異なる
      • idcfは2KB制限あり
      • cloudconfigをAnsibleで書き換え → 10個以上のPlaybookに分かれてしまった
    • 解決策:ブート完了後にAnsibleでログインして、Config用のyamlを設置し、coreos-cloudinitを実行
    • どんなツールや手法ともうまく連携できるのがAnsibleの長所
  • 今後やりたいこと

    • Ansibleからオートスケール
      • オートスケール操作はgce系モジュールでは未提供。やるなら自作モジュールが必要
    • Ansibleからk8sの操作
    • Ansibleからk8s内のコンテナの操作
  • 宣伝

    • Ansible 2.0に対応した「Ansible完全読本」を年内発売予定

LT

Dynamic Inventoryを使ってみよう! (@saito_hideki)

スライド: Ansible meetuptokyo 2015 Dynamic Inventory

  • IIJの方
  • OpenStack環境上のVM管理にもAnsibleを使っている
  • Dynamic Inventory
  • ansibleコマンド実行時に-iオプションでプログラム名を指定。通常はクラウドコントローラに丸投げ
  • 作り方を間違えると、APIサービスに過剰なAPIコールをして、レートリミットに到達してしまう
  • クラウドAPIは、リスト取得時にホストの詳細も返してくれるものが多い
  • これを--listの結果に返すと、その次の、各ホストの詳細問合せAPIコールを省略できる

Ansibleを使ってみよう~Windowsターゲット編~ (@tsarah0822)

スライド: Ansibleを使ってみよう ~Windowsターゲット編~

  • TIS OSS推進室 倉持健史
  • Windowsモジュールの紹介
    • win_get_url: インターネットからファイルをダウンロード
    • win_msi: msiの実行
    • win_file: ファイル、ディレクトリの作成
  • 実際の案件での使用頻度上位3件:raw, script, setup
  • 要注意モジュール: win_copy
    • 1MB近くのファイルでもやたら時間がかかる
    • win_get_urlでの代替を推奨
  • v2.0の期待モジュール: win_regedit, win_unzip, win_package
  • 人柱としてWindowsのモジュールを一通り試してQiitaに投稿している

Ansibleのベストプラクティス構成に従ったplaybook開発を一工夫する (ynn, Qiitaではyunano)

スライド: Ansibleのベストプラクティス構成に従ったplaybook開発を一工夫する // Speaker Deck

Serverspecを導入したものの放置気味な人へ (@ks888sk)

スライド: Serverspecを導入したものの放置気味な人へ

  • 自己紹介
    • メーカー系企業のインフラエンジニア
    • Ansibleは1年ほど実サービスで使ってる。Ansibleのデバッガも作ってる
  • Serverspecのテストを書き続ける方法 → ツールでテスト不足をチェックする
  • Kirby: Ansibleのコードカバレッジツール(自作)
    • Ansibleのタスクのうち、Serverspecでテストされていないものの一覧を表示
  • Ansible向けのコードカバレッジツールKirbyを作りました - ks888の日記

Ansible の CI を drone/Docker で試してみた (@grenteeea)

スライド: Ansible の CI を drone/Docker で試してみた

  • 自己紹介
    • 西村健太@NTTコミュニケーションズ
    • ビッグデータ系のプロジェクトでAnsibleを使っている
    • そろそろServerspec使わないと、と思っている
  • ソースコードを社外に出せないため、CIサービスは使えない。
  • 代替として、dockerでCI環境を構築する。
  • Serverspecでコケると、droneで表示される。
  • GitLabへのpush時にWebHook経由でdrone起動。サーバ構築してテスト実行。
  • 試してみての雑感:単体テストなら使える。複数のコンテナを動かす必要があるなら、もっと手を加える必要あり

Vagrant環境のAnsibleを速くしたい (@oinume)

スライド: Vagrant環境のAnsibleを速くしたい

  • 自己紹介
    • CyberAgent, Inc.
    • amebaownd.com のバックエンドエンジニア、Go, Pythonメイン
  • Ansibleの実行遅い
  • Profiling Ansible Tasksでプロファイリング
  • 遅い作業
    • パッケージのダウンロード
      • 海外からダウンロード → 国内に変更
      • vagrant-cachierプラグインで、パッケージをホストOSにキャッシュ
    • VirtualBoxのNIC変更
      • VirtualBoxではNICをエミュレートしないように変更 → virtio-netを使う

SIerでもAnsibleを導入したい! (@kk_Ataka)

スライド: slideshare/20150914_ansible_for_sier_digest_for_reveal.js.pdf at master · gosyujin/slideshare

  • SIerの社内で、Ansibleの紹介をして反応をヒアリング
  • Ansible導入に向けて押すと良いポイント
    • 秘伝のタレを再利用できます!
    • サーバ側は、とりあえずSSHで入れるだけでOK!

Ansibleモジュール作成・配布・貢献 (@hogegashi)

スライド: Ansible モジュール 作成・配布・貢献 // Speaker Deck

  • 自作モジュール紹介:blockinfileモジュール
  • モジュール配布のノウハウ
    • roleの形にしてGitHubで公開
    • Ansible Galaxyにロールを登録
  • アップストリームへの貢献のノウハウ
    • ansible-modules-extrasにpull requestを出す
    • Module Checklistをよく読む
    • 既存モジュール作者によるレビュー・承認が必要
    • 投稿しても放置されてしまった事例の紹介
    • Ansible, Inc.は課題を認識しており、プロセスの見直しを考えている

Pebble Timeのswim.comアプリで泳いだ距離を自動的に記録する

Image may be NSFW.
Clik here to view.
f:id:muziyoshiz:20150922161023p:plain:w360

Pebbleは防水のスマートウォッチです。公式サイトには、Pebble TimePebble Time Steelは30m防水、初代Pebble (Pebble Classic)Pebble Steelは50m防水と書かれています。

2015-09-26追記:
記事を書いた時は30〜50「気圧」防水と誤解していたのですが、実際は30〜50m water registance、つまり30「m」防水であるとG+のコミュニティで教えてもらいました。上の記載も修正しました。
日本時計協会 (JCWA)のページWater Resistant mark (Wikipedia)によると、30m防水(Pebble Time)は水泳には適さない、50m防水(Pebble)は適する、とのこと。僕はPebble Timeを水泳に使ってしまっていますが、これは非推奨の無理な使い方をしている、という前提で読んでください。

ということは、水中で使うことを意図したPebbleアプリがさぞたくさんあるんだろうなあ……と思って探してみても、実を言うとあまり見つかりません。そのようなPebbleアプリのなかで唯一と言っていいくらい実用的なアプリが swim.com です。

これはすごく便利なアプリで、僕も1ヶ月以上使っているのですが、動作に多少クセのあるアプリで、安心して使えるようになるまで僕は結構時間がかかりました。せっかく良いアプリなのにもったいないので、今回はこのアプリの使い方をご紹介します。

Swim.com とは?

Swim.comというのは、スイマー向けのSNSです。自分が通っているプールを登録し、自分が泳いだ距離を記録していくことで、泳いだ距離や速さを仲間内で競うことが目的のSNSのようです。泳いだ記録は、基本的にGarminなどのスイムウォッチや、PebbleシリーズやSony Smartwatch 3のような防水のスマートウォッチから自動登録するようになっており、見栄を張ることはできない仕組みになっています*1

僕は、泳いだ距離を自動的に記録するためにswim.comを使っているので、SNSの機能のほうは正直良くわかりません。Swim.comの対応スイムウォッチのページを見ると、海外では"Swim.com compatible"がスイムウォッチの売りになるくらいのようですが、日本のユーザはほとんどいなそうです。まあ、スイムウォッチの方が日本でほとんど売られてないせいかもしれませんけど……。ただ、一人で使うにしても、

  • 泳いだ距離を指折り数えて覚えておく必要がなくなる
  • 過去の記録と最新の記録を比較して、最速記録(Fastest Workout)だった、最長記録(Longest Workout)だった、といったことを教えてくれる

だけでも、定期的に泳いでいる人にはだいぶありがたいSNSです。

記録は、スマホ上で以下のように確認できます。WebサイトのSwim.com上では、より詳細なデータを確認できます。

Image may be NSFW.
Clik here to view.
f:id:muziyoshiz:20150922161027p:plain:w360
  Image may be NSFW.
Clik here to view.
f:id:muziyoshiz:20150922161030p:plain:w360

セットアップ手順

僕はPebble Time + Android 5.1.1 (Nexus 5)の組合せで使っているので、この組合せでの使い方を紹介します。他の組合せの場合は、細かいところが違うかもしれません。

1. Androidの"Swim.com"アプリをインストールする

Swim.comは、Pebbleアプリだけでは動作せず、スマートフォン上で動作するアプリ(Pebbleの用語では"Companion App"と呼ばれる)をインストールする必要があります。以下のリンクからどうぞ。

Swim.com - Google Play の Android アプリ

Play Storeで"Swim.com"で検索してインストールしてもいいですが、検索結果の上位には出てこず、この記事を書いた時点では25位くらいに出てきました。この時点で「このアプリ大丈夫なのか?」と不安になるかもしれませんが(僕はなりました)、大丈夫です。Storeの評価も現時点で2.6と低めですが、大丈夫です。正直使いづらいアプリですが、徐々に改善されてます。

2. Pebbleの"Swim.com"アプリをインストールする

次に、AndroidのPebble Timeアプリ(またはPebbleアプリ)を起動して、Pebbleアプリ(Pebble上で動作するアプリの方)のSwim.comをインストールします。こちらは"Swim.com"で検索すると1位に出てきます。

3. Swim.comアカウントの作成

Swim.comはSNSなので、Swim.comアプリを使う前にはアカウント作成が必要です。

Swim.comのWebサイトからアカウントを作成しても、Androidアプリから作成してもよいのですが、個人的にはWebサイトからの登録をおすすめします。ユーザ登録時に、自分が通っているプールの登録が必要で、その画面で地図が出てくるからです*2。とはいえ、住所は地名を英語で入力して地図に出てきたところから選択、プールの名前は自由に入力すればいいので、あまり深く考えなくても大丈夫です。細かいことが気になる方向けには、FAQに記載がありました。

I can't find my pool on Swim.com - how do I add it? – Swim.com

4. プールの長さの登録

Swim.comアプリは、加速度センサが急減速を感知した時に「プールを折り返した」と判断するだけで、プールの長さの単位でしか距離を計測していません。その関係で、プールの長さを、事前にPebbleアプリに登録しておく必要があります。デフォルトは25ヤードなので、これを25メートルに変えないといけません。

  1. Android上のSwim.comアプリを起動して、ログインする
  2. 以下のいずれかの方法で、Pebble Timeの画面を開く
    • Feed画面の右上に出てくる時計アイコンをクリック
    • 画面左上に出てくるSettingアイコンをクリックしてから、Devices → Pebble Time と選択
  3. Pebble Time上のSwim.comアプリを起動する
  4. Android上のSwim.comアプリで、プールの長さを切り替える

以上でセットアップは完了です。

プールでのSwim.comの使い方

これは、僕は最初全然分からずに、ウェブ上で情報を探し回ってしまったのですが、PebbleのSwim.comアプリのページの説明文の最後に付いているURLが、一番分かりやすい説明でした。この画像がまた、Pebble Timeアプリからは直接アクセスできないという……。

Swim.com brings all the functionality of a smart swim watch onto Pebble with the most advanced swimming app yet. The Swim.com app automatically records distance swum, lap times, pace, strokes and other key metrics to provide feedback about your swim. Workouts can be automatically synced to Swim.com using the companion app where you can track your progress, connect and compete. INSTRUCTIONS FOR USE: http://i.imgur.com/WDPLeyY.png

上記のURLにある画像を見れば使い方はだいたいわかると思いますが、画像にない情報も含めて解説するとこんな感じです。ボタンの名称は以下の可愛い図に準拠で説明します。

Image may be NSFW.
Clik here to view.
f:id:muziyoshiz:20150728233648j:plain

Pebbleのボタンとその名称(Introduction To Pebble Timeより引用)
  1. Pebble上でSwim.comアプリを起動する。後述する理由から、プールサイドに行く前に起動することをお勧めする。
  2. Swim.comアプリが起動した状態でSelect (Middle)ボタンを押すたびに、Swimmingモード (workout) と Restingモードが切り替わる。基本的に、泳いでいる間はSwimmingモード、休憩中はRestingモードに自分で切り替える。
  3. Swimmingモードのときに急減速すると、その時点で25m進んだと判断される。
  4. Restingモードの時にUpボタンを押すと、時刻の表示方法がトグルで切り替わる。
  5. プールから上がるときには、Back (Left)ボタンを押す。この時点で、その日の総距離が画面に表示される。Pebble上のSwim.comアプリは閉じてしまってよい。
  6. プールから上がったあとで、Android上でSwim.comアプリを起動し、Pebble Timeの画面を開く。この画面で「CHECK FOR NEW」ボタンをクリックすると、データが(Webサイトの)Swim.comにアップロードされる。

Swimmingモードは背景色が黒、Restingモードは白になるので、ちらっと見ればどちらのモードかわかるようになっています。また、モード切り替え時にはバイブするので分かりやすいです。

また、距離については、Swim.comのヘルプ(下記)によると10%くらいの誤計測があるそうです。

Also, many times two swims of the exact distance in the same body of water will not record the same distance because each time the watch is submerged it loses contact with the satellite and tries to regain each time it is out of the water. There is about a 10% +/- margin of error.

Can Swim.com for Pebble be used for open water swims? – Swim.com

ただ、PebbleのSwim.comアプリは加速度を使って距離を計測している、ということを意識しておくと、誤計測をある程度防げるようです。色々試した感じでは、ゆっくり折り返すと距離が増えないことが多く、折り返しのキックを強くしたところ、計測漏れはほとんどなくなりました。逆に、コースの途中で前の人に追いついてしまって、クロールから平泳ぎに変えて急減速したときには、距離が25m余分に増えたことがありました。

その他

以下、僕が1ヶ月ほど使っていて気付いたtipsです。

プールサイドに行く前に、Pebble上でSwim.comアプリを起動したほうがよい

僕だけかもしれませんけど、Pebble Timeは1ヶ月に1回くらい固まることがあって、その都度Recovery Modeで復旧しています(参考:Pebble | SOS screen / Recovery Mode)。

Recovery Modeで復旧したあとで、Pebbleアプリを起動すると、通常はスマートフォンからロードし直されてメモリに載ります。ただ、当然ですが、そのときにスマートフォンとBluetooth接続できる状態にないと起動しません。Swim.comアプリの場合、プールサイドでそのことに気付いて、一度更衣室まで戻ってロッカーを開けて、スマホと接続し直す必要があるということに……。最近Recovery Modeにしたか、なんて普通は覚えていないと思うので、とりあえずスマホと繋がっているうちにSwim.comアプリを起動しておくことをお勧めします。

Swim.comにデータをアップロードできるようになるまでに時間がかかる

普通にSwim.comを使っていて、プールから出た直後にデータをアップロードしようとすると、よく見かけるのが以下の "No new workouts to upload."というエラーです。

Image may be NSFW.
Clik here to view.
f:id:muziyoshiz:20150922161034p:plain:w360

このエラーをクリックすると、以下のページが表示されます。

Image may be NSFW.
Clik here to view.
f:id:muziyoshiz:20150922161036p:plain:w360

PebbleとのBluetooth接続をやり直すとアップロードが早く行われるよ、的なことが書いてありますが、あまりうまくいきません。それよりも、単に1時間くらい待ってからやり直すとうまくいくことが多かったです。これまでの経験上、データが消えたことはなかったので、落ち着いて待ちましょう。大丈夫です。

プールによっては使えないかも

僕が通っているプールでは時計着用を禁止しているということで、監視員に何度か止められました。その都度、これは泳いだ距離や速さを記録するために着用していることと、Pebble Timeはほとんどゴムとプラスチックでできており、ぶつかって怪我をさせる危険性は低いことを説明し、なんとか使わせてもらえている状況です。

そのため、PebbleでのSwim.com利用は許してもらえたらラッキーくらいに考えておいて、もしこのアプリを目当てにPebbleを購入するのであれば、プールの方に事前に相談したほうがよさそうです。監視員の方と何度か話した感じからすると、Pebble Time Steelに金属バンドを付けたようなものだと「人にぶつかると危険だから」という理由で使わせてもらえないかもしれません。まあ、そもそも、水中で使うつもりならSteelは選ばないと思いますけどね。

まとめ

日本でPebbleを買っている AND プールで泳いでいる という条件を満たすユーザが少ないのか、日本語の情報はさっぱり見つからない Swim.com をご紹介しました。

Pebble Timeを買ってから2ヶ月くらい経ちましたが、相変わらず毎日身に付けて、便利に使っています。Pebble Time良いですよ。Rebuild.fmで宮川さんも絶賛してたりしいますし、すごく良いですよ。さっぱりユーザ増えてる感じがしませんけど。日本語も普通に表示できるんですが、Pebbleが日本語を公式対応していないのが普及のハードルになってるんですかねえ。

*1:手動でのデータ入力もできるのですが、ランキング計算からは除外されるようです。

*2:すでにあるプールから選ぶこともできるのですが、日本国内だとユーザが少ないので最寄りのプールはまず登録されていないと思います。

SQLアンチパターンをパッと思い出すための一覧

Image may be NSFW.
Clik here to view.
SQLアンチパターン

SQLアンチパターン

SQLアンチパターン読みました。非常に面白かったです。アンチパターンについて事例を交えてわかりやすく書かれており、プログラマとして何年か仕事したことがある人なら、「あの案件のデータベースはこのアンチパターンだった」とか「このテーブル定義書いたことある」とか、過去の色々を思い出しながら楽しく読める本だと思います。

この本の良いところは、「アンチパターンを用いるのは絶対駄目」と言うのではなくて、アンチパターンを用いてもよい場合や、アンチパターンを見つけた場合の解決策のバリエーションにも十分なページ数を割いているところです。アンチパターンに遭遇したり、アンチパターンを使うしか無いと思える場面に遭遇したら、この本を読み返して考える、という使い方をするのが良さそうです。

あと、RDBMSではなくてNoSQLを使っていて正規化できないから、「どうしても本書のアンチパターン(EAVなど)を使わざるを得ない」といった場合にも、潜在的なデメリットを理解する助けになりそうだ、と読みながら思いました。

そこで、アンチパターンの概要をパッと思い出せるように、本書に載っているアンチパターンの一覧を作りました。概要や解決策は、基本的に本書の表現を踏襲しつつ、自分の思い出しやすいように表現を変えています。また、アンチパターン名は The Pragmatic Bookshelf | SQL Antipatternsに載っている英単語に直しました(どのみち本書も英単語をカタカナにしてるだけなので)。

Logical Database Design Antipatterns (データベース論理設計のアンチパターン)

アンチパターン名 概要 解決策
Jaywalking
(信号無視)
交差(intersection)テーブルの作成を避けるために、カンマ区切りのリストを使う 交差テーブルを作成する
Naive Tree
(素朴な木)
テーブルにparent_idカラムを追加して、素朴な隣接リスト(Adjacency List)にする 経路列挙モデル(Path Enumeration)、閉包テーブルモデル(Closure Table)などの代替ツリーモデルを使用する
ID Required
(とりあえずID)
すべてのテーブルに「id」列を用いる idではなく、bug_idのような分かりやすいカラム名を付ける、自然キーと複合キーを活用する(不要な擬似キーを作らない)
Keyless Entry
(外部キー嫌い)
外部キー制約を使用しない 外部キー制約を宣言する
Entity-Attribute-Value
(EAV)
サブタイプの属性の種類を可変にするために、汎用的な属性テーブルを用意する。そして属性名をカラム名にする代わりに、attr_nameカラムの値として格納する サブタイプのモデリングを行い、EAVを用いない
Polymorphic Associations
(ポリモーフィック関連)
親テーブルの種別およびIDを格納するカラム(xxxx_type, xxxx_id)を用意し、xxxx_typeの値によって参照する親テーブルを切り替える 親子の参照を逆にする、交差テーブルを作成する、または共通の親テーブル(クラステーブル継承の基底テーブル)を作成する
Multi-Column Attribute
(複数列属性)
属性の数だけカラムを事前に定義する(tag1, tag2, tag3, ...) 従属テーブルを作成する
Metadata Tribbles
(メタデータ大増殖)
増殖する性質を持つテーブルやカラムを定義する。例えば年ごとにバグのテーブルを分ける(Bugs_2008, Bugs_2009, ...) パーティショニングと正規化を行う(水平パーティショニング、垂直パーティショニング、従属テーブルの導入など)

Physical Database Design Antipatterns (データベース物理設計のアンチパターン)

アンチパターン名 概要 解決策
Rounding Errors
(丸め誤差)
FLOATデータ型を使用する NUMERICデータ型を使用する
31 Flavors
(31のフレーバー)
カラムに格納することを許可する値を、列定義で指定する 許可する値を、テーブルに格納したデータで指定する(例:Bugテーブルのstatusカラムに格納可能な値を、BugStatusテーブルのstatusカラムで指定する)
Phantom Files
(幻のファイル)
ファイルをファイルシステムに格納し、そのファイルのファイルパスをデータベースに格納する 必要に応じてBLOB型を採用する
Index Shotgun
(闇雲インデックス)
インデックスが多すぎる/少なすぎる、あるいはインデックスを活用しないクエリを実行する 「MENTOR」の原則に基づいて効果的なインデックス管理を行う(Measure, Explain, Nominate, Test, Optimize, Rebuild)

Query Antipatterns (クエリのアンチパターン)

アンチパターン名 概要 解決策
Fear of the Unknown
(恐怖のunknown)
NULLを一般値として使う、または一般値をNULLとして使う(例:-1を「未指定」の意味で使う) NULLを一意な値として使う
Ambiguous Groups
(曖昧なグループ)
GROUP BYの使用時に、グループ化されていない列を参照する 曖昧でない列を使用する(相関サブクエリを使用、導出クエリを使用、JOINを使用、他の列にも集約関数を使用、など)
Random Selection
(ランダムセレクション)
ランダムにソートを行い、最初の行を取得する。 特定の順番に依存しない(OFFSET句を用いてランダムに行を選択する、など)
Poor Man’s Search Engine
(貧者のサーチエンジン)
パターンマッチ述語(LIKE、REGEXP)を使用する 適切なツールを使用する(専用の全文検索エンジンや、データベース製品独自の全文検索機能、自作の転置インデックスなど)
Spaghetti Query
(スパゲッティクエリ)
複雑な問題をワンステップで解決しようとする クエリを分割する、サブクエリの列に互換性があるならUNIONを用いる、CASE式とSUM関数を組み合わせる
Implicit Columns
(暗黙の列)
SELECT文でワイルドカードを使う、あるいはINSERT文で列名の入力を省略する 必要な列の列名だけを明示的に指定する

Application Development Antipatterns (アプリケーション開発のアンチパターン)

アンチパターン名 概要 解決策
Readable Passwords
(読み取り可能パスワード)
パスワードを平文で格納する ソルトを付けてパスワードハッシュを格納する
SQL Injection
(SQLインジェクション)
未検証の入力をコードとして実行する 誰も信用してはならない(入力をフィルタリングする、prepared statementを使う、コードレビューしてもらう、など)
Pseudokey Neat-Freak
(擬似キー潔癖症)
欠番となった擬似キーを再利用する、あるいは擬似キーの値を再割当てすることで、連番になっている擬似キーの隙間を埋める 擬似キーの欠番は埋めない、擬似キーを行番号と混同しない
See No Evil
(臭いものに蓋)
肝心な部分を見逃す(データベースAPIの戻り値を無視する、アプリケーションコード内に点在するSQLしか読まない) エラーから優雅に回復する(戻り値と例外のチェックをする、構築後のSQLをログに出力する)
Diplomatic Immunity
(外交特権)
SQLを特別扱いする(「アプリケーション開発のルールは、データベース開発には当てはまらない」と考える) 包括的に品質問題に取り組む(文書化、バージョン管理、テスティング)
Magic Beans
(魔法の豆)
モデルがアクティブレコードそのもの モデルとDAOの関係をis-aではなくhas-aにする、DAOを外部に公開しない、ドメインモデルを使用する
砂の城*1想定不足(性能問題や障害が起きた時の対処についてのポリシーの策定漏れなど) どのようなトラブルが起こりうるかということを可能な限り想定しておく

*1:奥野 幹也さんによる寄稿のため、英語名なし

HashiCode #2 レポート 〜HashiConf 2015のSession内容や、新製品Nomad、Ottoの紹介など

Image may be NSFW.
Clik here to view.
f:id:muziyoshiz:20151020004820j:plain

クリエーションラインの前佛さんのイベント HashiCode#2 に参加してきました。

今回の内容は、9/28〜29にオレゴン州ポートランドで開催された HashiConf 2015の報告会、および10/22に発売される前佛さんの書籍 DevOpsを支える HashiCorpツール大全 (Think IT Books)の先行販売イベントでした。僕も書籍を購入して、その場で前佛さんのサインを頂きました。書籍の内容はThinkITの連載+コマンドリファレンスのようです。詳しくはこれから読んでみます。

報告会では、HashiConfで新たに発表されたNomadとOttoの話題が多かったです。Ottoの方については実用になったらすごく便利そうと思い、自分でも手元で動かしたり、HashiCorpのサイトで公開されているドキュメントを読んだりしていたのですが、前佛さんが注目しているポイントを直接聞けて参考になりました。

プレゼン資料は(前佛さんのプレゼン特有のいつもの事情により内容を削った上で)いずれ公開されるとのことなので、詳細はそちらを見てもらうのが良いと思います。以下、講演内容のメモです。

講演内容

書籍紹介(インプレス 鈴木氏)

Image may be NSFW.
Clik here to view.
DevOpsを支える HashiCorpツール大全 (Think IT Books)

DevOpsを支える HashiCorpツール大全 (Think IT Books)

  • 作者:前佛雅人,クリエーションライン株式会社
  • 出版社/メーカー:インプレス
  • 発売日: 2015/10/22
  • メディア:単行本(ソフトカバー)
  • この商品を含むブログを見る
  • 10/22に公式販売。Kindle版も出る
  • 今回の書籍は BitBucket + tex で執筆した

HashiConf 2015 Report (クリエーションライン 前佛氏, @)

1. HashiCorp & HashiConf 2015

  • エモい話

    • 自動化に夢見すぎな人が多い。100%は無理。出来るところからでおk
  • HashiCorpについて

    • HashiCorpは、ツールを導入すれば良いという話はしていない。"Workflows, not Technologies"というスローガンを掲げている
    • 農家とHashiCorpの類似点。いずれも単機能な部品の組合せ
    • HashiCorpのツールも、何らかの目的を実現するための様々な組み合わせを提供している
    • HashiCorpは、すべての開発過程をGitHubで公開している

2. Sessions

セッションは2並列だったので、前佛さんが聞いたものだけ紹介。

  • Electric Cloudの事例

    • 課題:Wikiでの情報管理。人数が少ないうちは回っていたが、人数が増えると更新が回らなくなり、古い情報ばかりで信用できなくなった
      • 対策:Vagrantを使い始めた。やりたいことにVagrantがハマった。Vagrantfileをドキュメントとして扱う(ことを社内に認知させた)
    • 新しい課題:Vagrantでは環境の切り替えが大変
      • 対策:Terraform導入で解決。色々な環境を切り替え可能にした
    • 更なる課題:テスト環境と本番環境の環境を統一したい
      • 対策:Dockerの導入
    • 組織が変更を受け入れるためには、問題を認識し、仮説を論理立てるのが第一。そのあとで、ツールやソリューションを見る
  • Grouponの事例

    • ペットと家畜のアナロジー。これをデータベースでもやっている。Databases as Pets
    • データベースのステート管理にConsulを使っている
    • 自動化にかかる手間について。Efficacy vs Efficiency
    • 彼らの基準:3人月かかる作業なら自動化する
  • CapitalOneの事例

    • Vagrantを証券部門で使い始めた。Packer, Terraform, ConsulにPull Request出してる
    • (この事例とは離れた余談だが)今回のHashiConfで紹介された事例で、Serverspecが3回は出てきた
  • Pinterestの事例

    • Image States ... AMIをレベル0〜2に分けてる。AWS提供が0、開発用が1、本番用が2
  • セッションは Schedule | HashiConf 2015 by HashiCorpに公開されてるので、興味のある方はそちらを

3. KeyNote wrap up day1&day2

  • 300名くらいのホールでKeynote。満員になるくらい集まっていた

  • 情報のアップデートがあった

    • Vault 0.3:使い捨てのSSH鍵を使えるようになる(前佛さんはまだ試してない)
    • 目玉はNomadとOtto
  • HashiCorpのビジョン

  • HashiCorp DevOpsの紹介

    • 彼ら自身の開発でもツールを使っている
    • Slackも使っている。Packer buildが終わると、通知がSlackに流れる、など
    • Atlas + ConsulでAWSを管理している
  • Nomad

    • Nomadの発音は、日本ではノマドとよく言われるが、関係者の発音はノーマッドに聞こえた
    • 分散スケジューラ:複数のクラウドをまたいで実行環境を用意する
    • Kubernetesとの違い:デプロイ対象はコンテナだけではない。すべてがnomadコマンドで解決する。Kubernetesは環境構築が大変だが、Nomadは1個のバイナリで環境を用意できる
    • Nomadのポイントは、ジョブの実行に必要なリソースを、コードで簡単に定義できるところ
    • Nomadのなかで、実際にはSerfとかConsulが動作している
    • 設定ファイルを見ていて、MesosとAuroraを連想した
    • 参考:【参考訳】Nomad | Pocketstudio.jp log3
  • Otto

    • Vagrantから学んだ知見を反映した、Vagrant後継ソフト
    • Ottoの理想系は、AppfileからVagrant、Packer、Terraform、Nomad、Vault、Atlasを含めて制御できる形だが、いまは実装途中なので、VaultやAtlasは制御できない
    • Appfile、dependencyのサンプルはバグがあって動かない
    • Windows版はバギーなのでおすすめしない。Macなら良いかもしれない
    • otto compileという新しい概念が導入されている
    • ottoは無くてもよいツールと思うが、うまく使うと仕事が楽になりそう
    • 個人的には、Chefなどの構成管理ツールとの住み分けを気にしている
    • HashiConfのデモでは、「Rubyのインストール手順」でGoogle検索すると、微妙に違う手順が山ほど検索結果が出てくる、だからottoがベストプラクティスを提供する、という話をしていた
    • Vagrantは、Ottoとは別に開発が進み、ずっと残り続ける
    • 参考:【参考訳】otto | Pocketstudio.jp log3

4. おまけ

質疑応答

  • Q) NomadやOttoの次のアップデートはいつごろ、という話はあった? 例えば、Otto 0.2はいつ出るか。雑談レベルでも良いので、もし何かあれば教えてほしい
    • A) 解決したIssueがある程度まとまったらリリースされるのでは。できるだけ早くリリースしたいと言っていた。自分の感覚では3ヶ月〜半年でバージョンが上がるイメージ

One more thing...

パネルディスカッション

その場でパネラーを募集してパネルディスカッション

  • @による、HashiConfに参加した感想
    • serverspecを含む事例がよく出ていた
    • ottoを使ってみた人はわかると思うが、otto testはなかった。MitchellにServerspecの開発者を紹介したが、反応がよくなかった。テストは重視していない?
    • ただ、事例ではワークフローにテストを入れる事例がよく出ていた
    • VagrantだけRubyが残っているのをどうするんだろう? VagrantがGoで書き直されることはあると思う? → (前佛)書き直すという話はないと思う
    • 経費。20万かからずに行って帰ってこれた。興味のある方は、来年の参加を検討してみては
    • 半分以上の発表はハズレだった。使ってみたレベルが多かった。ただし、雰囲気はアットホームだったので、講演後に講演者と喋れて楽しかった

  • @による、Ottoのコードを読んだ感想

    • ottoは触ってコードを読んで、ブログを書いた
    • 抽象化の仕方がPaaSに似ている。PaaSと比べて足りないところはオートスケールや、モニタリング
    • 先ほど、テストを重視していないのではという話があったが、Ottoのコードにはテストとドキュメントが大量にあったので、そんなことないと思う。ここまでテストを書く人がいるのか、と思ったくらい
    • 最近はNomadを追っている。スケジューラとして有名なのはYARN。YARNはホストをスロットに分けるという単純なリソース管理。より細かい管理をできるのがMesos
    • Nomadのコードを読んだら、またブログに書くので読んで欲しい
  • パネルディスカッションのなかで出た話題

    • Q) クリエーションラインが提供しているHashiCorp製品のサポートの内容は?
    • A) なんでもやっている。作りましょう、検証しましょう、など。正式メニューはない。ご相談ください。(前佛)

解説:Otto 0.1で dependency (AppType = docker) を指定した場合の動作

はじめに

Ottoとは、9月のHashiConfで発表された、Vagrantの後継となるソフトウェアです。9月に Version 0.1.0 がリリースされ、現在の最新版は 0.1.2 です。

Ottoの設定ファイル "Appfile"には、アプリケーション間の依存関係を定義することができます。以下の例は、Rubyで書かれたWebアプリ(otto-getting-started)がMongoDBに依存していることを示しています(Getting Started GuideApp Dependenciesページより抜粋)。

application {
  name = "otto-getting-started"
  type = "ruby"

  dependency {
    source = "github.com/hashicorp/otto/examples/mongodb"
  }
}

dependencyのsourceが示す先にはそのアプリケーション(上記の例ではMongoDB)のAppfileがあります。そのAppfileにもdependencyが書かれていたら、Ottoは芋づる式にそれらのAppfileをダウンロードします。

Otto の目標は、このようにアプリケーションの依存関係が定義された1個の Appfile を元に、複数のアプリケーションが動作するVM群(またはコンテナ群)を一括セットアップできるようにすることです。この機能があることをもって、HashiCorpは、Otto は "Built for Microservices"であると標榜しています。

この目標に魅力を感じて、僕も最近少しずつOttoを試しています。ただ、Otto はリリースされたばかりの version 0.1 ということで、この機能(Dependency)はまだ未実装な部分が多いようです。自分の理解のためもあり、現状をまとめてみました。

結論

Image may be NSFW.
Clik here to view.
f:id:muziyoshiz:20151030234318p:plain

AppType = docker のアプリケーションを Dependency に記載した場合、ローカル環境とクラウドサービス(0.1はAWSのみ対応)へのデプロイ結果は以下のようになりました。

  • ローカル環境の構築時(otto dev実行時)

    • ローカル環境で1台のVirtualBox VMが起動され、その上でWebアプリが動作する。
    • このVMにDockerが自動インストールされて、VM上でMongoDBのコンテナが動作する。
    • VMにはConsulもインストールされ、VMからは mongodb.service.consul という名前でMongoDBに接続できる。
  • AWSへのデプロイ時(otto deploy実行時)

    • AWS上で1台のインスタンスが起動され、その上でWebアプリが動作する(インフラ構成=Flavor が "simple"の場合)。
    • このインスタンスにはDockerがインストールされず、従ってMongoDBのコンテナも動作しない。dependencyの設定は単純に無視される?
    • Consulはインストールされる。しかし、現状ではdependencyに関しては意味がない。

動作確認の結果

Getting Started Guideにある例を用いて、動作確認を行いました。以下、動作確認を行った際の手順、および実行結果の詳細です。

動作環境

  • OS: OS X Yosemite バージョン 10.10.5(14F27)
  • Otto: Mac OS X版 Otto 0.1.2

サンプルアプリのダウンロード

サンプルアプリ(Rubyで書かれたWebアプリケーション)をダウンロードします。

happyturn% git clone https://github.com/hashicorp/otto-getting-started.git

ここにはAppfileが含まれないので、App Dependenciesにある内容でAppfileを作成します。

application {
  name = "otto-getting-started"
  type = "ruby"

  dependency {
    source = "github.com/hashicorp/otto/examples/mongodb"
  }
}

このmongodbディレクトリには、以下の内容の Appfile と、アプリケーションのIDが入った .ottoid ファイルが置かれています。これは、"mongo:3.0"という名前のdocker imageをダウンロードしてきて、"mongodb"という名前のアプリケーションとして動作させる、という設定です。

application {
    name = "mongodb"
    type = "docker-external"
}

customization "docker" {
    image = "mongo:3.0"
    run_args = "-p 27017:27017"
}

otto/Appfile at master · hashicorp/otto

コンパイル

otto compileコマンドを実行すると、このAppfileをもとに、Vagrant、Terraform、Consulなどの設定ファイルが自動生成されます。生成されたファイルは .otto フォルダ以下に格納されています。この処理を Otto では「コンパイル」と呼んでいます。

Otto 0.1.0 には、source = "github.com/hashicorp/otto/examples/mongodb"を指定するとコンパイルに失敗するというバグがあったのですが、最新版では解決しています。

happyturn% otto compile
==> Loading Appfile...
==> Fetching all Appfile dependencies...
    Fetching dependency: git::https://github.com/hashicorp/otto.git//examples/mongodb
==> Compiling...
    Application:    otto-getting-started (ruby)
    Project:        otto-getting-started
    Infrastructure: aws (simple)

    Compiling infra...
    Compiling foundation: consul
==> Compiling dependency 'mongodb'...
==> Compiling main application...
==> Compilation success!
    This means that Otto is now ready to start a development environment,
    deploy this application, build the supporting infrastructure, and
    more. See the help for more information.

    Supporting files to enable Otto to manage your application from
    development to deployment have been placed in the output directory.
    These files can be manually inspected to determine what Otto will do.

ログから、Dependency 'mongodb'のダウンロードも行われたのが分かります。mongodbのAppfileは .otto/appfile/deps/b80064b1b081e385b5c0cf62e2b2b3f0/Appfile にダウンロードされています。このフォルダ名は、otto-getting-startedとmongodbのいずれのOtto IDとも一致しないのですが、何のIDなのかは分かりません……。

ローカル環境への構築

コンパイル後に otto devコマンドを実行すると、ローカル環境にこれらのアプリケーションが構築されます。この処理の内部では Vagrant が使われています。以下のログの冒頭4〜5行目にあるように、OttoはVagrantの出力をミラーしているだけのようです。

このログをよく見ると、途中でdockerのインストールや、mongodbコンテナのダウンロードを行っているのがわかります。

happyturn% otto dev
==> Creating local development environment with Vagrant if it doesn't exist...
    Raw Vagrant output will begin streaming in below. Otto does
    not create this output. It is mirrored directly from Vagrant
    while the development environment is being created.

Bringing machine 'default' up with 'virtualbox' provider...
==> default: Importing base box 'hashicorp/precise64'...
==> default: Matching MAC address for NAT networking...
==> default: Checking if box 'hashicorp/precise64' is up to date...
==> default: Setting the name of the VM: dev_default_1446212147870_20376
==> default: Fixed port collision for 22 => 2222. Now on port 2200.
==> default: Clearing any previously set network interfaces...
==> default: Preparing network interfaces based on configuration...
    default: Adapter 1: nat
    default: Adapter 2: hostonly
==> default: Forwarding ports...
    default: 22 => 2200 (adapter 1)
==> default: Booting VM...
==> default: Waiting for machine to boot. This may take a few minutes...
    default: SSH address: 127.0.0.1:2200
    default: SSH username: vagrant
    default: SSH auth method: private key
    default:
    default: Vagrant insecure key detected. Vagrant will automatically replace
    default: this with a newly generated keypair for better security.
    default:
    default: Inserting generated public key within guest...
    default: Removing insecure key from the guest if it's present...
    default: Key inserted! Disconnecting and reconnecting using new SSH key...
==> default: Machine booted and ready!
==> default: Checking for guest additions in VM...
    default: The guest additions on this VM do not match the installed version of
    default: VirtualBox! In most cases this is fine, but in rare cases it can
    default: prevent things such as shared folders from working properly. If you see
    default: shared folder errors, please make sure the guest additions within the
    default: virtual machine match the version of VirtualBox you have installed on
    default: your host and reload your VM.
    default:
    default: Guest Additions Version: 4.2.0
    default: VirtualBox Version: 4.3
==> default: Configuring and enabling network interfaces...
==> default: Mounting shared folders...
    default: /vagrant => /Users/myoshiz/Otto/otto-getting-started
    default: /otto/foundation-1 => /Users/myoshiz/Otto/otto-getting-started/.otto/compiled/app/foundation-consul/app-dev
    default: /otto/foundation-mongodb-1 => /Users/myoshiz/Otto/otto-getting-started/.otto/compiled/dep-24cbbebc-fc41-53b4-c844-60d642fb523c/foundation-consul/app-dev-dep
    default: /otto-deps/mongodb-24cbbebc-fc41-53b4-c844-60d642fb523c => /Users/myoshiz/Otto/otto-getting-started/.otto/appfile/deps/b80064b1b081e385b5c0cf62e2b2b3f0
==> default: Running provisioner: shell...
    default: Running: inline script
==> default: stdin: is not a tty
==> default: [otto] Installing Consul...
==> default: [otto] Installing dnsmasq for Consul...
==> default: [otto] Configuring consul service: otto-getting-started
==> default: Running provisioner: shell...
    default: Running: inline script
==> default: stdin: is not a tty
==> default: Running provisioner: docker...
    default: Installing Docker (latest) onto machine...
==> default: Starting Docker containers...
==> default: -- Container: mongodb
==> default: Running provisioner: shell...
    default: Running: inline script
==> default: stdin: is not a tty
==> default: [otto] Configuring consul service: mongodb
==> default: Running provisioner: shell...
    default: Running: inline script
==> default: stdin: is not a tty
==> default: [otto] Setting locale to en_US.UTF-8...
==> default: Running provisioner: shell...
    default: Running: inline script
==> default: [otto] Adding apt repositories and updating...
==> default: [otto] Installing Ruby 2.2 and supporting packages...
==> default: [otto] Configuring Ruby environment...
==> default: [otto] Installing Bundler...
==> default: [otto] Bundling gem dependencies...
==> default: [otto] Configuring Git to use SSH instead of HTTP so we can agent-forward private repo auth...

==> Caching SSH credentials from Vagrant...
==> Development environment successfully created!
    IP address: 100.115.125.105

    A development environment has been created for writing a generic
    Ruby-based app.

    Ruby is pre-installed. To work on your project, edit files locally on your
    own machine. The file changes will be synced to the development environment.

    When you're ready to build your project, run 'otto dev ssh' to enter
    the development environment. You'll be placed directly into the working
    directory where you can run 'bundle' and 'ruby' as you normally would.

    You can access any running web application using the IP above.

ローカル環境への構築結果の確認

これで、1台のVMが起動し、そのうえでRubyアプリケーションと、MongoDBが動作するコンテナが動くようになりました。VMが1台しか動いていないことは、以下のコマンドで確認できます。ちなみに、otto dev vagrant [subcommand]で、Vagrantの任意のサブコマンドを実行できます。このように、ローカル環境に関しては、OttoはVagrantの単なるラッパーです。

happyturn% otto dev vagrant status
==> Executing: 'vagrant status'
Current machine states:

default                   running (virtualbox)

otto dev sshコマンドで、いま起動したVMにログインできます。

happyturn% otto dev ssh
==> Executing SSH. This may take a few seconds...
Welcome to Ubuntu 12.04 LTS (GNU/Linux 3.2.0-23-generic x86_64)

 * Documentation:  https://help.ubuntu.com/
New release '14.04.3 LTS' available.
Run 'do-release-upgrade' to upgrade to it.

Welcome to your Vagrant-built virtual machine.
Last login: Fri Sep 14 06:23:18 2012 from 10.0.2.2

dockerコマンドがすでにインストールされているので、以下のようにコンテナ上でMongoDBが動いていることを確認できます。

vagrant@precise64:/vagrant$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
mongo               3.0                 c08c92f4cb13        6 days ago          261.6 MB

vagrant@precise64:/vagrant$ docker ps
CONTAINER ID        IMAGE               COMMAND                CREATED             STATUS              PORTS                      NAMES
a49699c16bfc        mongo:3.0           "/entrypoint.sh mong   9 minutes ago       Up 9 minutes        0.0.0.0:27017->27017/tcp   mongodb

vagrant@precise64:/vagrant$ docker exec -it a496 bash
<8d0520a79fe446e0# ps -ef | grep mongod
vboxadd   5939  5667  2 13:37 ?        00:00:18 mongod
root     10144 10128  0 13:49 pts/2    00:00:00 grep --color=auto mongod

Consulの動作も、以下のように確認できます。Consulの設定が自動で行われており、mongodb.service.consul という名前でMongoDBに接続できるようになっています。

vagrant@precise64:/vagrant$ consul members
Node       Address         Status  Type    Build  Protocol  DC
precise64  10.0.2.15:8301  alive   server  0.5.2  2         dc1

vagrant@precise64:/vagrant$ curl http://mongodb.service.consul:27017
It looks like you are trying to access MongoDB over HTTP on the native driver port.

当然ではありますが、Install MongoDB on Ubuntu — MongoDB Manual 3.0の手順に従ってVM上にmongo shellをインストールすれば、以下のコマンドで、コンテナ上で動作するMongoDBサーバに接続できました。

vagrant@precise64:/vagrant$ mongo mongodb.service.consul:27017/test
MongoDB shell version: 3.0.7
connecting to: mongodb.service.consul:27017/test
>

2015-11-24追記

consul membersの出力にあるように、Consul AgentはVM上の1台しか動作していません。コンテナ上にはConsul Agentはありません。

しかし、以下の2点により、結果的にmongodb.service.consul:27017でMongoDBに接続できるようになっています。

  • Appfileに書かれた設定により、ローカルホストの27017番への通信がコンテナに転送されること。この転送設定は、docker psの出力からも確認できる。
  • Ottoが自動生成したprovisionのスクリプトにより、VM上の/etc/consul.d/service.mongodb.jsonに「MongoDBはローカルホストにある」という設定が書き込まれること。以下に、このjsonファイルの内容を示す。
{
  "service": {
    "name": "mongodb",
    "tags": [],
    "port": 0
  }
}

AWSへのデプロイ

AWSへアプリケーションをデプロイする際は、事前にotto infraコマンドと、otto buildコマンドを実行する必要があります。内部的には前者はTerraform、後者はPackerが使われています。今回の本題ではないので、詳細は割愛。

  • otto infra: 複数アプリケーションの共通基盤となるインスタンスを、AWSにデプロイ
  • otto build: デプロイ対象のアプリを含むAMIを作成(将来的にはコンテナ作成なども対応予定とのこと)

以下は、otto buildeのログ出力の例です。よく見てみると、otto devのときと違い、ログにdockerやmongodb関係のメッセージが出ていないのがわかります。つまり、AMIの作成時点で、dependencyの情報が無視されてしまっているようです。

happyturn% otto build
==> Detecting infrastructure credentials for: otto-getting-started (aws)
    Cached and encrypted infrastructure credentials found.
    Otto will now ask you for the password to decrypt these
    credentials.

Encrypted Credentials Password
  Infrastructure credentials are required for this operation. Otto found
  saved credentials that are password protected. Please enter the password
  to decrypt these credentials. You may also just hit <enter> and leave
  the password blank to force Otto to ask for the credentials again.

  Enter a value:
==> Querying infrastructure data for build...
==> Building deployment archive...
==> Building deployment artifact with Packer...
    Raw Packer output will begin streaming in below. Otto
    does not create this output. It is mirrored directly from
    Packer while the build is being run.

otto output will be in this color.

==> otto: Prevalidating AMI Name...
==> otto: Inspecting the source AMI...
==> otto: Creating temporary keypair: packer 563377c3-5671-2d2f-a472-67471e05270c
==> otto: Creating temporary security group for this instance...
==> otto: Authorizing access to port 22 the temporary security group...
==> otto: Launching a source AWS instance...
    otto: Instance ID: i-665669d9
==> otto: Waiting for instance (i-665669d9) to become ready...
==> otto: Waiting for SSH to become available...
==> otto: Connected to SSH!
==> otto: Provisioning with shell script: /var/folders/4s/c_r4lqx92wnd6ml3tz8q6mm80000gn/T/packer-shell148828306
==> otto: Uploading /Users/myoshiz/Otto/otto-getting-started/.otto/compiled/app/foundation-consul/app-build/ => /tmp/otto/foundation-1
==> otto: Provisioning with shell script: /var/folders/4s/c_r4lqx92wnd6ml3tz8q6mm80000gn/T/packer-shell567335967
    otto: [otto] Installing Consul...
    otto: [otto] Installing dnsmasq for Consul...
    otto: [otto] Configuring consul service: otto-getting-started
==> otto: Uploading /var/folders/4s/c_r4lqx92wnd6ml3tz8q6mm80000gn/T/otto-slug-758424784 => /tmp/otto-app.tgz
==> otto: Provisioning with shell script: build-ruby.sh
    otto: [otto] Waiting for cloud-config to complete...
    otto: [otto] Adding apt repositories and updating...
    otto: [otto] Installing Ruby, Passenger, Nginx, and other packages...
    otto: [otto] Installing Bundler...
    otto: [otto] Extracting app...
    otto: [otto] Adding application user...
    otto: [otto] Setting permissions...
    otto: [otto] Configuring nginx...
    otto: [otto] Bundle installing the app...
    otto: Fetching gem metadata from https://rubygems.org/..........
    otto: Fetching version metadata from https://rubygems.org/..
    otto: Installing rack 1.6.4
    otto: Installing rack-protection 1.5.3
    otto: Installing tilt 2.0.1
    otto: Installing sinatra 1.4.6
    otto: Using bundler 1.10.6
    otto: Bundle complete! 1 Gemfile dependency, 5 gems now installed.
    otto: Gems in the groups development and test were not installed.
    otto: Bundled gems are installed into ./vendor/bundle.
    otto: [otto] ...done!
==> otto: Stopping the source instance...
==> otto: Waiting for the instance to stop...
==> otto: Creating the AMI: otto-getting-started 1446213571
    otto: AMI: ami-c03e4daa
==> otto: Waiting for AMI to become ready...
==> otto: Terminating the source AWS instance...
==> otto: Cleaning up any extra volumes...
==> otto: No volumes to clean up, skipping
==> otto: Deleting temporary security group...
==> otto: Deleting temporary keypair...
Build 'otto' finished.

==> Builds finished. The artifacts of successful builds are:
--> otto: AMIs were created:

us-east-1: ami-c03e4daa
==> Storing build data in directory...
==> Build success!
    The build was completed successfully and stored within
    the directory service, meaning other members of your team
    don't need to rebuild this same version and can deploy it
    immediately.

最後に、otto deployコマンドを実行すると、ここで作ったAMIをもとに、インスタンスが作成されます。

happyturn% otto deploy
==> Detecting infrastructure credentials for: otto-getting-started (aws)
    Cached and encrypted infrastructure credentials found.
    Otto will now ask you for the password to decrypt these
    credentials.

Encrypted Credentials Password
  Infrastructure credentials are required for this operation. Otto found
  saved credentials that are password protected. Please enter the password
  to decrypt these credentials. You may also just hit <enter> and leave
  the password blank to force Otto to ask for the credentials again.

  Enter a value:
aws_security_group.app: Creating...
  description:                         "" => "Managed by Terraform"
  egress.#:                            "" => "1"
  egress.482069346.cidr_blocks.#:      "" => "1"
  egress.482069346.cidr_blocks.0:      "" => "0.0.0.0/0"
  egress.482069346.from_port:          "" => "0"
  egress.482069346.protocol:           "" => "-1"
  egress.482069346.security_groups.#:  "" => "0"
  egress.482069346.self:               "" => "0"
  egress.482069346.to_port:            "" => "0"
  ingress.#:                           "" => "1"
  ingress.482069346.cidr_blocks.#:     "" => "1"
  ingress.482069346.cidr_blocks.0:     "" => "0.0.0.0/0"
  ingress.482069346.from_port:         "" => "0"
  ingress.482069346.protocol:          "" => "-1"
  ingress.482069346.security_groups.#: "" => "0"
  ingress.482069346.self:              "" => "0"
  ingress.482069346.to_port:           "" => "0"
  name:                                "" => "otto-getting-started-571aac33"
  owner_id:                            "" => "<computed>"
  vpc_id:                              "" => "vpc-571aac33"
aws_security_group.app: Creation complete
aws_instance.app: Creating...
  ami:                               "" => "ami-c03e4daa"
  availability_zone:                 "" => "<computed>"
  ebs_block_device.#:                "" => "<computed>"
  ephemeral_block_device.#:          "" => "<computed>"
  instance_type:                     "" => "t2.micro"
  key_name:                          "" => "otto-571aac33"
  placement_group:                   "" => "<computed>"
  private_dns:                       "" => "<computed>"
  private_ip:                        "" => "<computed>"
  public_dns:                        "" => "<computed>"
  public_ip:                         "" => "<computed>"
  root_block_device.#:               "" => "<computed>"
  security_groups.#:                 "" => "<computed>"
  source_dest_check:                 "" => "1"
  subnet_id:                         "" => "subnet-9b6163c2"
  tags.#:                            "" => "1"
  tags.Name:                         "" => "otto-getting-started"
  tenancy:                           "" => "<computed>"
  vpc_security_group_ids.#:          "" => "1"
  vpc_security_group_ids.1716715879: "" => "sg-fd872d9b"
aws_instance.app: Creation complete

Apply complete! Resources: 2 added, 0 changed, 0 destroyed.

The state of your infrastructure has been saved to the path
below. This state is required to modify and destroy your
infrastructure, so keep it safe. To inspect the complete state
use the `terraform show` command.

State path: /var/folders/4s/c_r4lqx92wnd6ml3tz8q6mm80000gn/T/otto-tf083753006/state

Outputs:

  url = http://ec2-52-91-29-204.compute-1.amazonaws.com/

AWSへのデプロイ結果の確認

起動したインスタンスへは、ubuntuユーザでSSH接続できます。AWSへの最初の接続時に、Ottoが、SSH接続に使う証明書を尋ねてくるので、そこで正しく設定していれば以下のコマンドで接続できるはずです。

happyturn% ssh ubuntu@52.91.29.204
Welcome to Ubuntu 14.04.3 LTS (GNU/Linux 3.13.0-63-generic x86_64)

 * Documentation:  https://help.ubuntu.com/

  System information as of Fri Oct 30 14:15:28 UTC 2015

  System load: 0.39              Memory usage: 5%   Processes:       80
  Usage of /:  15.9% of 7.74GB   Swap usage:   0%   Users logged in: 0

  Graph this data and manage this system at:
    https://landscape.canonical.com/

  Get cloud support with Ubuntu Advantage Cloud Guest:
    http://www.ubuntu.com/business/services/cloud

ローカル環境の場合と違い、dockerコマンドはインストールされていません。

ubuntu@ip-10-0-2-222:~$ which docker
ubuntu@ip-10-0-2-222:~$

Consulはインストールされています。ただし、依存関係にあるアプリケーションが動作していないので、現時点ではあまり意味はありません。

ubuntu@ip-10-0-2-222:~$ consul members
Node           Address          Status  Type    Build  Protocol  DC
ip-10-0-2-222  10.0.2.222:8301  alive   client  0.5.2  2         dc1
ip-10-0-2-6    10.0.2.6:8301    alive   server  0.5.2  2         dc1

余談ですが、Rubyアプリケーションは /srv/otto-app以下にインストールされていました。

ubuntu@ip-10-0-2-222:~$ ls -la /srv/otto-app/
total 40
drwxr-xr-x 5 otto-app otto-app 4096 Oct 30 14:03 .
drwxr-xr-x 3 root     root     4096 Oct 30 14:03 ..
-rw-r--r-- 1 otto-app otto-app  307 Oct 30 13:24 app.rb
drwxr-xr-x 2 otto-app otto-app 4096 Oct 30 14:03 .bundle
-rw-r--r-- 1 otto-app otto-app   42 Oct 30 13:24 config.ru
-rw-r--r-- 1 otto-app otto-app   57 Oct 30 13:24 Gemfile
-rw-r--r-- 1 otto-app otto-app  267 Oct 30 13:24 Gemfile.lock
-rw-r--r-- 1 otto-app otto-app  160 Oct 30 13:24 README.md
drwxr-xr-x 3 otto-app otto-app 4096 Oct 30 14:03 vendor
drwxr-xr-x 2 otto-app otto-app 4096 Oct 30 13:24 views

最後に

OttoのDependencyの機能はまだまだ開発途上であることが、今回の解説でわかって頂けたかと思います。ただ、Ottoの概念自体は、一度理解してしまえばわかりやすいですし、開発環境と本番環境を一元管理できれば確かに理想的ではあります。個人的には、今後しばらくOttoに注目していきたいと思っています。

Viewing all 107 articles
Browse latest View live