2010/09/22

画像配信システムその2

こんにちは、hiroshiです。
自分のことをhiroshiとかアルファベットで表現すると軽く有名人っぽくて気分がいいです。みなさんも是非やってみてください。

さて、前回の記事の最後に紹介させていただいたはITメディアさんの記事では、記事画像配信の仕組みについて触れられています。
あの仕組みは「デコメで投稿される記事に使われている画像」の特性から現状のあの構成になりました。

一方でDECOLOGで扱われる画像はもちろん記事画像だけではありません。
代表的なものに、ブログTOP画像というのがあります。

ブログTOP画像は、その名の通りブロガーのみなさんの顔ともいえるブログのTOPページを飾る画像です。

図1.ブログトップページサンプル

上図の通りページ上部に配置される画像であるため、携帯画面でのファーストビューはこのブログTOP画像で占めらる割合が大きいです。
つまり、「ブログTOP画像=ブロガーのみなさんの顔」といっても過言ではなく、少しでも早く表示させたい画像なわけです。

さて、ITメディアさんの記事で、記事画像配信の仕組み変遷の歴史画像が掲載されていますが、あれの3代目まではこのブログTOP画像も同じパケットのフローにのっていました。違っていたは一番下のDBだけです。

しかしながらブログTOP画像と、記事画像では大きな違いが2点あります。
  1. アクセスの頻度が違う。記事画像は時間が経てばが落ちるが、ブログTOP画像は一定。
  2. データ量が違う。記事画像は際限なく増加するが、ブログTOP画像は仕様上、ブログ開設数×2が上限

1から、アクセス頻度がロングテール化する記事画像のようなアーカイブ方式はそぐわないことがわかります。
この大きな違いを持つ2種類の画像が共存していたため、squidのキャッシュ効率も悪く(たぶん)、ブログTOP画像の表示がもっさりする、登録しても反映が遅い(=replication delayの発生)ことがしばしばありました。


なので、「まずブログTOP画像をなんとかしよう!」ということで対策会議を開きました。といっても技術責任者のstoneと2人でベランダでタバコすいながら「なんかいい方法ないっすかねー」みたいなノリでしたけど。
で、いろいろ議論を重ねた末に、「とりあえずこれでやってみっか!」となったのが下図の構成です。

図2.ブログトップ画像構成

Before、Afterでの大きな違いは、画像の保持方法を動的(DB管理)から静的(FS管理)に変えたことです。
2の「データ量が違う」ということから、ITメディアさんの記事で触れられているinodeの枯渇の心配もないことがわかり、だったらDBにぶち込まないでそのまま置いといたほうが早くね?ということでそうしてみました。

画像サーバは読み取りと書き込み用にポートを分けておき、書き込み用には画像を受け取る用のPHPスクリプトを置いておきます。

書き込み処理の流れを追ってみます。ブログトップ画像の登録はメール送信で行われます。
  1. qmailで受け取ったメールはパイプ処理でphpプログラムに渡す
  2. phpでメールの中身を解析し、画像ファイルの取り出し・加工処理などを行った後、
  3. DBにぶち込み、
  4. HTTPでブログIDをキーに決められた格納先画像サーバとバックアップ用画像サーバにPUTする。

PUTと言っても、単純にrequestに画像データをぶち込んで画像サーバの81に向けて特定のURLを呼び出すだけです。

METHODとしてのPUTを使ってみる方法(WEBDAVとかを使うんだったけかなあ?)も試行錯誤してみたんですが、作業を担当したのが2流の僕なのですぐ音を上げました。「stoneさん、これムリっす!」「じゃあ、あと1時間だけ挑戦してダメだったらやめよっか」やっぱりだめでした。。

言い訳っぽいのですが、あのまま試し続けていればカッコいい方法が実現できたかもしれません。ただ、慣れてない方法でやるよりもシンプルかつ自分が完全に理解できている方法でやったほうが、トラブル時などの対策案も多々浮かんできます。それになにより、人がいないので方針を決めたら作業完了までの工数が読めない方式は取りたくない、というのもありました。

一方で、どこかで新しいことやモノに挑戦していかないと手元のカードは増えません。状況に余裕があるときであれば、新しい手法・試したことのない手法はどんどん突っ込んでいくべきだと思います。

ということでまあ当時は余裕がなかったんです。

さて、話を元に戻します。
ユーザーから送られてきた画像は、3箇所に書き込まれます。もちろん理由があります。
内訳はDB1箇所に画像サーバ2箇所。

画像サーバ2箇所に書き込んである理由はすぐわかります。画像サーバがぶっ壊れたらすぐ代替と交換できるようにそうしてます。なので、バックアップ用サーバは全ての画像を保持しています。

DBに書き込んでいる理由もやっぱり画像サーバがぶっ壊れたときのためです。
どこかの画像サーバがぶっ壊れても、バックアップサーバを突っ込めばサービスとしては問題ない状態になりますが、その間に復旧作業をしなければなりません。

バックアップ用サーバからrsyncで、とかもちょっと考えましたが、普及中もユーザーによる追加/更新が行われ続けるため、完全に同期がとれるのかがすごく疑問でした。また、その検証方法も思いつきませんでした。

一方、DBで持っておけば、その辺の取り回しはいくらでも融通が効くので、復旧用のスクリプトさえ用意しておけば数時間で普及できる計算が成り立ちました。ということでDBにも書き込んでいるわけです。


読み込み処理の流れはシンプルです。URLにその画像がどの画像サーバにあるかがわかるようになってるのでmod_proxyで振り分けるだけです。

この仕組は想定通りの結果が得られ、今でも非常に安定して稼働しています。

本当は、既存の仕組みから新しい仕組みに切り替えるときに発生する「移行」についてもいろいろ工夫してきた話があるのですが、これはまた後日にさせてください。

では、また次回に会いましょう。