Amazon S3 バケットにサイズの大きなファイルをアップロードするソリューション aws-samples/amazon-s3-multipart-upload-transfer-acceleration を試した❗️このソリューションは Amazon S3 の機能「マルチパートアップロード」と「署名付き URL」と「Transfer Acceleration」を組み合わせて実現されている💡ちょうど似たようなアーキテクチャを検討していて,参考になるサンプルを探しているときに発見した \( 'ω')/
ソリューションのサンプル実装は GitHub に公開されている.
またソリューションの解説記事は AWS Compute Blog に公開されている.
アーキテクチャ
ブログ記事に載っているアーキテクチャ図を見るとわかる通り,Amazon API Gateway + AWS Lambda で Amazon S3 のマルチパートアップロードの処理をしている.またパートごとに署名付き URL を生成しているため,アクセスキーなどを共有せずに一時的な権限でファイルをアップロードできる❗️
さらに Transfer Acceleration を活用することによって,エッジロケーション経由で転送効率を高めている.Transfer Acceleration 自体の効果は Amazon S3 Transfer Acceleration Speed Comparison でも確認できる👌
セットアップ
ソリューションには 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
アップロード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
深掘り
ソリューションの設定・実装も確認したので,ポイントをいくつか紹介しようと思う❗️
まず,フロントエンドから呼び出す Amazon API Gateway のパスは計4種類ある./initialize
と /finalize
はマルチパートアップロードの開始と完了の処理になっていて,Amazon S3 のマルチパートアップロードの仕組み(お作法)を理解しておく必要があるため,あわせて以下のドキュメントを読んでおくと良いと思う.
- [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 を使う)
/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 してもらえた🙇♂️もしソリューションを試しながらタイムアウトのエラーが出たら試してもらえればと〜 \( 'ω')/
まとめ
Amazon S3 バケットにサイズの大きなファイルをアップロードするソリューション aws-samples/amazon-s3-multipart-upload-transfer-acceleration の紹介でした❗️