たごもりすメモ

コードとかその他の話とか。

scribedのセットアップ手順ひと通り(hdfs書き込み有効版)

ログの収集をscribeでやりたいぜ! と思ったがREADMEに書いてある通りにやろうとしてもうまくいかず七転八倒し、しかもその上hdfsに書き込もうとしたらHadoopまわりで更に苦悶の日々を送るという苦難の道のりをようやく完走したので、それについて書いてみる。
正直に言ってかなりテキトーにやっつけたが、動くバイナリができているから多分大丈夫だろう。細かい問題については、知らん。

(1/24追記 CentOS 5.5 でもビルドできたので何箇所かに追記しました)

なお今回は Fedora 13 で実行しました。(01/24追記)CentOS 5 だとboostが古いので、そこを自分でどうにかする必要がある(素のCentOS 5.5だとscribeのビルド時にboostのバージョン 1.36 以降を要求されて失敗する)。UbuntuDebian だと割といけそうな話は多いが、試してないのでよくわからん。

scribeその他のバージョンについて

まずscribeもその下地となるThriftもかなりカジュアルにコードが変更され、しかも相互に非互換な状態にあっさりなってくれたりするので注意が必要だ。リリースされているバージョンではビルドが通らないというのはごく普通のことなので、最初に覚悟しておこう。

「この門をくぐる者は一切の希望を捨てよ」

なおこのエントリでできるだけ詳しく書くつもりではあるが、boostのバージョン違いなどによって容易にビルド不可能な状態に陥ることが予想される。責任はとれない。
覚悟が完了したところで、必要なものを手に入れる。

  • Thriftに必要なもの
    • http://wiki.apache.org/thrift/ThriftRequirements から必要なものを確認してインストールしておく
    • gcc は 4.4 以降のものを入れておくこと(後述)
    • Language Requirementsは必要そうなものはできるだけ入れておく
      • 自分は C#Erlang は無視し、PHPは明示的に外した
    • なおThriftの要求するboostは 1.33+ だが scribe が 1.36+ を要求するため、1.36以降のものが入っている(もしくは自分でビルドして入れる)必要がある (1/24追記)
  • Thrift
    • リリースパッケージ(現時点で0.5.0)には既に問題があることがわかっていて使いものにならない
      • 何をどうやっても php-config が無いとかいって configure がこける
    • http://instant.thrift-rpc.org/ から適当なものを手に入れるか、http://thrift.apache.org/download/ にあるリポジトリからtrunkをチェックアウトする
      • 自分は Instant Releases から r1027248 を手に入れた(現時点で最新)
  • Scribe
    • リリースパッケージ(現時点で2.2が最新)は既に問題があることがわかっていて使いものにならない
      • HDFSまわりの問題なので、そこが必要なければ 2.2 でもなんとかなる、かも
    • おとなしくfacebookarchive/scribe · GitHubからcloneするか最新状態のtarballを手に入れる(「Download」から)
      • 2011/01/05 時点のものを使用した
  • Hadoop (hdfsを使用する場合)
    • Apacheからリリースされているものは使用できない
      • libhdfs(C++バインディング) のAPIがCDH版で派手に変更されていて、Scribeのhdfs実装は完全にCDH版をベースに書かれている
    • Cloudera版のもの(CDH)を Index of /cdh/3 から手に入れる
      • なおバージョンはCDH3 beta3 (0.20.2+737) は libhdfs が壊れて使用できない状態なので注意すること
      • CDH3 beta2 (0.20.2+320) を使用した
    • なお、これは書き込み先のHadoopクラスタと合わせる必要がある
      • 0.20.2+320 と 0.20.2+737 は相互に通信できないし、0.20.2+320 と 0.20.2 (Apache版) も相互に通信できない
      • 書き込み先のHadoopクラスタがCDH3beta2 (0.20.2+320) 以外の場合は拝み倒してなんとかそのバージョンに合わせてもらおう
      • HDFSのデータ構造はバージョンを変更すると互換性が無いことがあり、その場合は初期化が必要なので注意すること
    • Java は Sun JDKを適当に
      • 自分は JDK 1.6.0_23

依存パッケージの類はインストール済みとし Thrift/Scribe/Hadoop はtarball等が適当に tmp ディレクトリにでも展開されているものとする。

ビルド

ビルド前に共有ライブラリの配置とリンク設定について確認しておこう。ライブラリを /usr/local/lib などに置いた場合は /etc/ld.so.conf.d/boost などを作成し、そこに /usr/local/lib と書いておく。また書いたあとでいちおう sudo ldconfig を実行しておこう。
これは今後すべての手順で注意すること。libthrift, libfb303, libscribe のデフォルトのインストール先が /usr/local/lib のようなので、最初にこのディレクトリは無条件で ld.so.conf.d に書いてしまっていいかもしれない。

boost (1/24追記)

CentOS 5.5の場合はscribeが boost 1.33.1 を拒否する(1.36以上を要求する)ので、諦めて最新版を1.36以上1.45以下のものをソースコードからビルドする。(2011/5/9 詳しくは boost 1.46 で boost::filesystem の非互換に死んだ - tagomorisのメモ置き場 に書いた。参照のこと。)

$ sudo ./bjam --without-mpi --prefix=/usr/local install

MPIいらないよね、と外してあるが明確な根拠はない。とりあえず動いてる。

Thrift

Thrift本体のソースコードを展開し、以下のコマンドでビルドする。これはPHPを明示的に外してある。(2011/10/26追記: Thrift 0.7.0の場合 --disable-gen-php と --without-php があるとエラーになる? ので、外すとphp入ってない環境でも正常にビルドできた。)

$ ./bootstrap
$ ./configure --disable-gen-php --without-php --with-boost=/usr/local/lib && make
$ sudo make install
fb303

Thriftのソースコードに含まれている fb303 というライブラリも必要なので、これも続けてビルド&インストールする。

$ cd contrib/fb303
$ ./bootstrap
$ ./configure && make   # するとmakeがこける場合があるので cpp/gen-cpp/fb303_types.cpp から fb_status:: の部分を削る
$ ./configure && make   # 削ってから再度実行
$ sudo make install

fb_status云々の部分、gcc 4.4で採用された Enum をNamespaceとして使うというC++拡張に関係するらしい。gcc 4.4なら大丈夫のはずなんだが、自分の環境だと必ずひっかかった。なにかオプションを見落しているのかもしれない。
とりあえずNamespaceとしての指定部分 "fb_status::" を削ればビルドが通って問題なく動作するので、そのようにする。

Hadoop

Hadoopはビルドというよりはtarballを展開して設置しておくだけ。
Hadoopクラスタ自体としての処理をやらせる(MapReduce計算ノードになるとか、HDFSをホストするデータノードになるとかの)場合にはあれこれ設定などが必要だが、ここではscribeサーバにはやらせないものとする。やらせたい人はHadoopインストール記事などを適宜ググるとよい。

以下の手順で /usr/local に展開し /usr/local/hadoop でアクセスできるようにする。

$ cd /usr/local
$ sudo tar xzf /path/to/archive/hadoop-0.20.2+320.tar.gz
$ sudo ln -s hadoop-0.20.2+320 hadoop

そして環境変数に以下の準備を行う。例はbashの場合。必要なのは JAVA_HOME と CLASSPATHhadoopコマンドも使うので、フルパス指定が面倒ならPATHに加えておく。

$ export JAVA_HOME=/usr/java/jdk1.6.0_23 # ここは自分のインストールパスで適当に
$ export CLASSPATH=`find /usr/local/hadoop-0.20.2+320 -name '*.jar' | grep -v 'test' | grep -v 'example' | perl -e '@jars=<STDIN>;chomp @jars; print join(":",@jars);'`
$ export PATH=$PATH:/usr/local/hadoop/bin

なお上記の例では考えるのが面倒だったので手当たり次第にjarをCLASSPATHに加えてある。こだわる人は適当に選んで入れること。また他にもJavaのプロダクトを扱っている場合には適切にどうにかしていただきたい。

またscribeのビルドの準備のため、以下のような感じで各種共有ライブラリにパスが通るようにしておく。

$  cat /etc/ld.so.conf.d/scribe.conf 
/usr/local/lib
$ cat /etc/ld.so.conf.d/jvm.conf 
/usr/java/jdk1.6.0_23/jre/lib/amd64
/usr/java/jdk1.6.0_23/jre/lib/amd64/server
$ cat /etc/ld.so.conf.d/hdfs.conf 
/usr/local/hadoop/c++/Linux-amd64-64/lib
$ sudo ldconfig

あと /usr/lib64 も明示しないといけないケースがあるかも。

Scribe

(1/24 CentOS 5.5 でのビルド試行のうえ、いくつか修正済み)

scribeのソースコードを展開し、以下のようにビルドとインストールを行う。ただし configure が難物なので、とりあえずそこまで。

$ ./bootstrap.sh
$  ./configure --with-hadoop=/usr/local/hadoop --enable-hdfs \
     CPPFLAGS="-I/usr/java/jdk1.6.0_23/include -I/usr/java/jdk1.6.0_23/include/linux -I/usr/local/hadoop-0.20.2+320/src/c++/libhdfs" \
      LDFLAGS="-L/usr/java/jdk1.6.0_23/jre/lib/amd64/server -L/usr/local/hadoop-0.20.2+320/c++/lib -ljvm -lhdfs"

言うまでもないがCPPFLAGSやLDFLAGSのJDKのパスやアーキテクチャ(amd64 or i386)は自分の環境にあわせて適切に。ただし32bit環境ではscribeはうまく動作しないような疑いを抱いている。
またHadoopのディレクトリツリー構造はバージョンによって変わっている(少なくとも0.20.2と0.21.0ではかなり違う)ので、CPPFLAGSには hdfs.h が含まれるパスを、LDFLAGSには libhdfs.so が含まれるパスを正しくセットしてやるよう注意すること。

なおboostのビルド・インストール方法によっては、以下のようにマルチスレッド対応のlibboostの使用を明示しなければいけない場合があるので注意。

$ ./configure --with-boost-system=boost_system-mt --with-boost-filesystem=boost_filesystem-mt  \
     --with-hadooppath=/usr/local/hadoop \
     --enable-hdfs \
     CPPFLAGS="-I/usr/java/jdk1.6.0_23/include -I/usr/java/jdk1.6.0_23/include/linux" \
     LDFLAGS="-L/usr/java/jdk1.6.0_23/jre/lib/amd64 -L/usr/java/jdk1.6.0_23/jre/lib/amd64/server -ljvm -lhdfs"

boostのパスを変えていたりする場合は --with-boost=/usr/local としたりする。また上記の例ではboostは boost_system-mt などとしているが、このあたりは自分の環境にあわせて変えること。ただし普通はマルチスレッド環境を考えてビルドするのが良いと思われるので -mt つきの名前で指定した方が安全だと思われる。というか自分の場合、mt 指定しないとうまくビルドが通らなかった(ような気がする)。
Hadoopのパスや --enable-hdfs は見てわかる指定なので割愛。

configure がうまく通ってもその後の make で失敗したとき、ライブラリが見付からないとかリンクに失敗するとかいうエラーであれば configure に戻ってきて make clean 後にオプションを変えてやり直してみよう。

で、次にビルドする。

$ make

makeだけなんだが、例によって失敗する。ResultCode 云々というエラーメッセージが出る場合はfb303のときと同じ問題なので src/gen-cpp/scribe_types.cpp の "ResultCode::" というnamespace指定を削除し、再度 make する。
makeが完走したらインストール。

$ sudo make install

これで(デフォルト指定の場合は) /usr/local/bin/scribed が正常にインストールされていれば完了だ。

設定と起動

scribedをどう設定・構成するかはまたこの次にして、とりあえず起動するだけであれば以下のような設定を /etc/scribe.conf とでも名付けたファイルに書いておく。これはscribeのリポジトリの examples/example1.conf からほぼそのまま流用。examples 以下は大変参考になるので眺めると良い。

port=1463
max_msg_per_second=2000000
check_interval=3

# DEFAULT
<store>
category=default
type=buffer

target_write_size=20480
max_write_interval=1
buffer_send_rate=2
retry_interval=30
retry_interval_range=10

<primary>
type=file
fs_type=std
file_path=/var/log/scribe_archive
base_filename=access_log
max_size=1000000
add_newlines=0
</primary>

<secondary>
type=file
fs_type=std
file_path=/tmp/overflow_logs
base_filename=thisisoverwritten
max_size=3000000
</secondary>
</store>

上記設定ファイルを指定して scribed を起動する。/usr/local/bin にパスが通っていればコマンドをそのまま打てばいい。

$ sudo mkdir /var/log/scribe_archive # ディレクトリくらい作っておこうか
$ sudo mkdir /tmp/overflow_logs
$ sudo scribed /etc/scribe.conf
$ sudo nohup scribed /etc/scribe.conf >> /var/log/scribed.log & # 端末から切り離して起動する場合はこっち

起動したデーモンに向かってログを投げてやるには、テスト程度なら examples に入っている scribe_cat が使える。適当な場所にコピーしておくとよろし。

$ echo "hogehoge pospos "`date +%Y%m%d` | scribe_cat -h localhost:1463 HOGE

これを実行後に /var/log/scribe_archive/HOGE ディレクトリが作成され、そこにログファイルが作成されて上記ログが書き込まれていれば成功だ。またファイルをオープンしたことは scribed のログ(上記のnohupパターンなら /var/log/scribed.log)に書き込まれているはずなので、そちらを確認すること。対象のファイルを開けなかった場合などにはその旨のエラーが出ているはず。

ついでに停止方法について書いておくと、直接シグナルを送ると何が起きるのか(受け取った後のログが適切に処理されるのか)がよくわからない。examplesディレクトリに scribe_ctrl というスクリプトがあるので、これを使うのが良い。

$ sudo scribe_ctrl stop 1463

起動したユーザの権限で実行し、最後の引数には起動しているポートを指定してやること。

なお設定にもあれこれパターンがあると思うし、設定ファイル自体にも実は罠が多い。後日エントリを別途書く予定。とりあえず上記設定パターンのようにprimaryもsecondaryもローカルディスクのような buffered store の構成はあまり意味がないと思う。

hdfsへの書き出し

さてhdfsへの書き出しはもうひと手間必要。まず既にhdfshdfs://node01.hadoop-cluster:50070/ というパスでアクセスできるものとする。

確認は以下のコマンドでできる。JAVA_HOMEやCLASSPATHは前述の通りに設定しておくこと。
ついでにscribeが書き込むためのディレクトリも作っておく。*1

$ hadoop fs -fs hdfs://node01.hadoop-cluster:50070/ -ls /
$ hadoop fs -fs hdfs://node01.hadoop-cluster:50070/ -mkdir /scribe
$ hadoop fs -fs hdfs://node01.hadoop-cluster:50070/ -mkdir /scribe/HOGE
$ hadoop fs -fs hdfs://node01.hadoop-cluster:50070/ -touchz /scribe/HOGE/hoge

なおこれらのコマンドが全く通らない場合、問題は完全にHadoopまわりのことなので、自力でなんとかしていただきたい。自分は知らん。いちおう今回直面して調べた現象だけを並べると以下のとおり。

  • Apache版のHadoopとCDH版のHadoopは別物
    • バージョン番号が同じに見えても余裕で互換性ないので注意すること。混ぜるな危険。
  • CDH3のうちでも beta2 と beta3 は別物
    • scribeは CDH3 beta2 でしかマトモに使えないので、それに合わせてあげよう。

カテゴリごとのディレクトリもあらかじめ掘っておく必要があるので、default カテゴリにすべてお任せして勝手に掘ってほしい身としては悲しいが、いたしかた無し。
最後の -touchz コマンドは空の hoge というファイルを作るためのもので、本来は必要ない。必要ないはずなんだが、自分の環境ではなぜかこれをやっていない状態だと scribe からのhdfs上のファイルへの書き込みがうまくいかず、全く関係ないはずのファイル名で -touchz したとたんに成功しはじめるという怪現象が起きた。Hadoopに詳しい人に教えてほしい。度胸のある向きには touchz を実行せずにscribeの設定変更までやってみていただきたい。

scribed の設定と起動だが、その前に起動ユーザについて。できればhadoopクラスタを起動しているユーザ名と合わせておくことが望ましい(らしい)。詳しい理由は知らん。
一般ユーザ(ここでは仮に hadoop ユーザとする)の場合にこのユーザでscribedを起動すると、ログの書き込みもこのユーザ権限で実行できる必要があるので、そのあたりは設定の実施前に注意しておく。

$ sudo chown -R hadoop:hadoop /var/log/scribed.log /var/log/scribe_archive /tmp/overflow_logs

設定は例えばこんな感じ。単純だが primary を hdfs にして、そこに書き込めない場合はローカルディスクにバッファする。

port=1463
max_msg_per_second=2000000
check_interval=3

# DEFAULT
<store>
  category=default
  type=buffer

  target_write_size=20480
  max_write_interval=1
  buffer_send_rate=2
  retry_interval=30
  retry_interval_range=10

  <primary>
    type=file
    fs_type=hdfs
    file_path=hdfs://node01.hadoop-cluster:50071/scribe
    base_filename=access_log
    create_symlink=no
    rotate_period=hourly
    rotate_minute=0
    max_size=1000000
    add_newlines=0
  </primary>

  <secondary>
    type=file
    fs_type=std
    file_path=/tmp/overflow_logs
    base_filename=thisisoverwritten
    max_size=3000000
  </secondary>
</store>

hdfs:// のパスについて、なんで上では 50070 だったのに、ここでは 50071 なんだ! とか言われても知らん。今の俺に聞くな。Hadoopの世界は摩訶不思議。自分の環境については周囲の詳しい人に聞いてみよう。
HDFSに書き込むファイルは1時間ごとに rotate することにしてある。これは rotate のタイミングまでは他のプロセスから何が書かれてたか見えないため。HDFSはcloseされた後でないと中身が読めない? のかな? 早目に見えたほうがいいよね。

上記設定を /etc/scribed.conf (もしくは任意のファイル)に書いておいて scribed を起動。

$ sudo su - hadoop
$ nohup /usr/local/bin/scribed /etc/scribed.conf >> /var/log/scribed.log &

起動後に scribe_cat を実行してからログを確認して、以下のような感じでhdfsに書き込まれたことが出ていればOKだろう。(hdfs上のファイルが create/open できないというログが出ていれば -touchz を試してみよう。)

$ echo "hogehoge pospos "`date +%Y%m%d` | scribe_cat -h localhost:1463 HOGE
$ tail /var/log/scribed.conf

...
[Thu Jan  6 20:00:00 2011] "[HOGE] Opened file <hdfs://node01.hadoop-cluster:50071/scribe/HOGE/access_log-2011-01-06_00001> for writing" 
...

これで多分ちゃんと書き込まれているはずだが、心配であれば hadoop -get して書き込まれたはずのファイルを取得し、内容を確認しておこう。

以上!

おつかれさまでした!

あとでかく
  • なぜscribeは CDH3 beta2 でしか使用できないのか、あるいはHadoop/libhdfsの怪
  • 障害と設定変更と高負荷に強いscribedの構成方法
    • こっちは運用経験が蓄積されてからかなあ。設計段階で一度書いてみるかな?

*1:scribeは自動でディレクトリを掘ってくれない。なんとなく掘ってくれるためのコードがあるように見えた気もするんだけど……。