画像配信:SwiftからS3への引越について

こんにちは、インフラストラクチャ部の橋本です。
このエントリは GREE Advent Calendar 2015の12日目の記事です。

 

はじめに

グリーではOpenstack Swiftを使いSNSの画像の配信を行っていました。構成は下記のようなもので、WebDAVにWarp(Haskellで書かれていています)、一時的なキャッシュにはFlareを、恒久的な保存にはSwiftを使用しています。このたびSwiftからAWSのS3に切り替えることになりました。その引越にまつわる話です。

untitled

SwiftとS3は別もの(当たり前ですが)

Swiftを採用される理由としてS3のAPIと互換というものがあります。しかし、それなりの規模で運用するには、バケット(Swiftの用語ではコンテナ)の構成に別々の工夫が必要です。

Swiftはバケットをほぼ無制限に作れます。その一つのバケットのファイルリストはSQLiteで管理され、ファイルの数に対してスケールしません。
一方S3はバケットの数は有限(100個まで)です。そこに入れるファイル数に制限はありません。ただし、パフォーマンス上の問題でファイルのプリフィックスに気を付ける必要があります。グリーのSwiftのバケットの構成はパフォーマンスを最適にするようにつくってあり、上記の件を考慮して使用しています。そのため、SwiftからS3に引越にあたり、SwiftとS3でバケット名とファイルのパスの変換を行いました。

変換の例を見てみましょう。

例えば、/sns/hogehoge/78e273823823232.jpgのようにハッシュ値がついた画像ファイルの場合を考えてみましょう。

Swiftだと一つのバケットにいれるファイル数を制限したいので、ハッシュ値をバケット名に含めて使用しています。
3232というバケットに/sns/hogehoge/78e273823823232.jpgのファイルを入れてます。
(実際のものと違いますが、このようなものだとお考えください。)

一方S3だとバケット名は一律同じでファイル名の頭にハッシュ値をいれるような変換を行います。
例えば、以下のようにプリフィックスにハッシュ値をいれます。
/3232/sns/hogehoge/78e273823823232.jpg

上記のようなファイル名の変換や、SwiftのMETAデータの引越(Expireに使用)がありましたので、信頼性の高そうなHaskellを使って引越ツールを作成しました。s3cmdやswiftコマンド+bashよりはいいような気がしますよね?

ツールを作ってみたもののパフォーマンス上の問題点にあたる

HaskellでSwift->S3引越ツールを作成したのですが、パフォーマンス上の問題にあたりました。

SSLの通信の問題です。一般にHaskellでSSLのプログラムを書く場合はNative Haskellのtlsパッケージがスタンダードなのですが、こちらを使用するとCPUネックになって、一台で20Mbpsくらいでパフォーマンスが頭打ちになりました。
(僕の書き方の問題があるかもしれませんが。)
これではいつまでたっても引越が終りません。

調べたところopensslのラッパーがみつかり、すぐに解決し、一台で100Mbps以上でて、CPUネックではなくなりました。

その後、引越のボトルネックはSwiftなどのDiskIOとなりました。

実際に引越を開始してみたもののS3のエラーに悩ませられる

実際に引越をするとS3はエラーを頻発することがわかりました。引越ではPUTなリクエストを多く使っているためか(GETなリクエストではあまりエラーが発生しないのかもしれません)

下記のように多くのエラーにぶつかりました。結局、エラーの種類ごとにリトライと数十分の転送待機時間をいれて対応しました。

S3に比べてSwiftはエラーが少なくて使いやすいですね。

1, SlowDown Error

アクセスが速すぎる場合にでる503を発生するエラーです。一見するとアクセス数を減らすと改善しそうですが、転送速度に変化はないにも関わらず、24時間転送し続けて一回発生したりとなかなか厄介なエラーです。ファイルのアクセス順や転送速度を変えても撲滅できなかったです。

2,頻発するInternalError

500を発生するInternalErrorエラーで、Please Retryとかいってくるなんだかわからないエラーがよく起こります。(よくといっても500万回に一度とかです。)

3,RequestTimeout

400を発生するエラーで、20秒通信がないと勝手に切断するものです。SwiftからS3へストリームでデータを転送していたのですが、Swiftの読み込みが遅く時々発生しました。

4,その他のエラー

その他通信途中にセッションが途切れたり、いろいろあり、Haskell側でHttpExceptionが飛んで来ました。

仕様の確認忘れとその後の対策

こうしてみるとS3だけが悪者のような感じがしますが、そうではありません。

引越はユーザー単位で行っていて、個々のユーザーのファイル一覧を取り出して、それを元に引越を行ってます。そのファイルの一覧をとるAPIは最大1000件までです。1000件を越える場合はNext Markerを指定してもう一度とりにいく必要があります。次の1000件をとるロジックをいれていませんでした。仕様書はよく読みましょう。

みなさま、ご迷惑をおかけしてすみませんでした。

再発防止のため、その後S3とSwiftのファイルの一覧を取り直して比較するようにしました。

その後

その後、S3との瞬断があったり、その他いろいろありましたが、無事お客様のデータを守りながら引越できました。

WebDAVのところでみているだけなので、お客様の体感と違う可能性がありますが、以前よりほんの少し品質(レスポンス速度)があがっているはずです。

引越にあたり、下記のようにSwift、S3、Haskellにそれぞれいいところとわるいところがありました。

Swift

  • Pros
    • Httpのプロトコル上のエラーはない。(400や50x系の例外)
    • 動作が安定している。(刺さらないし、瞬断とかもない。)
  • Cons
    • 刺さりはしないが引越のボトルネックになるくらいにレスポンスが悪化する。

S3

  • Pros
    • 刺さったりしないし、レスポンスは安定している。
    • 書き込みミスはない。(MD5を見ている限り)
  • Cons
    • エラーが多い。
    • 一度瞬断が発生した。

Haskell

  • Pros
    • ファイルのパスの変換のミスはなかった。
    • 独自ツール故にMETAデータも引越できた。
    • 並列処理が簡単にかけた。
    • 頻繁にコードの修正を行いましたが、バグを直すためにバグをいれこむようなことはなかった。
    • md5sumでデータのチェックを実施(不一致になることは一度もなかったですが。)
  • Cons
    • 仕様書を熟読する必要がある。(S3からファイルの一覧をとるところで漏れが発生したことから)
    • エラー処理への対応(s3cmdでも同じ問題にぶつかっていたようですね。)

本業務を行うにあたり、いなわちゃんと舟橋さんに大変お世話になりました。
ありがとうございました。

明日は奥畑さんによるCocos2d-xの記事です。お楽しみに!