Winny裁判で金子氏の著作権侵害幇助が成立するという意見を述べた裁判官のところにバツをつけるたった一度きりのチャンスがやってきたというわけですよ!!!!!!!
というのを選挙広報を読んでて見付けた。プログラマのみんなもよく読んでみよう!
Operation Engineers' Casual Talks というイベントが行われます。で、そこで話すことになってます。
Operation Engineers' Casual Talks | イベントアテンド [ATND] でイベント作成・チケット販売・参加者の出欠管理
開催が12月中旬の金曜夜なので、定員100人に現状107人となってますが、たぶん余裕で3〜4割はドタキャン出ると思います。普通に師走で忙しい人が多いだろうし、これから忘年会が入るひととかいるだろうし。だから補欠枠をがつんと広げた上で補欠の人は全員来るくらいでいいと思いますね!
とはいえ詰まらない話しかしないのに人がいっぱい来て残念でしたということになってもアレです、が、自分の話はともかくfujiwaraさんの話とかトークセッションとか面白いことは確定しているようなものなので、みんな来るといいと思います。
「ほげエンジニア、の定義について」
ところでこのようなタイトルにしておいたんですが「わからん」「わからん」「何の話だ」とたいへん好評をいただいてます。多少分かるように、先日内容(の案)を書き出しておいたものから簡単に抜粋しておくと、以下のような話になる予定です。だいぶモヒモヒしく語ります。本番は4以降ですね。
時間の制約もあるので全部話せるか、一部削るかは分かりませんが、意識高くいきたいと思いますのでみなさまもぜひ意識を高めてご参加ください。面白い話にできるといいなーと思ってます。
1. 言葉の定義を確認しよう
言葉の意味は使う人によって異なる、プログラマとデザイナーと企画でも違う可能性がある、それを常に意識しておくべき
2. 誤解の余地の少ない言葉を使おう
別の職種の人と共通して理解できる言葉を使おう
3. 言葉の原義と一般的な意味を大事にしよう
「エンジニア」の定義について
4. 自分のことをどう呼ぶかを大切にしよう
自分のことを何と呼ぶか、は、自分が何をする人なのか、という自意識に影響を与える、無意識の内に縛る
「インフラエンジニア」という言葉を使う人は「UIを書かない人」だと思っていないか? 「コードを書かない人」だと思っていないか?
世の中の似た職種の人が何と自称しているか、を真似て自分の役職を決めるのはやめよう
自分が何をどう解決する人なのかを考え直してみよう
5. コンピュータシステムスタックを意識しよう
あなたはどこをやる人?
・いわゆる基盤部分の人は物理面に近いところ(はエンジニアリングじゃないかも)からサーバサイドアプリケーションまでやれないと駄目
・UNIXの基礎やらネットワークプロトコルやらまで知ってないとダメ
・(社内システムなどを作る場合に)UIを作れないとダメ
分業はできないと思わないといけない
何でも相手にしろ、泣き言は誰も聞いてくれない、覚悟を決めろ
6. プログラムを読めること、書けること
「ソフトウェアエンジニアリングの力」=「プログラマブルに物事を解決できる力」それ以外は全て余技といってもいい
Chef等の運用ツール、Fluentdプラグインなどのコード、いろいろあるけど、結局はプログラミングの恩恵をあずかる範囲を広げただけ
自分のやる範囲が広過ぎることは分かっているんだから、プログラミングの力を最大限に使って対抗するしかない、今も昔も
コードを読め、コードを書け、問題はコードで解決しろ
7. なんかキャッチーな結論
このエントリに書くことはほとんど与太話なのであまり真面目に受け取ってはいけない。
特定のツール/ソフトウェア/業界であれこれやっていてTwitterに何となく書いたりしていると、かなり詳しい人からダイレクトに反応があって議論が進み仕事も進んでみんなハッピー、ということがある。自分だけじゃなくて、割と周囲を見てても起きてるなーと思う。
特に、だいたい詳しい人とかはそれぞれお互いのやりとりも見てるので、後日になって「あの人とあの人がこんな話を」というと、みんなけっこう覚えてて、ああお仕事の役に立ってますね、ソーシャルネットワーク万歳! となる。
が、これ、実際あまりよくない。tweetは流れちゃって後から追いにくいし、その時に両方をfollowしてないと会話が追えないし、まとめて集積されないと多少その道に詳しい人でないと全体像をとらえるのが難しかったりする。
これらの問題は特にできたばかりのソフトウェアに顕著だ。
試した人が「こういう問題がある! 使い方にはこういう注意が必要!」という話を上げたとき、Twitterでも(近いところに詳しそうな人がそれなりにいる場合だが)反応はそれなりにやってくる。ああそうだよねー、それには注意しないとねー、で満足する。してしまう。
たいへん良くない!
ところで近年の Fluentd 界には @oranie さんという人がいて、この人が「アルファヒトバシラー」と言われるほどにあれこれ問題を踏み抜いてはblogエントリに詳細を残してくれるので、みんなしてありがたやーありがたやーと思っていた。
#fluentd fluentdでログ集約する際にwarnが多発した件 - iをgに変えるとorangeになることに気づいたoranieの日記
#fluentd GrowthForecastでグラフ描画がエラーになってしまう対処方法 - iをgに変えるとorangeになることに気づいたoranieの日記
#fluentd fluentdでtype copyを使う時書く順番で破壊的な変更が影響を及ぼす件 - iをgに変えるとorangeになることに気づいたoranieの日記
このくらい詳細に経緯と状況と予後を報告している人がいると当然みんな注目する。というか、実際すごく助かる面も(他の人から見て)すごくいろいろあるわけですよ。blogエントリ書いても注目されない、見に来る人がいないとか言う人は oranie さんのマメさとエントリの書き方を見習うべき。
しかし数ヶ月前に @oranie さんの職場状況が変わってしまい、我々としては深い悲しみに沈んでいるわけですよ。かえして! あの日のオ○ニ○さんを返して!
なんの話だっけ。
そうそう、Twitterに放流して満足してないでblogエントリを書こう! というだけの話でした。
http://yapcasia.org/2012/talk/show/96627a88-ab9d-11e1-a255-2a656aeab6a4
僕もしゃべります。
なんというか、一言でいうと
YAPC で僕のトークをみにきてください。本物の分散ストリーム処理とはどういう物かお見せしますよ
というかんじです。
ちょっとした小ネタも用意してあります。
Fluentd 興味ある人も見にきた方がいいです。
このエントリは以下の記事およびFluentdの現状を受けてのものであり、Fluentdの実装についての知識を前提とします。
また言うまでもないことですが、自分の使いかただったらこうだといいなー、という程度のものであり、それ以上の意味もそれ以下の意味もありません。
では順にいってみよう。
実際にはこれだけでは問題は解決しなくて、forward先のFluentdで、forward元のFluentdではどこまで処理をやったメッセージなのか、ということを知る必要がある場合があるので、結局タグの操作もする必要がある。そうするとラベルの操作で解決するケースとタグの操作も行われているケースが混在することになり、状況はむしろ悪くなる。
じゃあどうするかというと悩ましいけど。じつは個人的には現行 v0.10 のタグ操作 + out_route(は勝手に入れた) であんまり困ってなかったりして。
ラベルを導入するとしたら、以下のどちらかになると嬉しい。
include [@error] /etc/fluentd/error.conf include [@sampled] /etc/fluentd/mongodump.conf include [@unknowns] /etc/fluentd/mongodump.conf
<match accesslog.**> ... </match> <match accesslog.** @filtered> ... </match>
相反する提案なのがアレだけど。
includeの拡張は、複数のラベルに対して同じ処理を適用したい場合(全部Mongoに突っ込む、など)に設定ファイルを再利用可能にするためのもの。これにより似たような設定がファイル内に大量にコピペされるのを防ぐことができる、かもしれない。そこまで必要な人がどれだけいるか分からないので、いらないかもしれない。
ただし副作用的に、ラベルつきメッセージの処理を他の設定ファイルに追い出すことで、メインの設定(デフォルトの空ラベルを処理する設定)の見通しをよくする、という効果はあるかも。メイン設定ファイルの末尾に include [@error] hoge ; include [@sampled] pos ; みたいに書いてあれば、どのようなマイナーケースについてはどのファイルを確認すればいいかも明らかになって良い。
match の拡張は設定ファイルの構文をよりシンプルに保つためのもの。 @label: という記法はありていに言ってスジが悪いと思う。ファイルをぱっと見たとき、どこからどこまでがどのラベルについての処理なのかが明らかでない。そのような構文を採用するべきではないと思う。
というわけで、上記ふたつの提案はどちらも設定ファイルをシンプルに保つためのもの。例外ケース(ラベルつきメッセージの処理)の記述量が多くなりそうなケースに対処したければ include 拡張が欲しいし、ラベルつきメッセージ処理をうんざりするほど繰り返して書く必要があまり無さそうなら match 拡張が欲しいんじゃないかと思う。
両方あっても、いちおう衝突はしないかな。ラベルつきincludeされたファイル中にラベルつきmatchが書かれてたらどうするか、の優先順位が決めてあれば、あるいは起動時エラーになれば大丈夫か。
Error#mortal? とか Error#fatal? でいいんじゃないのかな
ほぼ全面的に賛成
結局流量の多い input plugin を担当するFluentd Engineのプロセスがボトルネックになってあんまり状況が改善しないような気もする、けど、よくわからない。input pluginを並列化するところまでいかないとダメなんじゃないかと思う(httpdのpreforkみたいなモデル)。自分のところでは現状、流量の極端に多い input plugin (in_forward) とそこからのメッセージの配送を担当するEngineがボトルネックになっている印象がある。
プラグイン単位で並列化するより、Engine/input plugin のセットを並列化し input plugin へのデータの配送をロードバランスする prefork モデルの方がCPUコアに対してより良くスケールするモデルになると思う。これはSocketManagerの導入を前提にすれば作れるはず。output pluginは集約処理する可能性がある以上こういうことはできないから、これは元の設計案にある通りプロセスをまたいでルーティングするような方法をとるしかなさそう。
ここまで書いたけど、汎用の input plugin に求められることじゃないかー。たとえば in_dstat みたいなプラグインはこのような仕組みにはまったくそぐわない。
ので、データ配送(の受信側)だけプラグインの種類を分けるとか、あるいは何かプラグインごとにフラグを立てさせるとか、そういう形で扱いを分けたほうがいいかもしれない。in_forwardだけ特別扱いってのも考えたけど in_scribe とか in_flume とかもあるか。
まあ、ともあれ、プラグイン単位で別コアにするより、全メッセージを均等に各コアに割り振っていかないとCPUコアを効率よく使うのはたぶん無理だと思う。
またFluentdプロセスが細分化されると、そのそれぞれで(しかも違う増大ペースで)メモリリーク的な挙動を示したときに本当に収拾がつかなくなる。ので、MaxRequestsPerChild的な機構はおそらくほぼ必須になると思う。ないと運用はだいぶ厳しい。
おれイラネ。
……だけじゃなくて。ぱっと考えるに、Fluentdインスタンス内でのエラーについてはメッセージの改変があるから、その前後での正当性保証はたぶん非常に難しいと思う、というか無理なんじゃないかな。
現実的には out_forward から in_forward に渡るときにメッセージの件数もしくはチェックサムをとって伝達の確実性保証をするしかないんじゃないかなと思う。バッファチャンク単位とかで。そうなると forward じゃないプロトコルになりそうなので、専用のプラグインを作るしかないか。(あんまり真面目に考えてない)
プラグイン側で一定時間ごとにリアルタイム集計処理をしていたりする場合などに、現状だと別スレッドを立てるなりCool::Io あたりを自分で使うなりしないといけなくて、実装負荷も実行時負荷もあんまり馬鹿にならない気がする。
一定時間毎や一定の条件(emitされたメッセージ数など?)でプラグイン側の処理をkickしてくれる機構があるといいような気がする。そうすれば非同期処理エンジンはFluent::Engine側にひとつだけあればいい。kick条件とkickされるメソッドはプラグインの initialize (かstart)でEngineに対して登録するような形にする。
こんなもんかな?
最近 fluentd というツールのことがたいへんよく話題に上がっており、かく言う自分もささやかながら使用している身なのだが、それはそれとして比較対象に上がってくるツールに scribed というものがある。これがどういうものなのか、話には聞いていてもよくは知らないという人が多いようなので、これもささやかながら触ってみている自分としてはここらで一度まとめておかねばなるまい、と思った次第である。
日本全国に10人くらいはいるかもしれない scribed のヘビーユーザ各位に捧げる。
なお記憶と経験だけを頼りに書き殴るので、意思決定の重要な局面とかで「これこれこういうブログにたごもりすなる者がこのようなことを書き残しており」などと引用するのはくれぐれも避けていただきたい。
また途中から思いっきりビール飲みながら書いたので文章自体の品質にも問題のある可能性がある。
そも scribed とは何かというと、みんな大好き facebook が2008年だかに公開したオープンソースソフトウェア*1で、主にログの配送と収集・集約を目的とする。同じく facebook が開発した Thrift*2 というRPCプロトコルをベースにしたもので、C++で書かれている。Thrift自体が依存関係にけっこううるさい上に scribed 自体の依存関係もだいぶ激しい状態なのでインストールが極めて面倒くさいのが特徴。詳しいソフトウェアとしての特性は後に譲る。
なお上述リンクのgithubプロジェクトが公開場所なのは間違いないのだが、Commit履歴を見ればわかるとおり、現時点でもう一年半、コードはほぼ放置状態。事実上、開発は停止しているといってもいい。動くのは動くんだけど。*3
このため、既に Boost ライブラリのバージョンについて問題が起きており boost 1.45 以下と組合せてビルドしないと動作しない。なお現時点(2012年2月14日)での boost 最新版は 1.48。詳細については こちら を参照のこと。またThriftが boost 1.36 以降を要求するため、boost はこの間のどれかのものを使う必要がある。これが scribed の面倒な依存関係の一部、ということになる。
その他に Hadoop HDFS への書き込みを使う場合についても、Hadoop側は新しくなり続けているが scribed 側がついていかない、ということになると今後どこかで動作しなくなることが考えられるので注意したい。
なお scribed について知っている限りでは、ドキュメントの類は 公式の設定ファイルの解説 くらいしかない。日本語で書かれた記事となると本当にさわりの使ってみた系のものしか読んだ覚えがないので壊滅的といっていいと思う。
scribed はログを配送するためのデーモンである。ここでログといったが、実際にはこれは「カテゴリ」「メッセージ」というふたつの文字列からなる。カテゴリはscribedによる配送中にその経路や最終的な書き込み先を決定するためのラベルで、メッセージは通常であればログの行そのものとなる。もちろんscribeプロトコルに乗せてログ以外のものを流すことも、やろうと思えばできる。
scribeプロトコルおよび scribed そのものの作りとしては同期的な処理をするものになっている。クライアントからメッセージの送信がある scribed に対して行われた場合、受け取った側の scribed は設定ファイルの内容に従って手元のディスクへの書き込みなり他の scribed へのネットワーク転送なりを行うが、これらが完了した時点ではじめてクライアントに対して OK というレスポンスを返す。通信は全て TCP で行われ、またネットワーク送信時のエラー制御・再送処理などもしっかり作られているため、これらのメッセージ伝達の確実性においては実はかなり秀でている。*4
ログの受信については完全にネットワーク経由のみである。port 1463*5を listen し、そこに送られてきたメッセージはどこからのものであってもすべて平等に(混ぜて)扱う。
libeventを用いた非同期I/Oおよび設定で指定可能なマルチスレッドにより、多数のコネクションを扱うことができる。手元では150程度は1プロセスで全く問題なく捌けることを確認している。
特定の scribed にネットワーク経由で送信できる。きわめて安定している上に速くてまったく問題なし。
ローカルディスク、もしくはHDFSに書き込む機能がある。HDFSに書き込む場合はビルド時に libhdfs を有効にする必要がある。
書き込み先は設定で指定したパス(例: /path/to)の下にカテゴリ名でディレクトリを作り、その下にファイルを作成する。 /path/to/category/file_access_log_00000 のような感じ。00000の部分は連番でファイルのローテート毎に数字が増えていく。
ファイルのローテーションをサポートしており、設定した1ファイルのサイズ上限(デフォルト1GB)を超えるか、もしくは設定した期間を過ぎるか(時間毎、もしくは日毎が指定可能)で次のファイルに書き込みが切り替わる。また最新のファイルに対してシンボリックリンクを張る機能もついていてたいへん便利。
これも極めて安定しつつ高速に動作する機能で、特にファイルのローテーション回りは実運用を考えて作られている、という印象がある。複数のサーバから送られたログが単一のディスクに整然とローテートされて格納されるのは後から調査する側からすると極めて扱いやすい。
scribed はバッファリングの機能を持っている。これは性能特性を向上させるためのものではなく、ある特定のログの配送先(例えばネットワーク送信対象の別ホストの scribed)に対する書き込みが連続的に失敗する場合、一時的にそのログを確保しておき、正規の配送先が復活したときに再送するための一時的な置き場のことだ。詳しくは このあたり を読んでいただきたい。
これは実際にうまく動作する。何度も試したから間違いない。再送パフォーマンスもよく、設定を間違えさえしなければ新しくやってくるログの受け入れに負担をかけないまま障害中の再送を完了させてまたストリーム処理モードに戻ることができる。配送経路の途中のノードを割と気軽に停止できるのは運用の手間が格段に向上するので、なくてはならない機能と言っていいと思う。
またメッセージを複数の宛先に分散する機能もある。multi と bucket だ。multi はメッセージを複数の宛先にコピーして渡す。bucket は設定した複数の宛先にどれかひとつに渡す。
multi を使うことで「ローカルディスクに書き込みつつ次の scribed にネットワーク送信する」ということが可能になる。自分の手元ではこれを用いて複数のサーバのローカルディスクにログを書き込ませることでバックアップの代わりとしている。
bucket はメッセージの先頭にユーザ名などが書かれていた場合に、それをキーに使って配送先を選ぶことができる。したがってログ全体を複数の宛先に分割しつつ、特定のユーザのログは特定の宛先にのみ行くようにする、といった機能だ。またランダムで宛先を選ぶこともできるため、単純なロードバランスにも使うことができる。(……と思っていたんだけどなあ。)
scribed は C++ で書かれており、また前述のように libevent をベースにした非同期I/Oライブラリを使用している。これらの要素と、おそらく真剣にパフォーマンスを考えてコードが書かれているのだろう、性能が良い。きわめて良い。
最終的にピーク時で150Mbpsを超える量のログデータを1プロセスで扱っているが、これでも性能的にはまったく問題ない。4Core HTのCPUの論理CPUひとつ分(12.5%)を使いきるところに全く届いていない。その半分も使ってない感じ。まだまだ行けるだろう。先に disk I/O wait の方がきびしくなると思う。*6
さて機能も豊富、性能特性もきわめて良い、というscribedだが、問題はある。いっぱいある。順にいこう。
なおインストールが面倒くさいという問題もあるにはあるが、そこはまあアレですよ、要は慣れというか。自分はもう15分あれば1台いけるね。きっとみんなも10回くらいやればそうなるよ。あと他の言語はともかく、日本語に限って言えば自分がだいぶ細かくblogエントリ書いたので、それを読めばなんとかなるんじゃないかなー。そうだといいなー。
で、問題。
たとえば先述の bucket だが、ランダムで送り先を指定できる、とあるのでこれがロードバランサに使えるかと思ったら、もう全然使えない。ひと桁メッセージでの各ノードへの配送が100回くらい連続したかと思ったら、その次に突然40000メッセージくらいがどかっと特定ノードに送られたりする。あのな。それじゃロードバランスになってねえだろ。おまえランダムって言葉の意味知ってんのか。いいかげんにしろ。
そのほか、各出力設定の細かいオプションなど、これ本当に動くのか? と言いたいところが随所にある。設定例の最後に書いてある Thriftfile 出力とか用途もわからないし試す気にもなれない。
先述した通り公式の設定例解説みたいなものが唯一あるだけで、それも実際にはどう書いていいものかよくわからない説明だったりすることもある。ソースコードツリーの中の example を頼りになんとか推定し、それでもわからないものは実際に書いて動かし試してみるしかない。ひどく不便。
またコードが C++ なので、いや C++ を言い訳にするのは俺の C++ が劣っているせいなので良くないのだが、ともかくどう設定すればいいのかコードを読んでもぱっとは理解できないのがつらい。自分の感覚だと試行錯誤して確認した方がまだ早くて確実だった。
その上、唯一の頼りの設定例にすら 「(may not be in open-source yet)」と書かれた設定項目があったりして憤死しそうになる。あのな。いいかげんにしろ。俺はその設定項目が欲しいんだよ。外に出して釣りしてんじゃねえ。*7
プラグインとかまったく考えていない構造になっており、自分でちょっとコード書いて動作追加して動かしたいなー、と思ってもそのようなことをする余地は一切ない。いや scribed そのものにがっつり手を入れる気になればやれないことはないんだろうが、そもそもコード全体が fb303 という facebook 内部のフレームワークべったりなためかなりクセがある。fb303べったりになる覚悟があるならやりきれるかもしれないが、自分には無理である。
scribed はあくまでデーモンとして動作する単独のソフトウェアであり、普通なら他に必要となるような周辺ツールがほとんど無い。たとえばデーモンの起動・停止・再起動・状態報告をするための init script であったり、たとえばネットワーク経由で scribed にログを送りつけるためのエージェントツールであったり。
ログを送信するためのツールとしては scribe_cat というものが申し訳程度にコードに含まれているが、これは stdin からの入力をすべて読み込み、全部一括してサーバに送る、というたいへん難儀なつくりになっており、マトモに運用に耐えられるようなものではない。
これらについて、自分は エージェント も init script も自作することでどうにか運用まで持っていったが、このような時間/手間を払える場所ばかりでもないだろうことを考えると、世間一般で使われるには難易度が高過ぎるだろう、という気がする。
根拠もほとんどない妄想みたいな想像だけど、facebookはきっとWebサーバも自分たちで書いてて、そこから直接 Thrift のコード経由でログを scribed に送り付けてるんだろうなあ。あんだけ大きけりゃそれはアリかもしれないけど、俺達にはそれはちょっと無理だし。
さっきも書いたが、開発が止まって久しい。boostもHadoopも着々とバージョンが上がっている中で scribed のコードがいつまでも対応しないと、いずれはこれらのバージョンアップに置いていかれる可能性がある。
boostは最悪いちどビルドしてしまえばそのまま置いておけるが、Hadoopについてはクラスタのバージョンを一切上げないことなどできるわけもないので、HDFS書き込みを有効にして使用している場合は特にこの制約がいつか現実的なものになるだろうと思う。
性能的には極めて優れている。いっぽうであれこれやったりするには拡張性・メンテナンス性のうえで難がある。
以下のようなケースでは scribed を使うのは現実的で、コストパフォーマンスに優れているだろう。
以下のようなケースにはあまり向いていないと思う。
特にscribedがいかんと言うわけでない。自分が面倒を見ているシステムではついに稼動中の scribed 台数は2台にまで減ってしまったが、その2台、つまりアーカイブ目的での最終的な集約とディスクへの書き込みについては scribed を使い続けるだろうと思われる。
何事も目的とメリット・デメリットである。
Flume? 誰かがそのうちこの記事と似たようなのを書いてくれるんじゃないかなー。期待したいなー。