kakakakakku blog

Weekly Tech Blog: Keep on Learning!

Amazon S3 のマルチパートアップロード・署名付き URL・Transfer Acceleration を組み合わせてファイルをアップロードするソリューション

Amazon S3 バケットにサイズの大きなファイルをアップロードするソリューション aws-samples/amazon-s3-multipart-upload-transfer-acceleration を試した❗️このソリューションは Amazon S3 の機能「マルチパートアップロード」「署名付き URL」「Transfer Acceleration」を組み合わせて実現されている💡ちょうど似たようなアーキテクチャを検討していて,参考になるサンプルを探しているときに発見した \( 'ω')/

ソリューションのサンプル実装は GitHub に公開されている.

github.com

またソリューションの解説記事は AWS Compute Blog に公開されている.

aws.amazon.com

アーキテクチャ

ブログ記事に載っているアーキテクチャ図を見るとわかる通り,Amazon API Gateway + AWS Lambda で Amazon S3 のマルチパートアップロードの処理をしている.またパートごとに署名付き URL を生成しているため,アクセスキーなどを共有せずに一時的な権限でファイルをアップロードできる❗️

さらに Transfer Acceleration を活用することによって,エッジロケーション経由で転送効率を高めている.Transfer Acceleration 自体の効果は Amazon S3 Transfer Acceleration Speed Comparison でも確認できる👌

Uploading large objects to Amazon S3 using multipart upload and transfer acceleration | AWS Compute Blog より引用

セットアップ

ソリューションには React で実装された「フロントエンド」と AWS CDK で実装された「バックエンド」が含まれていて,簡単にデプロイしてすぐに試せる.

バックエンドは cdk deploy コマンドを実行すれば OK👌 whitelistip に指定する IP アドレスは checkip.amazonaws.com などを使って確認できる.

$ cd backendV2
$ npm install
$ cdk deploy --context env="xxxxx" --context whitelistip="x.x.x.x"

フロントエンドは npm run start コマンドを実行してローカル環境で動かせば OK👌

$ cd frontendV2 
$ npm install
$ npm run start

試す

さっそくソリューションを試す.ブログ記事に載っていた例を参考に Docker Desktop の Docker.dmg (536.4 MB) をアップロードする.今回は以下のように2パターンを試して,アップロード速度の変化を確認した.

アップロード1回目

パートを分割せず,Transfer Acceleration も活用せずにアップロードすると 142.1087999999523 sec という結果だった(もちろん環境・タイミングによっても異なる).

  • Step 2 - Choose part size (MB): 600
  • Step 3 - Choose number of parallel uploads: 1
  • Step 4 - Use Transfer Acceleration: OFF

アップロード1回目

アップロード2回目

パートを細かく分割して,Transfer Acceleration も活用しつつ並列でアップロードすると 112.57530000019074 sec という結果だった(もちろん環境・タイミングによっても異なる).

  • Step 2 - Choose part size (MB): 40
  • Step 3 - Choose number of parallel uploads: 12
  • Step 4 - Use Transfer Acceleration: ON

アップロード2回目

深掘り

ソリューションの設定・実装も確認したので,ポイントをいくつか紹介しようと思う❗️

まず,フロントエンドから呼び出す Amazon API Gateway のパスは計4種類ある./initialize/finalize はマルチパートアップロードの開始と完了の処理になっていて,Amazon S3 のマルチパートアップロードの仕組み(お作法)を理解しておく必要があるため,あわせて以下のドキュメントを読んでおくと良いと思う.

docs.aws.amazon.com

  • [POST] /initialize(AWS SDK for JavaScript v3 で CreateMultipartUploadCommand を使う)
  • [POST] /getPreSignedUrls(AWS SDK for JavaScript v3 の s3-request-presigner で getSignedUrl を実行する)
  • [POST] /getPreSignedTAUrls(AWS SDK for JavaScript v3 の s3-request-presigner で getSignedUrl を実行する)
  • [POST] /finalize(AWS SDK for JavaScript v3 で CompleteMultipartUploadCommand を使う)

Amazon API Gateway のパス(リソースは削除済)

/getPreSignedUrls/getPreSignedTAUrls に関しては,どちらもパートごとの署名付き URL を生成して返す API だけど,AWS SDK for JavaScript v3 で S3Client を初期化するときに useAccelerateEndpoint(Transfer Acceleration を活用するかどうか)を設定するかどうかに違いがある💡

他にもフロントエンド側でパート数を計算したり,アップロード中に画面に表示される Step 6 - Monitor の進捗を計算したり,参考になる実装がたくさんあった❗️

パート数を増やす場合の注意点

ソリューションを試しているときに Step 2 - Choose part size (MB) のサイズを徐々に小さくして,パート数を増やしたところ,ある程度のパート数からアップロードが失敗するようになってしまった.AWS Lambda 関数のログを確認したところ,3秒のタイムアウトになっていて,パート数が多すぎると署名付き URL を生成する回数も増えて間に合わないことに気付いた.

2024-01-03T01:31:28.984Z 928bde21-3a8f-4f5c-888b-6d781ff8a831 Task timed out after 3.12 seconds
2024-01-03T01:46:44.240Z 18e402db-214b-42d8-93fa-75b09bca0a0e Task timed out after 3.03 seconds
2024-01-03T01:57:05.761Z e4f2922d-aca6-40df-87ec-9f07798cc9c9 Task timed out after 3.05 seconds

どうするべきかと考えて,最終的に cdk deploy コマンドを実行するときに --context functionTimeout="8" のように AWS Lambda 関数のタイムアウト値を任意の値にできるように修正して,プルリクエストも merge してもらえた🙇‍♂️もしソリューションを試しながらタイムアウトのエラーが出たら試してもらえればと〜 \( 'ω')/

github.com

まとめ

Amazon S3 バケットにサイズの大きなファイルをアップロードするソリューション aws-samples/amazon-s3-multipart-upload-transfer-acceleration の紹介でした❗️