(レポート) STG406: S3を使った数百万ユーザー対応のスケーラブルで無制限なストレージサービスの構築 #reinvent

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは、虎塚です。

「STG406 - Using S3 to Build and Scale an Unlimited Storage Service for Millions of Consumers(日本語タイトル: S3を使った数百万ユーザー対応のスケーラブルで無制限なストレージサービスの構築)」を聴講したので、レポートします。

これは、Amazon Cloud Driveの開発者の方が、容量無制限のストレージサービスをどのように作ったかを解説してくれるセッションです。発表はTarlochan CheemaさんとKevin Christenさんでした。

このセッションで扱うこと

  • Amazon Cloud Driveとは何か
  • キーとなるチャレンジ
  • サービスデザインとアーキテクチャ
  • コンテントストアについてのディープダイブ
  • 経験から得た知見

容量無制限のストレージサービスを構築する上で、ユーザーのファイルそのものやメタデータはどのように保存すればよいでしょうか。カスタマーエクスペリエンスを維持しつつ、大規模なユーザーに使ってもらうにはどうすればいいでしょうか。

このセッションでは、経験から得たデザインパターンやベストプラクティスなどの知見を共有します。それらは、開発者が高度にスケーラブルなサービスをS3で作るときの役に立つでしょう。

Amazon Cloud Driveの紹介

Amazon Cloud Driveとは

  • Amazon Cloud Drive: Cloud Storage - Online Backup

  • Amazonが消費者向けに提供する容量無制限のクラウドストレージ

    • 無制限の写真ストレージ
    • 年間$11.99払うと、動画とファイルのための容量を+5GB利用できる
  • サブスクリプションベースのストレージプラン
    • 年間$59.55払うと、ファイルの種類を問わずに無制限の容量を利用できる

最後のプランがunlimited planです。3ヶ月のお試し期間もあるとのこと。

場所を問わず、さまざまなデバイスから、どのようにして使うのか

Amazon Appstore, Apple App Store, Google Play, Webアプリ、デスクトップアプリなど、Amazon Cloud Driveを使うためのさまざまなアプリケーションがApps: Extend Amazon Cloud Driveで公開されています。

こういった状況で、ストレージ提供側は、キャパビリティをどのようい管理するのでしょうか?というのが問題提起です。

開発者やパートナー向けに提供されるもの

Amazon Cloud Driveは数百万人に利用されているので、アプリ開発者はより多くの顧客にリーチできるようになります。Amazonからは、RESTfulなAPIに加えて、開発者がアプリを素早く開発できるようにAndroidとiOS用のSDKが提供されています。

このようにAmazon Cloud Driveを取り巻くエコシステムは広がっていて、ファイル管理や同期などのツールがどんどん作られています。あなたもいいアイデアがあったらアプリをつくってレベニューシェアしましょう、とのことです。

キーとなるチャレンジ

Amazon Cloud Driveには、次のような課題があります。

  • 無制限の容量
    • 膨大なコンテンツをどうやってハンドルする?
  • 数百万ユーザ
  • 数十億のファイル
  • さまざまなコンテンツ(写真、動画、ドキュメント)
  • さまざまなメタデータ
    • 新しいコンテンツタイプ、変化するスキーマ
  • 柔軟なインデックス作成とクエリ発行
  • テラバイトに至るログ
    • ログをどう管理すればよいか、サービス管理をどうするか

キーとなるデザインゴール?

一貫性のある素晴らしいカスタマーエクスペリエンスをどのようにして達成するか、といったことを考えたときに、次のような事項が設計のゴールとなるでしょうか。携帯電話やタブレットなど、さまざまな端末で利用する顧客にどう対応すればいいでしょうか。

  • 高い可用性
  • 永続性
  • 信頼性
  • RESTful
  • 低レイテンシー
  • ほぼリアルタイムのクエリ発行
  • 一貫性
  • 冪等性
  • 低コスト

アーキテクチャ

Amazon Cloud Driveのアーキテクチャの紹介です。

サービスのキーとなるのは、コンテンツストアであるS3です。スケーリングパフォーマンスに優れているため、ここにS3を使っています。一方、ファイルのメタデータはDynamoDBに格納されます。

利用者がファイルをpushすると、メタデータとコンテンツが格納されます。キーデザインパターンは、メタデータとコンテンツを同時かつリアルタイムに格納することです。また、ファイルがアップデートされる時にも、メタデータが格納されます。

その裏では、Kinesis StreamにデータをrepushするためにSQSワーカーが動きます。通知にSNSも使用されています。

ダウンロードのリクエストがきたら、メタデータとコンテンツを参照して、デバイスに適したフォーマットになるように画像変換します。特にビデオやドキュメントの場合は、Elastic Transcoderを利用した変換によって、さまざまなデバイス上でのプレビューを実現しています。

S3上のCloud Driveストアは何をするか

(ここからスピーカーが交代しました)

Cloud Driveストアでは、コンテンツに対して次のようなことをします。

  • 顧客のコンテンツ
  • 取り出されたコンテンツ
    • ビデオをトランスコードしたり、サムネールを作成したりします
    • ドキュメントをPDFに変換したりもします
  • ログファイル
  • 動的な設定変更
  • 動的なDBバックアップ
  • Amazon AWS SDK の使用

コンテンツをストレージから探すためにはメタデータが重要です。メタデータは、DynamoDBのテーブルに格納されています。テーブルスキャンの際にも必要です。

顧客コンテンツの格納

顧客コンテンツは次のように扱います。

  • リージョン1つにつきS3のバケット1つ
    • コンテンツバケット1つに、数十億個のオブジェクトが保存されます
    • S3はオブジェクトの個数に上限がないのでこれでOKです
  • キーのランダム生成
    • キーはAmazon DynamoDBに保存されます
    • パーティションキーとなるため、性能問題が出ないようにホットキープリフィックスを避けました。イーブンにばらけるように工夫したのです
  • S3のサーバサイド暗号化を利用
    • AES 256

ログファイル管理

  • Cloud Driveは、(ピーク時には)3つのリージョンにまたがる800以上のサーバから構成されます
    • Amazon.comのピークタイムは、クリスマスシーズンやギフトシーズンです。その時期には、KindleやHDを皆が買います
  • ピーク時は1時間あたり200GBのログが発生します
  • Timberのログアーカイブサービスにログを配信しています
  • Timberはログを暗号化してくれます

ログファイルの種類

システムのパフォーマンスを把握するために、ログにはタイムスタンプやセキュリティタグを埋め込みます。

  • アプリケーションログ
    • タイムスタンプと厳密にタグ付けされたメッセージ
  • サービスログ
    • Amazon全体の標準的なフォーマット
    • サービス呼び出しごとに記録
    • メトリックスの元となるもの
  • ワイヤログ

ログファイルの処理の流れ

TimberによってすべてのログがS3上にアーカイブされます。Amazon EMRでジョブを回して処理した後、ログファイルはS3に書き戻されます。その後、サービスログはS3からRedshiftへ並列処理で読み込まれます。

動的な設定調整

  • feature togglesのように頻繁な変更
    • テストユーザに対してある機能をオンにしたり、機能の性能を0%から100%まで調整したりできます
    • これによってプロダクションスケールでテストできます。予期しない事態があれば0にすればよいのです
  • S3にあるファイルの設定
  • 設定変更のためにHTTP HEADを使ってサーバにpollする (GetObjectMetaData)
  • ETagが変更された時だけファイルを再読み込み

  • 参考: FeatureToggle

Challenges

Challenge 1/6: アップロードするサイズのばらつき

  • ユーザによってアップロードされるファイルのサイズは実にバラバラです
    • テキストファイルのように軽いものから、VMのマシンイメージのように巨大なものまであります
    • 10KBのGif画像から20MBのローデータまで
  • すべてのファイルサイズにとって良いパフォーマンスが出るように調整しました
  • 巨大なファイルによってリソース枯渇が引き起こされることを避けました

解決策は、ファイルサイズを意識したアップロードロジックでした。

  • PUTされるオブジェクトは15MBより小さくなるようにしました
    • リクエストスレッドによるアップロードのパフォーマンスを維持するためです
  • 15MBより大きい時、もしくはサイズがわからない時は、S3のmultipart upload APIを利用しました
    • スレッドプールを利用して分割アップロードをおこなうことで、ファイルサイズをハンドルできるようにしました
    • ファイルサイズを5MBに固定
    • multipart upload APIの上限が10000分割のため、ファイルサイズ上限を50GBに設定

Challenge 2/6: 高速なアップロードの可用性

  • アップロードされたコンテンツはできる限り早く利用可能になるべきです
  • しかし、処理に時間がかかるコンテンツもあります

解決策は、同期と非同期を混在させて、楽観的同期処理をおこなうことでした。

  • 画像や動画からメタデータを取り出す処理は、高速で、ファイルサイズと無関係なので、同期で処理します
  • 動画のトランスコーディングは、非同期で処理します
    • さまざまなデバイスで再生できるようにトランスコードする必要があります
    • 時間がかかりますし、動画ファイル自体のサイズに依存します
    • Amazon Elastic Transcoderを利用しました
  • ドキュメントのPDFへの変換には、楽観的同期処理をおこないました
    • タイミングが予期できません
    • タイムアウトとあわせて同期処理を試してみます
    • もしタイムアウトしたら、非同期処理で対応するためにSQSメッセージにキューします

ドキュメントのPDF変換は、ファイルをすべてのデバイスで閲覧できるようにするために重要な処理です。ここに楽観的同期処理を使うことで、レイテンシを最小化することができました。

Challenge 3/6: 断続的なコネクション

  • クライアントはAmazon Cloud Driveに対して、スローで断続的な接続をおこなうでしょう
    • モバイルデバイスからのアクセスの場合は特にそうです
  • このため、単一のHTTPリクエストで大きなサイズのファイルをアップロードすることは困難です
  • しかしmultipart upload APIは複雑です
    • 特に異常系に対応しなければならないケースでは

解決策は、途中から再開できるようにアップロードすることでした。

  • クライアントが大きなファイルをアップロードしようとします
  • もしストリームの途中でアップロードに失敗したら、Cloud Driveは送信されたバイトデータを保存します
    • 既存のS3 multipart uploadをさらに拡張するものです
  • 再開点に向けてクライアントがクエリを投げます
  • クライアントはアップロードを再開します
    • HTTP Content-Range headerを利用します
  • Cloud Driveはmultipart uploadを完了します

小さなパイプを作ってファイルを小さい単位で送るようにしました。

しかし、インスタンスのプロファイルクレデンシャルが使えないという問題が、新たに持ち上がりました。一連のmultipart uploadを異なるインスタンスから実施することができません。

アップロードの各ステップでローカルクライアントを一貫して認証する仕組みが必要でした。そこで、IAMが生成するトークンであるAWS Security Token Service (STS) を利用することで解決しました。

Challenge 4/6: ダウンロードサイズのばらつき

  • アップロードと同様に、ダウンロードするファイルサイズもばらばらです
  • すべてのファイルサイズで最適なパフォーマンスが出るように調整しました
  • リソースの枯渇を引き起こす大きなリクエストを避けました

解決先は、ここでもやはり、サイズを意識したダウンロードロジックでした。

  • 5MB未満の小さなファイルのダウンロード
    • 単一のGET objectリクエストスレッドで取得できます
    • 失敗したら1回だけリトライします
    • この方法で顧客のファイルの90%をカバーできました
  • 大きなファイルのダウンロード
    • 巨大なファイルにはカスタマイズした並列ダウンロードロジックを用意しました
    • レンジリクエストで5MBずつのサイズに分けました
    • アップロードや小さなファイルのダウンロード処理への影響を避けるために、ブロッキングキューを使ってスレッドプールを分離しました
    • コネクションプールでコネクションを再利用します
    • 失敗やタイムアウトが起きた時には1回リトライします
  • Apache HTTPClientを利用しました

大きなファイルのダウンロードでは上記のような処理が必要なため、S3とEC2の間でファイルをダウンロード(移動)しました。フェイルバックをクライアントに伝えることができるようにしました。

Challenge 5/6: 大きな画像のサムネール

解決策は、次のとおりです。

  • 中間サイズのJPEGサムネールを生成し、S3のバケットにキャッシュします
    • S3バケットにキャッシュしたファイルは48時間で期限切れにします
    • ハッシュキー: customer ID + image id + image version
    • 2k x 2kサイズのJPEG、ファイルサイズは1MBまで
    • キャッシュ候補: 10MBより大きいJPEG, PNG, TIFF、他

上記の処理を、サムネール取得のリクエストの中で実施するようにしました。大きな画像のサムネールがリクエストされたら、中間サイズのものをJPEGでサムネール用のバケットへ格納します。そして、ポリシーをエスカレーションして、次回からサムネール用バケットへ取りに行くようにします。

90%はコンテントバケットから取得されるものの、意味があったそうです。

Challenge 6/6: 巨大な直接ダウンロード

サムネールをon-the-flyで作ったようにはいかないケースもあります。大きなファイルをディスクにダウンロードするのは有意義とはいえません。そういう場合は、短期間だけ有効なS3 pre signed URLへ、リクエストをリダイレクトするようにしました。

持ち帰ってほしいポイント

  • S3はじつに柔軟性のあるシステムです
    • ビッグデータだけでなく、顧客のコンテンツ、キャッシュ、ログファイルなど、何にでも向いています
  • S3キーに何を選択するかは重要です
  • アップロードとダウンロードの戦略は、ファイルサイズとワークフローに依存します
    • 異なるサイズのものを1つのサイズの塊としてダウンロードすることもあります
  • 分散コンピューティングの最初の誤信は、ネットワークが信頼できるというものでした(!)
    • アップロードやダウンロードのリクエストをリトライさせることはおそらく適切です
    • ただし、1回だけリトライするのが大事です

AWSではシンプルであることが大事であると語られました。

発表の最後に

とのことです。

おわりに

やはり開発者の方が発表されるセッションは、話が具体的で面白いなあと思いました。Cloud Driveという世界的な人気サービスを開発している人たちの話となると、なおさらですね。

ファイルのアップロードやダウンロードの機能が重要な位置を占めるシステムを開発する人にとっては、非常に参考になるセッションだったのではないかと思います。

それでは、また。