ロングテールな画像配信 その2 - 3,000万の画像を配信するシステム
Squidを検索する度に最初に表示される画像検索の結果に吹き出しそうになる開発部・システム運用グループの長野です。前回のロングテールな画像配信のその2ということで、実際の画像配信システムについて書かせて頂きます。
■プロフィール画像の配信について
前回紹介しましたが、mixiにおいてプロフィール写真を設定を設定しているユーザ数は全体の約70%、1,000万人の方が設定をされています。現在配信をしているプロフィール画像のサイズは180x180、76x76、40x40と3サイズあり、合計3,000万以上のファイル数になっています。また、もっともよく使われる76x76のサイズ1,000万件において、1日にアクセスされる画像の数は800万ファイル以上、うち97%が30回以下と非常に広範囲に渡ってアクセスされています。そのため大量の画像を配信できる仕組みが必要になります。
■配信システムの全体像
プロフィール画像の配信システムの全体像です。
画像の保存の部分から説明していきたいと思います。
■ストレージ層
edit_photoの画面や携帯メールからプロフィール画像をアップロードすると、アプリケーションサーバから変換(サイズの変更)と転送を行うサーバへ画像が渡されます。画像を変換するライブラリはメモリを多く使うことが多いのでWeb画面とは別のサーバで変換を行っています。
画像を変換を行った後、配信兼ストレージサーバにコピーされます。ストレージサーバはバックアップと負荷分散を目的に2台1組で構成されており、画像のアップロードは必ず2台へ行われます。
どのグループに保存されるのかは、画像のファイル名の番号の剰余によって決まります。もしストレージサーバに障害がおきて保存が出来なかった場合には、ファイル名を変更して別のサーバに保存をします。
実はこの変換の時には180x180のサイズしか作成していません。あとのサイズは配信時にオンザフライで生成しています。これでファイル数を一気に1/3まで減らす事ができます。ディスクIOのコストは画像の変換にかかるCPUのコストよりも高いと考えています。
■Reverse Proxy層
今度はもっとも外側のサーバです。プロフィール画像へのリクエストはまずこのサーバで受ける事になります。これはmod_proxy(mod_proxy_balancer)とmod_rewriteなどを組み込んだApacheです。
RewriteRule ^/photo/member/([0-9]+/[0-9]+/[0-9]+_[0-9]+)([sm]).jpg$ ¥ ? balancer://balancer/photo/member/$1$2.jpg [P,L] RewriteRule ^/photo/member/([0-9]+/[0-9]+/[0-9]+_[0-9]+).jpg$ ¥ balancer://director/photo/member/$1.jpg?? [P,L]
76x76と40x40の画像はbalancerに、180x180の大きな画像はdirectorにそれぞれproxyされます。balancerは次に紹介するSquidサーバになり、directorはSquidサーバ群の下にあるサーバになります。180x180の画像についてはアクセスは比較的少ないのでSquidによるキャッシュ層を通さずに配信しています。
■Squid層
Squid層は2つのレイヤーに分かれます。Squidによるcacheサーバとそれを分散するbalancerレイヤーの2つです。
最初に紹介しましたが、プロフィール画像は非常に広範囲なURI、1,000万以上の画像にアクセスがあり、これらを高速に配信するためには画像データをメモリに載せる必要があります。Squidにキャッシュさせる画像のサイズのうちの1つ、76x76の容量の平均は約2.7KBになるので、必要なメモリは最低27GB以上のメモリが必要になります。1台のサーバ(最近のメモリの値段から言えば不可能ではないですが)ではメモリが足りないので、キャッシュデータを複数のサーバに分散していく必要があります。そこで利用したのがSquidに備わっているCARP(Cache Array Routing Protocol)という機能です。
*参考資料
carp.txt
http://icp.ircache.net/carp.txt
Cache Array Routing Protocol and Microsoft Proxy Server version 2.0
http://www.microsoft.com/technet/archive/proxy/prxcarp.mspx?mfr=true
CARPはmemcached界隈で話題になるConsistent-Hashingと考え方は似ていて、URIとcacheサーバのhash値を組み合わせてアクセスするサーバを決定します。サーバの追加や障害時の切り離しを行っても大幅なキャッシュの組み替えが発生しないという特徴もあります。詳しく知りたい方は上のURLやsquidディストリビューション中ののsrc/carp.cソースコードを参考にされるといいと思います。 SquidでCARPを利用する場合は、squid.confにおいて、cache_peerにcarpと指定します。Squidの制限としてキャッシュを横に並べるsiblingモードでCARPを利用する事ができないため、Squidが2段構成になります。
http_port 80 vhost vport cache_peer 192.168.1.11 parent 80 0 no-query no-digest carp proxy-only weight=1 cache_peer 192.168.1.12 parent 80 0 no-query no-digest carp proxy-only weight=1 cache_peer 192.168.1.13 parent 80 0 no-query no-digest carp proxy-only weight=1 cache_peer 192.168.1.14 parent 80 0 no-query no-digest carp proxy-only weight=1
balancerとなるSquidサーバではcache等はせずproxyだけの機能で利用しています。上の設定では192.168.1.11〜14がcacheサーバとなりcarpアルゴリズムにてproxyしています。Apacheのmod_proxy_balancerでもbalaceの方式をモジュールで追加することができるので、こちらも研究しています。 CacheサーバとなるSquidでは、cache_dirとしてCOSSを利用しています。COSSは大きな単一ファイルにキャッシュデータを保存する形式で以前からSquidにあるufsやdiskdと比較して格段に性能が高く、効率のよいキャッシュを構築できます。
cache_dir coss /var/spool/squid/coss 8000 block-size=512 max-size=500000 cache_swap_log /var/spool/squid/%s http_port 80 vport cache_peer 192.168.1.15 parent 80 0 no-query no-digest round-robin originserver cache_peer 192.168.1.16 parent 80 0 no-query no-digest round-robin originserver
cacheサーバから再び、cache_peerでストレージサーバの上にあるdirectorサーバ(192.168.1.15と16)を指定しています。round-robinオプションを指定する事で負荷分散と冗長化も可能です。
■director層
最後の層になるdirectorですが、これは通常のApacheです。mod_proxy(mod_proxy_balaner)とmod_rewriteが組み込まれています。Reverse ProxyやSquidのcacheサーバからリクエストを受けて、そのURIから画像ファイル名の剰余を求め、正しいストレージサーバへproxyします。
この層を置かずに、reverse proxyやSquidのcache層から直接にストレージサーバへ接続を行う事もできますが、Squidやインターネットに接するサーバの設定を極力シンプルに保つためにこのような構成になっています。ストレージサーバの障害時にはこのdirectorサーバの設定を変更するだけなど影響を極小化するのにも役にたちます。
■まとめ
2回に分けてロングテールな画像配信という事で、mixiのプロフィール画像の配信について紹介してきました。画像の配信についてあまり気にされること多くはないと思いますが、裏側には様々な工夫があります。今回紹介してきた3,000万ファイル以上あり、広範囲にアクセスされるプロフィール画像の配信システムも、何度かの試行錯誤を経験した上でApacheやSquidなどのオープンソースソフトウェアを利用して高速でかつ、安定している仕組みを作り上げられています。
また機会があれば今回紹介しなかった画像配信についても紹介したい思います。