としたにあんの左脳

備忘録です.

さくらのクラウドで名前解決ができない時の解決法

さくらクラウドインスタンスを立ち上げた直後にapt-getをしたらエラーになった.

いろいろ調べると名前解決ができていないらしい.

ここを参考に

$ sudo dpkg-reconfigure resolvconf 
$ sudo reboot

してみる....

まだ名前解決できない.

/etc/network/interfacesdns-nameserversを変更して

dns-nameservers 8.8.8.8

にしたらpingが通った!

(追記) /etc/hostsでそのhostを登録していないと名前解決がうまくいかない場合があるみたいです.

fatal error: unsupported/Eigen/MatrixFunctions: No such file or directory

OpenCV

OpenCVをソースからビルドしていて,以下のエラーが出た.

$ cmake ..
$ make
...
fatal error: unsupported/Eigen/MatrixFunctions: No such file or directory
...

原因はEigenをBitBucketから持ってきて,手動で/usr/local/includeにインストールしていたからだった.

OpenCVは内部にもつEigenをビルドに使用するようだが,パスの通った場所にあるEigenを使おうとしたことによりビルドに失敗したようだ.

OpenCVをインストールするときはEigenをインストールしていないか確認したほうがいい.

mongoのISODateのtimezone問題に対処する

mongodbのISODateはtimezoneに対応してないらしい. いろいろ検証してみて,最後はAggregation Frameworkでどうにかしてみようと思う.

準備

以下のスクリプトを動かすとサンプルデータがmongodbに入る.

データの形式は以下の通り.

日本時間で2013/01/01 00:00:00 から 2013/01/02 23:59:00 の1分おきのデータが入る.

2013/01/01 は火曜日 2013/01/02は水曜日

{
        "_id" : ObjectId("52af0bc3090fdd09be0c6ff5"),
        "gmt_time" : ISODate("2012-12-31T15:00:00Z"),
        "gender" : "female",
        "jst_strf" : "2013/01/01 00:00:00"
}

Timezone問題

mongodbのISODateは現在のところ,GMTにしか対応していない.

先ほど挿入したデータも,タイムゾーン付きでdatetimeを生成したので,jst_strfは文字列なので日本時間で挿入されている(2013/01/01 00:00:00 ~ 2013/01/02 23:59:00)

一方で,gmt_timeはTimezone情報が捨てられてしまうのでGMTで挿入されている.(2012-12-31T15:00:00 ~ 2013-01-02T14:59:00)

このコレクションに対して,クエリをかけるときに問題が発生する.

例えば,日本時間の1月1日のデータだけを取り出したいときなどである.

以下のクエリをかけると,当然,GMTに対してクエリがかかるので,日本時間では,2013/01/01 09:01:00 ~ 2013/01/02 08:59:00の結果がかえってくる.

> db.gender.find({gmt_time:{$gt:ISODate("2013-01-01T00:00:00Z"), $lt:ISODate("2013-01-01T23:59:59Z")}})
{ "_id" : ObjectId("52af0bc4090fdd09be0c7212"), "gmt_time" : ISODate("2013-01-01T00:01:00Z"), "gender" : "female", "jst_strf" : "2013/01/01 09:01:00" }
...
{ "_id" : ObjectId("52af0bc4090fdd09be0c77b0"), "gmt_time" : ISODate("2013-01-01T23:59:00Z"), "gender" : "male", "jst_strf" : "2013/01/02 08:59:00" }

そこで,次に考えるのは,クエリに使う時刻に,予め9時間のオフセットを引いておくことだ.

> db.gender.find({gmt_time:{$gt:ISODate("2012-12-31T15:00:00Z"), $lt:ISODate("2013-01-01T14:59:59Z")}})
{ "_id" : ObjectId("52af0bc4090fdd09be0c6ff6"), "gmt_time" : ISODate("2012-12-31T15:01:00Z"), "gender" : "male", "jst_strf" : "2013/01/01 00:01:00" }
...
{ "_id" : ObjectId("52af0bc4090fdd09be0c7594"), "gmt_time" : ISODate("2013-01-01T14:59:00Z"), "gender" : "male", "jst_strf" : "2013/01/01 23:59:00" }

これで,本来取り出したかった2013/01/01 00:01:00 ~ 2013/01/01 23:59:00 のデータが取り出せる.

しかし,何かイケてない

いろいろと使いまわせない感じかする.

後述するアグリゲーションフレームワークを利用した場合,曜日でのクエリをかけることができるのだが,上記の方法だとTimezone問題に対応できない.

aggregation framework

mongodbにはaggregation frameworkというものが存在する.

Aggregations operations process data records and return computed results. Aggregation operations group values from multiple documents together, and can perform a variety of operations on the grouped data to return a single result.

簡単に言うと,レコードに対して処理を行い,計算結果のみを返してくれものらしい.

> db.gender.aggregate({$match:{gender:'male'}})
{
        "result" : [
                {
                        "_id" : ObjectId("52af0bc4090fdd09be0c6ff6"),
                        "gmt_time" : ISODate("2012-12-31T15:01:00Z"),
                        "gender" : "male",
                        "jst_strf" : "2013/01/01 00:01:00"
                },
                 ...
        ],
        "ok" : 1
}

gender='male'にmatchするものを取り出すというアグリゲーションフレームワークのクエリをかけると,result というリストに結果のドキュメントが入ってくる.

ソートをする場合は

>db.gender.aggregate({$match:{gender:'male'}}, {$sort:{gmt_time:1}})

リミットをかける場合は

> db.gender.aggregate({$match:{gender:'male'}}, {$sort:{gmt_time:1}}, {$limit:1})

ISODataに対するAggregate

Aggregation Framework Operators - Date Operators

上記のリンクにあるように,Aggregation Frameworkを利用することで,ISODateから,年や月,曜日などを数値として取り出すことができる.

> db.gender.aggregate({$project:{weekday:{$dayOfWeek:"$gmt_time"}}})
{
        "result" : [
                {
                        "_id" : ObjectId("52af0bc4090fdd09be0c7423"),
                        "weekday" : 3
                },
                 ....
                {
                        "_id" : ObjectId("52af0bc5090fdd09be0c7b34"),
                        "weekday" : 4
                }
                 ...
        ],
        "ok" : 1
}

このように,gmt_timeから曜日のみを取り出すことが可能だ.

Aggregation Frameworkは処理をつなげることができる(パイプライン処理と呼ばれている)

以下の処理は,曜日が火曜日のドキュメント群のみを取り出す処理である.

> db.gender.aggregate(
                   {$project:{jst_strf:"$jst_strf",weekday:{$dayOfWeek:"$gmt_time"}}},
                   {$match: {weekday:{$gte:3, $lt:4}}}
)

{
        "result" : [
                {
                        "_id" : ObjectId("52af0bc4090fdd09be0c7211"),
                        "jst_strf" : "2013/01/01 09:00:00",
                        "weekday" : 3
                },
                ...
                {
                        "_id" : ObjectId("52af0bc4090fdd09be0c77b0"),
                        "jst_strf" : "2013/01/02 08:59:00",
                        "weekday" : 3
                }
         ],
         "ok":1
}

これは,

  1. jst_strfweekdayのみをフィールドにもつドキュメント群をつくる.($project)

  2. weekdayが3以上4未満のドキュメント群をつくる($match)

を連続して行っている.

この結果では火曜日のドキュメント群を取り出したつもりなのに,1/1と1/2のドキュメントが混じっている.

ここで,思い出してほしいのは,2013/01/02は火曜日だった.一方,weekdayが3と言うのは火曜日であることを表す.(日=1, 月=2, 火=3,...)

ズレが生じてしまっている.これはweekdayがgmt_dateから計算されているためである.(このドキュメントのISODateは2012-12-31T15:00:00であるため)

本当は月曜日のドキュメントがほしいのに火曜日のドキュメントがとれてしまっている.

対処法

対処法として,以下のリンクを教えてもらった. Timezone support in date operators at query time How to agregate by year-month-day on a different timezone

要は,最初にISODateにTimezone分だけ時間を加算してから,残りの処理を行えばいいらしい.

> db.gender.aggregate(
                          {$project: {jst_strf:"$jst_strf", gmt_time:{$add:["$gmt_time", 9 * 60 * 60 * 1000]}}},
                          {$project:{jst_strf:"$jst_strf",weekday:{$dayOfWeek:"$gmt_time"}}}, 
                          {$match: {weekday:{$gte:3, $lt:4}}}
)

{
        "result" : [
                {
                        "_id" : ObjectId("52af0bc3090fdd09be0c6ff5"),
                        "jst_strf" : "2013/01/01 00:00:00",
                        "weekday" : 3
                },
                ...
                {
                        "_id" : ObjectId("52af0bc4090fdd09be0c7594"),
                        "jst_strf" : "2013/01/01 23:59:00",
                        "weekday" : 3
                }
         ],
         "ok":1
}

これで正しくとれた!

{$project: {jst_strf:"$jst_strf", gmt_time:{$add:["$gmt_time", 9 * 60 * 60 * 1000]}}},

このように,あらかじめISODateを日本の時差の分だけずらしたあとにクエリをかけて上げればよい.

まとめ

mongoのtimezoneで困ったらAggregation Frameworkを使いましょう.

次回以降,mongoのC++ DriverでAggregation Frameworkを触ることについて書いてみようかなと思います.

Fluentd Casual Talks #3いってきた

Fluentd Casual Talks #3にいってきたので感想を残しときます.

@tagomoris さん

スライド

Norikraのfluentプラグインのお話. Norikraというのとfluentdを連携してSQLチックにフィルタリングしてゴニョゴニョできるらしい.

一定時間ごとにGroupしてcountしたり,ネストされたハッシュのSELECTとかもできるらしい.

in_norikara/out_norikara は大規模向けを意識したもので,1台でやるならout_norikra_filterでOKらしい

試してみよう.

@sonots さん

スライド

Haikanko Yohoushi ってのを作ってるらしい.うまく使えれば運用が楽になりそうかな.社内の人にも使ってもらえるかな.とか思ったり.

Denaでは最大で1秒に10万行のログがfluentdで流れるらしい!

シャドウプロキシサーバをfluentdでもやったら良い感じっていうお話だった.性能評価をちゃんとやってらっしゃった.

@stanaka さん

LTSVと2014年のウェブシステムアーキテクチャの人.すごい.

  • Go のgo routineよさげ

  • Go早そう

  • GoでopenCVつかいたい

@okahashi117 さん

fluentdをWindowsでも使えるようにした話.

Windowsのコマンドプロンプト久々にみた.

直接話す機会があったので話をきいたら,大企業のシステムだとか,Windowsで実装されたセンサなどの需要が結構あるらしい.なるほど.

@kzk_mover さん

スライド

TDのCTOの方

プロダクションにfluentdいれるなら,ちゃんとモニタリングしなきゃなーと痛感した(実際,昨日落ちてた)

TreasureDataでtd-agentのモニタリングサービスを始めるらしい(しかも無料!)ので使ってみたいなー

@frsyuki さん

スライド すごい人.若い.

v11で無停止再起動とかできるらしい.

v11のライブリリース.

これ読んだら幸せになれそうだった.


Flaskでltsv形式のログを出してみた.

前置き

最近,いろいろな問題に直面して,ログの重要性を実感しました.そのため,複数のサーバに分散したログを収集する試みを行っています.

その際使用しているのがfluentdです.

fluentdの説明は割愛しますが,以下の記事が参考になると思います.

Fluentdで始めるリアルタイムでのログ有効活用

実運用上では,アプリケーションからファイルへ出力されるログをfluentでtailして収集することが多いと思います.

その際,ログを正規表現でパースしてあげる必要があるのですが,組み込みのフォーマットであれば,正規表現を書く必要がありません.

この組み込みフォーマットには以下のものが有ります.(参考: fluentd/lib/fluent/parser.rb - Githubより

  • apache

  • apache2

  • syslog

  • json

  • tsv

  • ltsv

  • csv

  • nginx

この中でアプリケーションで利用することを考えると,json tsv ltsv csv に絞られます.

ltsv形式は,かなり新しい形式らしく,tsvにkeyをつけたものらしいです.

LTSV FAQ - LTSV って何? どういうところが良いの?を参考にするといいと思います.

fluentdはログをJSON形式で保持します.上記の4つの中で,key-value形式になっているものはjsonltsvです.

tsv csvはkeyを持たないため,fluentdの設定で,tailしてきた内容に対して,keyを割り当てなければなりません.

この際問題になるのが,ひとつのログファイルで一貫して同じ形式のログを出力しなければならないことです.ログの要素数を可変にしたい場合にはこれらは向きません.

残るは.jsonltsvです.これらは.ログの効果としては同じ能力を持っていると言えます.(多分)

この際の選択基準として.

  • 汎用性を選ぶならjson

  • 生ログの可読性を選ぶならltsv

かなと思います.

今回は,fluentdの運用をうまくできるか不安だったので,生ログでも見やすいltsvを選択しました.

本題

で.本題ですが,Flaskでltsvのログを出力しているサンプルが見当たらなかったので.作りました.とはいっても,通常のPython loggingと同じですね.

出力:

time:2013-12-04 01:17:34,780    level:ERROR     method:hello_world      message:distance        value:44  

fluentdに読ませつつ.要素数が可変で,可読性を取っておきたいならltsvはいいんじゃないかなと思います.

ちなみに,ltsvでログを取って,fluentd + elasticsearch + Kibana3 の運用もしているので,時間があれば書きたいなぁ.

(追記)

このコードはイケてないのでリベンジした

http://toshitanian.hatenablog.com/entry/2013/12/10/130439

Boost.Logのエラー

Boost1.54でロガー機能が追加されたらしく,使ってみようとしたところ,

Undefined symbols for architecture x86_64:
  "boost::log::v2s_mt_posix::record_view::public_data::destroy(boost::log::v2s_mt_posix::record_view::public_data const*)", referenced from:
      boost::log::v2s_mt_posix::record::reset() in main.cpp.o

リンクできない...

OSは OSX10.9,boostはhomebrewで入れたboost1.54 cmakeでFindPackageしてBoostをリンクしようとしている.

ググッた結果,

ここ

に答えが書いてあった....つまり

staticライブラリをリンクしようとして見つからない!

ってことらしい.

解決方法としては,sharedライブラリをリンクする必要があるみたいで,CMakeLists.txt

ADD_DEFINITIONS(-DBOOST_LOG_DYN_LINK)

を追記してあげるといいらしい.

あと,つまずきそうなのは,CMakeLists.txtFIND_PACKAGElogを入れるのを忘れちゃった!ってとこな気がする.

忘れないようにせねば.

これで快適Boost.Log生活!