Continuous Deliveryの時代とJava

かつてバージョン管理やCIなどが存在しなかった頃
人々は手作業で(FTPなどを使い)PHPファイルをアップロードすることで、本番環境へデプロイを行っていた

筆者はJavaプログラマだったので
「本番環境でバグが見つかっても、問題のファイルを修正してアップロードすることで、一瞬で修正できる」
というPHPの利点が非常にうらやましかった
Javaだとjarに固めてアプリケーションサーバにFTPで上げて再起動する、みたいな感じで非常に面倒くさかったのだ

しかし時代は経過し、「ユニットテスト等が行われた後でしか本番環境のファイルが変更されるべきではない」という考え方が常識となってきた
こうなると、「手軽にちょちょいと問題を修正する」というPHPの良さは失われることになる

ウェブアプリケーションを構成するファイル群が全体としてバージョン管理され、テストをパスしたファイル群のみが本番環境に到達できるのだ
まぁそれが正解なのだが、ぶっちゃけ、いざというときの俊敏さは失われることになる
PHPを手作業でアップロードしていた時代、ひどい場合には電話で「なんか問題でてるんだけど」という報告を受けてから数十秒で修正が完了していたケースもあるだろう。

そんなわけで、相対的にJavaでもいいかな、という感じになってきた。

Log4j 2にも採用されたLMAX Disruptorはなぜ狂ったように速いのか?

dis

LMAXという会社はおそらくFX業者で、筆者はLMAXの開発者の講演を、InfoQの動画で何度か見たことがあった。
彼らは非常に特異な集団で、さしずめ「Javaのスピード狂」という感じだ。
印象的なのは、シングルスレッドで仕事を片付けることを強調している点だ。
「Javaならマルチスレッドで並列処理すれば性能が出ると広く思われているが、我々の仕事においてはシングルスレッドが最速だ」というような主張を何度も見た。
ゴールドマンサックスといいLMAXといい、やはり多額の金が動く会社でガチでJavaをやっている連中はカリカリにチューニングするため、技術的には非常に面白い。
彼らがコアのライブラリをOSS化してくれるというのは、金融業界を否定的な目で見る筆者からすると複雑だが、悔しいことに参考になる。

LMAX DisruptorはJavaのライブラリだ。Producer/Consumerパターンであるスレッドから別のスレッドへメッセージを渡す際のパフォーマンスを追求したものである。Log4j2のAsync実装に採用されているようだ。

Disruptorの速さの秘密はいくつかのブログで詳細に解説されているので、じっくり知りたい人はソースコードを読むとともにそちらを参照するとよいだろう。

  • ロックを極力排除し、Producerからのアクセスの一ヶ所でしか使用していない
  • データ構造としてリングバッファを使い、メッセージが格納されている領域のインデックスを各Consumer等がシーケンス番号として管理することで、ロックを減らしている

何とも、非常に頭が良い解決方法である。

シーケンス番号はどんどん上がっていくので、配列を用意すると無限の大きさの配列が必要となってしまう。しかしリングバッファをぐるぐる廻して、全Consumerが処理済みの領域を上書きして使っていくことでこの問題を解決している。

どんなConsumerが存在するのか?が中央で管理されているのが面白い。オブジェクト指向的にはイケていないが、ロックを排除するための工夫ということだろう。

2015/01/06追記
最後の「オブジェクト指向的にはイケていないが…」の部分に対して「別にイケていなくてもいいのでは」的な反応を頂いたので追記しておく。

まさしくその通りで、Disruptorはパフォーマンスを極めるためのものなので、「オブジェクト指向的にイケて」いなくてもOKである。
しかし実は筆者ではなく、LMAXの中の人が、この点について(かなり強く)言及しており、ここは大きなポイントだ。
オブジェクト指向的に綺麗に設計する(関心を分離する)ことにこだわることがパフォーマンス劣化(正確にはcontention)につながっている
と、http://lmax-exchange.github.io/disruptor/files/Disruptor-1.0.pdfに記述されている。

Further investigation and a focus on the computer science made us realise that the conflation of concerns inherent in
conventional approaches, (e.g. queues and processing nodes) leads to contention in multi-threaded implementations,
suggesting that there may be a better approach.

同様にこちらのブログの以下の部分も同様。

The ConsumerTrackingProducerBarrier has a list of all the Consumers that are accessing the ring buffer. Now to me this seemed a bit odd – I wouldn’t expect the ProducerBarrier to know anything about the consuming side. But wait, there is a reason. Because we don’t want the “conflation of concerns” a queue has (it has to track the head and tail which are sometimes the same point), our consumers are responsible for knowing which sequence number they’re up to, not the ring buffer. So, if we want to make sure we don’t wrap the buffer, we need to check where the consumers have got to.

筆者の書き方が悪かったのだが、つまり「オブジェクト指向的にきれいな設計を捨てることで性能向上を実現している」という、トレードオフの部分が非常に面白いのだ。(そのため「オブジェクト指向的にはイケていない」と書いたことは文脈的におかしくないし、むしろもっと強調してもよい部分だと考えている。)

ちなみに上記の指摘をされた方からは「アルゴリズム的な話に、オブジェクト指向をもってくるのがナンセンスなんです。」とも言われたのだが、上記PDFの図を見てもらえばわかるようにDisruptorは典型的なオブジェクト指向で書かれたソフトウェアであるので、Disruptorについて言及している本エントリにおいて「オブジェクト指向をもってくるのがナンセンス」と言われても困る。おそらく指摘された方はDisruptorのドキュメントやソース等は読まずに、本エントリだけに脊髄反射されただけだろうと思う。

Disruptorはいわば「守破離」的に、全体としてはオブジェクト指向に従いながら、オブジェクト指向で当たり前とされる「関心の分離」の一部だけをわざと崩すことで最高のパフォーマンスを実現しているところが素晴らしい。

WekaのIBkはデフォルトで正規化してくれる

フリーソフトではじめる機械学習入門という本があります。

フリーソフトではじめる機械学習入門
荒木 雅弘
森北出版
売り上げランキング: 18,141

筆者的にはかなりツボな本で、既に2度ほど読み通しています。
今度は実際に手を動かしながら(Wekaを使ったりJavaのコードを書きながら)もう一度読んでみようかと思っています。

18ページあたりから、KnowledgeFlow Environment(KFE)を使ってWekaに格納されているirisのデータをK近傍法で分類する例が始まります。
28ページ真ん中あたりで正規化(1~10程度の値を取る属性と1~1000程度の属性などが混在する場合に、データ間の距離の計算において、後者の属性の影響が強くならないようにすること)が紹介され、下図のようにNormalize(左から3つめ)のが追加されます。

weka-nfe

筆者はまず、本書の通りにKFEを使ってみました。

次に、KFEのようなGUIのツールではなく、JavaのコードからIBkクラスを使ったirisデータの分類をしたかったので、Googleで調べながら独学で進めてみました。これは、特に難しいところはありませんでしたが、その時点で手元で書いてみたコードでは、データの正規化を行っていませんでした。そのためこのときの結果は不正確だろうと考えました。

影響を確認するために、わざと極端にレンジの広い属性と狭い属性を持つデータを用意して分類させてみたのですが、特におかしな結果になりません。仕方ないのでNormalizeクラスの使い方を学んできちんと正規化してみましたが、結果はNormalizeクラスを使っても使わなくても同じで、正常な結果となりました。

原因を調べてみると、どうやらIBkを利用したK近傍法による分類では、デフォルトで正規化を行ってくれるようです。デフォルトではインスタンス間の距離を計算するのにweka.core.EuclideanDistanceクラスが使われますが、こちらのクラスが計算の際に正規化をしてくれます。-Dオプションを渡すと、正規化を行わないという動作になるようです。

そのため、本書の28ページから29ページにかけて行われているNormalizeの追加は、省略しても同じ結果になります。

分類されていない大量データは教師あり学習に使えるのか

手元に大量のデータがあって、それを教師あり学習に使って、最終的には分類器を作りたい。
「教師あり学習」なので、まずはそれぞれのデータについて、人間がラベルをつける必要がある。
しかし、データは大量なので、人間がいちいちひとつずつラベルをつけることができない。

詰んだ…

ってなる気がするのだが、これはどう解決するべきなのだろうか。
やっぱり、大量のデータがラベルなしで存在している場合は、教師なし学習が基本なのか?

Amazon EMRでHive/Impala「思考のスピード」とSQL

Amazon AWSのEMRでHiveとImpalaに触ってみた。

通常なら自分でHadoopクラスタを組んでからHiveやImpalaをインストールして…とやる必要があるのに、EMRだとコンソールからポチポチするだけで、しかもSpot Instanceで超安価にクラスタができあがる。

SSHでログインしてhiveやimpala-shellとコマンドを叩くだけ。AWSのサービスはいくつかあるけど、個人的にEMRは一番凄いと思う。

テスト的に某SaaS型WAFサービスのアクセスログを22億行ほどロードして、impalaでいくつかSQLでクエリを投げてみた。m1.mediumという遅いインスタンス20台のクラスタで、単純な問い合わせが4分くらい。

Hiveに比べれば圧倒的に高速なんだけど、俗に最近のSQL on Hadoopで言われる「思考のスピードで解析する」という感じはしない。4分も経ったら脳みその一時記憶領域は完全にクリアされてしまう。

で、ぶっちゃけ思ったんだけど、数億件以上のアクセスログ的なデータに対して解析目的でSQLを投げる場合って、実際にはサンプリングして1/100~1/1000くらいにサイズダウンさせたデータに対して同じクエリ投げれば、十分に目的を達成できる場合が殆どなんじゃないだろうか。

本当にそのまま数億件以上のデータを扱う必要があるケースというのは、異常検知や、あるいは非常にロングテール的にデータの特徴が多岐に渡るケースになるんだと思う。多くの場合はそうでない気がする。

継続的WEBセキュリティ診断サービス VAddy

ウェブアプリケーションの開発において、CI(Continuous Integration)のサイクルに、クロスサイトスクリプティングやSQLインジェクション等を早期に発見するための脆弱性診断も組み込まれるべきでは?というコンセプトで、新しいウェブサービスを開発しました。現在、アルファ版の詰めに入っている段階です。アルファ版を使ってくれるユーザさん(もちろんアルファ版の段階では無料)を探しています。

VAddy
http://vaddy.net/

Jenkins等のCIサーバ上でcommit毎にビルドやユニットテストが実行されるという文化は定着しつつあり、誰もがその良さを実感していると思います。

同じように、セキュリティ面でのテストもCIサーバが自動的に実行してくれれば便利ではないでしょうか。

VAddyはSaaS型のウェブアプリケーション脆弱性診断サービスです。基本的な使い方として、ユーザはまずウェブブラウザのプロキシ設定をVAddyのプロキシサーバのものに設定し、テスト環境にデプロイされた、ウェブアプリケーションの正常なフローを実施します。このときVAddyサーバ側に、これらのクロール時のデータが保存されます。(このクロールの作業は基本的に初回のみ必要となりますが、アプリケーションが改変された場合にはクロールをやり直す必要があります。SeleniumIDE等を利用されている場合には、単にFirefoxのプロキシの設定を変えてSeleniumIDEを一度実行するだけです)

次に、ユーザはJenkins等のCIサーバからAPIを呼び出すか、あるいはVAddyのウェブサイトから手動で、スキャンの実行を指示します。するとVAddyのサーバ上で稼働する脆弱性診断アプリケーションが、ユーザのテスト環境にデプロイされているウェブアプリケーションに対して、クロスサイトスクリプティングやSQLインジェクション等の脆弱性が無いかどうかを調べます。

VAddyはCIサイクルに組み込まれ、自動的に、毎日(場合によっては一日に何度でも)繰り返し実行されることを念頭に開発されています。
課金は月額定額の予定(無料プランも用意する予定)で、実行回数に制限は設けない予定です。

NSAがわざわざ直接CISCOのルータをいじる理由

NSAやCIAが本気なのであれば、CISCOを説得して、出荷前の工場の段階でトロイを入れればいいのでは?
という疑問がある。

ではなぜスノーデンがリークしたように、NSAはわざわざ自分たちの手でCISCOのルータをいじっていたのだろうか。
以下のような理由があるかもしれない。

  • もし「NSAのスパイ活動に協力している」という噂が立ってしまった場合、米国外でも大きな売上を持つ企業であるCISCOにとっては大きなダメージとなるだろう。そのため、CISCOがすんなり協力してくれるとは限らない。
  • 盗聴したいのはCISCOのルータだけでなく他のメーカー製のルータやサーバも含むので、CISCOだけ(NSAが時間的労力を割いて)協力を取り付けてもそれほど意味が無い
  • 仮にCISCOの全ルータにトロイを埋め込むことに成功した場合、全世界に出荷されたトロイからNSAのサーバにパケットがたくさん飛んでくるが、そのうちのほとんどはノイズであり、どれが監視対象(ターゲット)のものなのか区別するのが面倒くさい
  • また、上記の場合には、ネットワークトラフィックの異常などに気がついた、腕の良い、いわゆるホワイトハッカーに見つかってしまい、NSAとCISCOが何かをやっていることがパブリックにされる可能性がある
  • 元々、麻薬取引やテロリストの監視等の理由で、NSAやCIAは米国内外の空港や港などの場所においての活動を得意としている。そのため出荷されていく荷物(ルータ等)をインターセプトするのは得意中の得意。
  • 出荷される途中のルータは宛先がはっきりしている(例えば北京とか?)ので、先述した「ノイズ」という観点で考えた場合のS/N比がそこそこマシな値になることが期待できる
  • トロイがハードウェアの場合、工場で出荷されるルータ全部に付けるほどの量を用意するのが大変

KinesisかS3か

Kinesisを触ってはみたものの、特別「これは使いやすい!」という印象ではなかった。

Kinesisの用途は要するに

  • AWSにガンガンデータを送る
  • AWS側では、送られてきたデータを片っ端から処理する
  • AWS側に着いたデータは処理しようがしまいが一定期間(現在は24時間)で消える

というもの。

しかし、AWSへの送信は単純にHTTPSのPOSTリクエストであり、さらにBase64エンコードされているということで特に効率的な方法ではない。むしろ効率をまったく考慮しておらず、シンプルさ、扱いやすさに徹している。

HTTPSのPOSTリクエストであるにも関わらずサイズ上限(現在は50KB)がきついので、小さなデータを送る用途に限定される。

個人的に数十~百台程度のサーバからAWSへのログの集約(Log Aggregation)を計画しているのだが、Kinesisの用途にぴったりか?と考えた一方で、S3でもいけるのでは?と思った。

S3も

  • AWSにデータを送るために使える
  • HTTPSのPOSTリクエストでデータを送ることができる

という意味ではKinesisとまったく同じ。各オブジェクトのサイズの上限はKinesisよりずっと緩い(具体的にいくつなのかは調べてませんスミマセン)。

そのため、ログ集約にS3を使うというのも十分に悪くない考えのように思える。

S3では

  • 秒間100以上のPUT要求がコンスタントに発生する場合

を「high request rate」と定義しているようである。(ドキュメントへのリンク)

そのため、Kinesisとは1桁以上、対象としているトラフィックの性質が異なるようだ。
ただしS3へは例えば「ログを100行まとめて1つのオブジェクトに入れる」みたいに少し工夫するだけでこの問題は解決する。

KinesisにするべきかS3にするべきか。

シンプルさ

S3の場合、各サーバがそれぞれS3にアップロードリクエストを送るだけでAWSへの集約そのものは終わるというシンプルさが非常に大きな利点になる。

Kinesisでは各サーバがAWSへデータを送る一方で、AWS側には別途Kinesisアプリケーションを配置しなくてはならない。

データの消去

Kinesisが自動的に古いデータを消すという点については、ケースによってメリットになったりデメリットになったりするだろう。

データの順番

Kinesisで送られてきたデータはストリームとして扱われるので、あるデータを処理したら「次のデータ」を取得する、というアクションが可能である。
S3では各オブジェクトはバラバラなので、「次のデータ」のような概念が必要な場合には、ユーザが自分でその仕組みを考えなくてはならない。
数多くのセンサーのようなものからAWSに対してデータを送信し、時系列で処理していきたい場合などには、Kinesisの方が向いていることになる。
S3で時系列に処理していきたい場合には、S3へのアップロードのログが取得できるようになった時点で、ログを元に処理していくような方法が考えられるだろうか。

モニタリング

KinesisはAWSコンソールでほぼリアルタイムに使用状況を把握できるが、S3はできない。この点は明らかにKinesisの勝ち。

ざっくりとした結論

数百~千/秒以上の、それぞれのサイズはそれほど大きくないデータを扱いたい場合にはKinesis、~数百/秒でそれぞれのデータのサイズはそれなりに大きくなる(50KB以上)場合にはS3だろうか。

(余談: FluentdやFlumeは当然向いているのだと思うが、今回はとにかくシンプルにAWSだけで片付けることを考えている)

Elastic BeanstalkでJenkinsを動かしたけどすぐやめた感想

Elastic Beanstalk(Tomcatのやつ)はAuto Scalingするようなウェブアプリケーションに向いている。つまりサーバのインスタンス自体は状態を持たないことを前提とする設計が向いている。

今回、Elastic Beanstalkそのものを触ってみたかったので、手っ取り早く手元にあったjenkins.warをアップロードしてみた。

Jenkinsはプラグインのインストールやジョブの設定、ジョブの実行ログなど、基本的にローカルファイルシステムにデータをじゃんじゃん蓄積するものなので、そういう意味で「状態もちまくり」であり、真のMutableアプリケーションという感じ。

Elastic Beanstalkは設定変更を行ったりするとすぐにイメージをまっさらな状態で作り直すので、その際にJenkinsが貯めていたデータはすべて破棄される。なので基本的に向いてない。もちろん工夫を重ねればElastic Beanstalk上で運用することもできるだろうけど、メリットが小さいなという印象。

Elastic Beanstalkそのものの完成度やコンセプトは素晴らしい。EC2やVPCその他、AWSの知識がある人には、「なるほどこう組み立ててくれるのね」という感じで便利。逆にあまり詳しくない人は「どこまでがElastic Beanstalkなんだ!?」となりそう。

Amazon Kinesisに触ってみた感想

公式ドキュメントにあるサンプルソースコードを参照に、データのput/getを試してみた。
(IRecordProcessorは軽く見ただけ)
以下、感じたことを箇条書き。

 

富豪的ネットワーク転送

(個人的に富豪的なのは嫌いじゃないんだけど…)
データは基本的にbyte[]を送るんだけど、Kinesisの最も低レベルなAPIではHTTPのPOSTリクエスト、かつボディ部がJSONという形式で送られる。

{"StreamName":"teststream1","Data":"dGVzdERhdGEtMA==","PartitionKey":"partitionKey-0"}

生のbyte[]をどう扱うのかと思ってたらBase64だった(;´Д`)サイズ膨らみますね

HTTPのPOSTで送るだけでもオーバーヘッドがあるのにBase64っすか。。。って感じで意外でした。
大規模なLog Aggregationのシステムを組む場合、例えば1行ごとにKinesisに投げるように作ると、相当無駄なトラフィックが発生する感じ。
通信もHTTPSだしリクエスト毎に署名作業が発生するし、CPUにもそれほどやさしくなさそう。同じCPU使うなら圧縮すればいいのにとか思わないでもない。(もしかしたら転送時にgzip圧縮してたりするのかも。未確認)

 

 

期待していたよりやや複雑

ShardIteratorとかシーケンス番号とか、そこそこ複雑だった。もうチョイ楽にならなかったか?という印象。
IRecordProcessorを使うサンプルも見たけどそんなに楽になっていないような…
まぁ、APIが8種類くらいしかないのは、とりあえずシンプルでよかったです。

 

 

データ形式が潔すぎる

上でも書いた通り生byte[]なので非常にわかりやすいけど、JSONとかのサポートもあっても良かったんじゃないかと。
(もちろん自分でser/deすりゃいい話ですが)

これだけ(;´Д`)
ちょうどLog Aggregationを考えているところだったのでKinesisには非常に期待しています。とりいそぎ東京に早くきてほすぃ。