読者です 読者をやめる 読者になる 読者になる

ku-sukeのブログ

Just another hatena blog

Amazon MapReduceを使ってPHPで画像処理してみる

 今日はとある私的なプロジェクトで画像の情報操作が必要になったので、ローカルで計算してもいいんだけど今後もパラメータチューニングで何度か発生しそうなので、AmazonのMapReduceを使ってみました。

今回の構成図はこんなかんじ。全部Amazonで全部PHPでやってみた。

やろうとおもったきっかけ

 別に5万枚くらいじゃ実はAmazonじゃなくてもよかったりします。一晩ペチぺーのスクリプトを4〜5本平行で走らせておけば、ローカルのMySQLに結果をためるくらい可能です。でも、

  • 今後のために勉強したかった。MapReduceで調べても、Apacheのログとかテキスト操作くらいしか見つからなかったので。
  • データをオンラインに置きたかった・ローカルに置きたくなかった。
  • そうすると、MapReduceするにせよしないにせよS3がよさそうだった。
  • じゃあ、EC2一晩動かすより、MapReduceで一気にやった方が安く上がるんじゃない?とも思った。

まず処理対象の画像ファイルをS3に配備する。

 Amazon MapReduceから一斉にアクセスするには、通常のレン鯖だとご迷惑をおかけするかもしれないので、S3にしました。というか最初っからS3に置くつもりでした。異なるURL体系のデータ5万ファイルをちまちまS3にアップします。最初はGUIでやろうと思ったけど、S3Foxだと1ディレクトリ内のファイルが巨大になると固まって使い物にならず、s3sync.rbを使ってみたらメタデータが書き込まれずブラウザアクセスで使い物にならず、おとなしくPHPでバッチをかきました。

で、5万ファイルを5スレッドくらいでちまちまアップしました。フォルダ数も含めると全部で20万強のノード数なんですが、S3はフォルダという概念がないので、PUTはファイルの数だけですみました。またそのアップロードの過程で、URL一覧をログに出力しておきます(あとで使う)。

MapReduceに必要なファイルをアップする

 次に、MapReduceに必要なmapperとreducerをアップします。これはS3Foxを使いました。アクセス権(ACL)は自分のみでOKです。また、先ほどのアップロード処理で生成したURL一覧

https://s3.amazonaws.com/(略)/0001.png
https://s3.amazonaws.com/(略)/0002.png
https://s3.amazonaws.com/(略)/0003.png
https://s3.amazonaws.com/(略)/0004.png

みたいな5万行くらいのテキストファイルも/bucket_name/input/filelist.txtに配備します。

MapperとReducerの処理内容

Reducerは、よくあるテキストカウントとおなじで、Mapからの計算結果を出力して、分布をみるのに使おうと思ったので、こちらのサンプルコードをそのまま使わせていただきました。

Mapperなんですが、コピペすると長くなりますので概要をかきますと

  1. ログ出力toSimpleDB
  2. while(標準出力が存在する間)
    1. 巨大なテキストファイルの1行を取得=URLをゲット
    2. file_get_contentsして、imagecreatefromstringする。
    3. 画像処理を行う(GDとか普通に使える)
    4. 画像の処理結果を、SimpleDBにinsert(Put Attribute)する。
    5. Reducerに渡すタブ区切りのkey valueをechoで出力する

という感じです。PHPを1ファイルでつくらないといけないので、各ライブラリからメソッドを寄せ集めて再構成してつくりました。

SimpleDBの活用

 こちらも勉強用途の色が強いのですが、最近はやりのkey/valueストアを使ってみたかった。GAE/Jは触りましたが、「負荷が上がってもリニアにスケールする」PHPから使えるKVSでメジャーで簡単に試せるもの。。。となるとSimpleDBが一番よさそうだったので、自分でサンプルコードのWebUIラッパーをかきつつ、今回のMapperにも組み込んでみました。
 SimpleDBの概要はこのへんがわかりやすいです。

んで、自前でつくったWebUIラッパーがこんなかんじ。自分用なのでユルい作りです。まだRDBMSにおけるUPDATEとページ送りが未実装。

テーブル(ドメイン)のメタデータもこんな感じで日本語表示

で、結果は失敗しました。

 なんか、よくわからないんですが、おそらくreducer.phpのメモリ不足か何かで2.8万ファイルくらいで止まってしまいました。それ以外にも、6400ファイルくらいで急に進捗が止まったりしたので、何か問題が起きているようでした。ログが見にくいのが難点ですね。このへんはHadoopの知識が必要なようです。timestampというvalueが複数putされている例もあったので、もうちょっと詳細にログをとりながらやってみようと思います。
 ログはファイルに出力できないので、SimpleDBにputするというやり方は悪くないなと思いました。auto_incrementみたいなのがなかったはずなので、一意キーのつけ方だけが課題です。ログならマイクロ秒とかでもいいんですけどね。

使ったお金は20台を2時間で400円

 MapReduce代が360円に、AmazonS3代が40円位でした。数十時間使うと練習台としては高くつきますが、この値段ならガンガン失敗できますね。ひとまず練習台はインスタンス数を1、処理するファイルも10行とかにしておけば、1回テストするごとに10円しかかかりませんのでおすすめです。
僕は今回のテストで、

  • inputファイルは、ディレクトリを指定しないといけない
  • PHPを使うときはCGIなのでシェバング(#!/usr/bin/php)をかかないとダメ
  • ログはデフォルトではいっさいでない。インスタンス数を入力する画面でオプションで設定しましょう。