楽しく! 美味しく! 美しく! 画像加工サーバ Rudess(仮)
よく訓練されたアップル信者、都元です。本日は画像加工処理のお話。
チューター
クラスメソッド社にはチューターというWebサイトパフォーマンスチューニングのサービスがあります。チューニングのポイントは、一般的に言われていることから、AWSを利用したアプリケーションアーキテクチャまで様々です。
一般的なWebサイトパフォーマンス向上の施策として、画像まわりのチューニングも色々あります。少し考えるだけでも、「CSSスプライト」「サイズ変換」「画像の軽量化」など、多くのテクニックがあります。しかし画像というのは、見栄えは良くしたいがパフォーマンスは落としたくない、というトレードオフに挟まれることが多く、扱いが難しい *1ことが多々あります。
よくあるパフォーマンスチューニングポイントとしては、「画像をHTML/CSSでリサイズしてしまっている」というものです。具体的には、例えばページ上に100x100で表示したい画像があったとします。しかし、そのimgタグに対してサーバから送られてくる画像は300x300であり、ブラウザが100x100の大きさに変換してから表示している、という状況です。この場合、サーバは100x100の画像を送るべきで、そうであればブラウザにリサイズ処理の負担を掛けることもなくなりますし、何よりファイルサイズが小さくなって転送時間も短くなることでしょう。
しかし。1つの元画像から様々なサイズの派生画像を作るのって、技術的にはそんなに難しい話ではありませんが、結構骨の折れる作業かと思います。その画像が、サイト側が準備した画像であればまだ良いのですが、ユーザがアップロードした画像だったりすると、動的に数が増えていくことになります。さらに、ユーザはどんなサイズの画像をアップロードしてくるか分からないのです。
さて、ここで突然ですが、COOKPADさんののスライド「料理を楽しくする画像配信システム」をご覧ください。
mod_tofu…、いいですね。しかしこのシステムはCOOKPADさんの内部システムのようで、一般に公開されているものではないようです。というわけで、このシステムをパクにインスパイアされて、私も画像変換を行うサーバを書いてみました。
といっても私、Apache moduleなんて書いた事もないので、一番手慣れたJavaのWebアプリとして実装しました。
名前はまだない
プロダクトの正式名はまだありません。表向きの名前決めは然るべき人にお任せしますが、とりあえず開発者としてこのシステムをRudess(ルーデス)と呼んでます。意味はありません。いわゆるコードネームです。ジョーダン・ルーデスかっこいいですね、ってダケですw
プロダクトにコードネーム(マーケティング的な理由等で変わらない内部的な名前)は必須だよなー。
— 都元ダイスケさん (@daisuke_m) 2013年4月10日
あーあ、表に出さない名前なのに、出しちゃったさ。呼び名が無いと困るじゃないですか。
Rudessのアーキテクチャ
COOKPADさんはCloudFrontとAkamai、ImageMagickとImlib2等、かなり細かい検討の上、様々な工夫をされています。mod_tofuの作者である成田さんのブログを拝見すると、苦労の跡が見て取れます。
Rudessでは、まずはキャッシュの役割を果たしてもらうために、フロントにはCloudFrontを配置しておきます。CloudFrontはカスタムオリジンで設定しておき、参照先はELBです。ELBの配下にはEC2サーバが控えており、このサーバが画像変換を担います。これらのサーバはIAM Roleを割り当てられており、DynamoDBや、元画像バケットへのアクセスを許されています。
画像変換のエンジンにはimgscalrというJavaのライブラリを使っていますが、今後、ImageMagickはもちろん、OpenCVと連携した高度な画像処理なんかも試してみたいなー、と思っています。
また、このRudessは、変換サーバのCPU負荷が高くなると、AutoScalingによりスケールアウトするようになっています。デフォルトで最大10台までいきます。
以上のこのシステム環境は、CloudFormationテンプレートで一発作成が可能です。何が凄いって、ココですよ。CloudFormationカコイイ…。
あなたの環境にもRudessを
で、これを皆様にご提供。mod_tofuができることはFREE版として無料公開しちゃいます *2。既に、CloudFormationのテンプレートと、そのテンプレで内部的に必要なAMIはpublicな状態で置いてあります。つまり、AWSアカウントさえ持っていれば、約30分 *3であなたの環境にRudessが立ち上がります。構築して試してすぐ片付ければ、たいしてお金も掛からないので、是非試してみてください。
雑多な前準備
まずは、リージョン決めてください。まぁ日本在住ならap-northeast-1で決まりかと思いますが。Rudessはどのリージョンでも動きます。
また、そのリージョンのEC2にKeyPairを作っておいてください。既に作成済みの方は、そのキーを使い回しても構いません。
利用するAWSアカウントのアカウントID(12桁の数字) *4を調べておいてください。My Accountのページで確認できます。画面右上(下記画像の右下)のハイフンで区切られた数字ですね。ハイフンは取り除いた12桁を後ほど使います。
変換元画像S3バケットの準備
続いて、Amazon S3 Management Consoleより、変換元の画像を入れておくS3のバケットを作ります。このバケットは、先ほど決めたリージョンに作るように注意してください。Tokyoですね。バケット名は適当に決めてください。ここではrudess-originとしていますが、この名前はもう私が確保済みなので、別の何かでドウゾ。その他の細かい設定(PermissionやPolicy等)は一切必要ありません。なので、既存のバケットでいい感じに画像が格納されたものがあれば、敢えて作らなくても大丈夫です。
バケットを作ったら、画像をいくつかバケットに放り込んでおきましょう。RudessはJPEG, GIF, PNGの3種に対応しています。
Rudessの起動
CloudFormationのManagement Consoleより、スタックを作成します。Create Stackボタンをクリック。
Stack Nameは適当に「rudess」とかにしておきましょうか。Templateは一番下の「Provide a Template URL」を選択し、URLとして以下を入力します。
https://s3-ap-northeast-1.amazonaws.com/cm-cfn-templates/rudess/rudess-0.1.template
続いて、Rudessの設定(CloudFormationのパラメータ定義)を行います。3点だけ修正/記入が必須です。
- OriginBucketに、先ほど作成したバケット名を指定してください。
- AWSAccountNumberに、先ほど調べたアカウントID(12桁)を指定してください。
- KeyNameに、先ほど確認したEC2のKeyPair名を指定してください。
その他のパラメータはデフォルトでOKです。が、一応解説。
- RudessInstanceTypeは、サーバのEC2インスタンスタイプの指定です。t1.microは選ばないでください。動作検証していない上に「多分動かないんじゃないかなぁ」っていう心当たりがありますw
- Rudessは裏でDynamoDBのテーブルを1つを使用していますので、その分のReadCapacityUnitsWriteΩCapacityUnits設定があります。
- SecretPhraseは、URLハッシュ計算(後述)のための文字列です。実運用する場合は変更しましょう。
- RudessはAutoScaling構成になっていますので、FrontendFleetMaxSize及びFrontendFleetMinSizeを設定しても構いません。
- SSHLocationは、EC2サーバにSSH接続できるネットワークブロック指定(CIDR)です。
次の、タグ設定の画面はデフォルトのまま飛ばして構いません。
最後に設定のチェックを行い、Continueボタンをクリックします。
ここからは自動で環境構築です。30分弱、そのままお待ち下さい。20〜25分程度でステータスはCREATE_IN_PROGRESSからCREATE_COMPLETEに変化しますが、そこからELBの配下でインスタンスが正常に稼働するまで、さらに数分かかります。
約30分程度経過したら、表示のRefreshを行い *5、ステータスがCREATE_COMPLETEとなっていることを確認してください。その上で、Outputsのタブに移動すると、RudessURLというURLが手に入ります。実運用する際は、独自ドメインからこのドメインに対してCNAMEを貼れば良いでしょう。
このURLに飛んで、「ok」と言われれば、起動成功です。okが帰ってこなかったら、もうしばらく待ってみてください *6。
ちなみに、スケールアウトを確認してみたい方は、変換サーバのEC2インスタンスにSSHでログインして、例えば以下のコマンドなんかでCPU負荷を掛けてみてください。5分もすれば、2台目が立ち上がるはずです。
gzip -9 </dev/urandom >/dev/null
つかいかた
http://hostname/v1/{hash}/{command}/{key}
RudessのURLは、上記のような構成になっています。右側から説明しますね。まずkeyというのは、S3バケット内の画像ファイルのキー(パス)です。バケットのルート直下であれば、ただのファイル名ですし、フォルダ階層があっても構いません。
次にcommandは、この画像をどのように変換したいのかを表します。この辺り、mod_tofuのインスパイアが盛りだくさんですが、画像の部分切り出しにはまだ対応していません。
最後にhash。これもmod_tofuインスパイアです。例えばRudessを何らかのWebサービスに使った時、このURLの法則性さえ分かれば、色んなサイズの画像を誰でも取り出し放題になってしまいます。CPUパワーも馬鹿になりません。そこで、Rudess起動時に設定したSecretPhraseを知っている人だけが、正しいhashを算出できる、という仕組みです。hashが間違っているリクエストは拒否されます。
hashの計算式はmd5(secretPhrase + "/" + command + "/" + key) です。従って、secretPhraseがRudessRocks!であり、Beer.gifというファイルを元にして100x200cという加工を掛けたい場合、例えばOSXであれば以下のようなコマンドで求めることができます。
$ echo -n 'RudessRocks!/100x200c/Beer.gif' | md5 8bbda4f24248a32763ab1e533d7fca9c
結果として出来上がったURLは以下の通り。
http://hostname/v1/8bbda4f24248a32763ab1e533d7fca9c/100x200c/Beer.gif
初回はS3から元画像をDLしてリサイズをする、という処理が入るので、少々時間が掛かりますが、あとはCloudFrontがキャッシュしてくれるので、爆速になります。
さて、こういったURLにアクセスすると、それぞれどんな画像が返ってくるか、いろいろ例示してみます。
基本的に *7画像のアスペクト比は維持します。画像が縦横にびろーんと伸びたりする変換は、今の所できませんし、ひとまずあまり要らない気もしています。サイズのみを指定した場合、指定されたサイズのボックスに収まるように元画像を縮小します。cオプション(cropping)を使うと、指定されたサイズになるよう、上下または左右をトリミングします。
mod_tofuと(多分)異なる点としては、Rudessは基本的に「拡大」をしません *8。図中で、300x300と元画像が同じなのは、拡大をしないからです。元画像が小さかった時に、指定サイズの拡大もしたければ、eオプション(expansion)をつけます。
あと、qオプション(quality)も実装してあります。まぁ、q0〜q99まで意味がありそうにみえるインターフェイスですが、現状4段階しかありません。将来色々の予定。
あと、制限事項として、今の所512x512以上の指定はできません。あんまりデカくても用途無いかな、と思って。あと、アニメーションGIFの変換は上手くできません。最初の1コマ目の静止画像をリサイズする感じになるはずです。将来は対応してみたいなぁ、とは思いますが。
後片付け
CloudFormationで環境構築をすると、後片付けが楽なのも嬉しいところです。先ほど作成したスタックを選んで、Delete Stackボタンをクリックしてください。片付けにも十数分かかりますが、全ての関連リソースを跡形もなく削除してくれます *9。
今後の展開
さて。今回ご紹介したのは「FREE版」ということで、画像のサイズ変換にしか対応していません。まぁ、これはこれで、ご自由に使って頂いて構わないのですが、今後は「PRO版」の機能を色々考えていきます。モノクロ・セピアにしたり、回転・反転したり、文字を書き込んだり、漫画加工をしてみたり、フレームを付けたり、肌がキレイに見えるようにしたり、ゴハンが美味しく見えるようにしたり、モザイクを掛けたり、目からビーム出したり、まぁネタはいっぱいあります。まぁ必要に応じてだったり面白おかしくだったり、色んな画像変換機能を実装して行きたいと思っています *10。
あとは、CloudFrontでキャッシュされているとは言え、一度変換した画像は別のバケットに永続化しておきたいなぁ、とも考えています。が、上手いアーキテクチャがまだ閃かず。同じ事をしようとしてる人は一杯いるんですが、同じように困っているようです。
- https://forums.aws.amazon.com/message.jspa?messageID=421512
- https://forums.aws.amazon.com/thread.jspa?messageID=295499
- http://stackoverflow.com/questions/8709206/how-can-i-redirect-image-404-error-on-amazon-s3-to-my-own-server
あと大事なところで、変換処理自体の速度もまだまだ改善の余地があり、課題だと思っていますので、これも今後ですねー。
というわけで、PRO版のお値付け *11等も然るべき人が担当してをりますので、ご興味を持って頂けました場合はお問い合わせなど頂ければと思います。
脚注
- 話は簡単だが対応が「面倒」であることも含む。 ↩
- 今の所、アプリのソースを公開したりはしてません。 ↩
- ほとんどが待機時間。 ↩
- CloudFormationテンプレート内で、DynamoDBのARNが必要なのですが、AWS::DynamoDB::Tableから取り出す事ができません。RefやFn::GetAttで取得出来ればいいのですが。取得出来ないのであれば生成するしかありません。そのためにアカウントIDが必要となります。ただ、アカウントIDを取得する方法もないので、パラメータとして提供してもらうしかないのです…。疑似パラメータで取れればいいんですけどねー。 ↩
- 右上のRefreshボタン ↩
- ただし、一度失敗してしまうと、数分間はCloudFrontが「エラー」をキャッシュしてしまいますので、あまり慌てないのが得策です。 ↩
- cオプションを使わない限り。 ↩
- cオプションを利用した場合は、拡大もします。 ↩
- 最初に作ったS3のbucketだけは、CloudFormationで作ったものではありませんので、手動で消す必要があります。 ↩
- そんな展開を見据えつつ、ボスはサービス名「美人写真」「美食写真」等と言ってノリノリであります。 ↩
- といっても、恐らく他サービスをご契約頂いた際の付帯サービスという形で、Rudess PRO自体の利用料は頂かない、という形もあるかもしれません。応相談デス。 ↩