AWS S3へ巨大なファイルをアップロードする際、より高速に転送するには「マルチパートアップロード」を利用することが推奨されています。また一定サイズ以上のファイルはSDKやREST APIからはそもそもPUTすることができません。
マルチパートアップロードというと一見難しそうに聞こえるかもしれませんが理屈も使い方も非常にシンプル。対応したGUIのアプリを使えば裏側で自動的に行なってくれるためそもそも意識する必要すらありません。
今回はAWS CLIとWindowsやmacOS上のGUIのクライアントを利用した方法を取り上げます。
マルチパートアップロードとは
通常のアップロードは以下のようにファイルをそのままサーバに送りつけるだけです。
マルチパートアップロードではファイルを分割(みじん切りに)し、それぞれのパーツを並行してアップロードします。最終的にすべてのパーツがサーバ上に揃った段階で合体しもとのファイルに復元するという仕組みです。
これにより回線が許す限り並行してファイルの転送が行えるため高速であるだけでなく、一部が転送に失敗しても部分的なやり直しで済むため非常に効率的です。
S3のそもそもの話
オブジェクトのサイズ上限
AWSのドキュメントによると、執筆時点ではアップロード方法によって次のような上限が設定されているようです。
少なくとも1つのファイルが5Gを超え、マネジメントコンソール以外から巨大なデータを上げる場合はマルチパートアップロードを利用することになります。
Q: Amazon S3 にはどれだけのデータを保存できますか? 格納可能なデータの総量とオブジェクトの数には制限はありません。個別の Amazon S3 オブジェクトのサイズは、最低 0 バイトから最大 5 テラバイトまでさまざまです。1 つの PUT にアップロード可能なオブジェクトの最大サイズは 5 GB です。100 MB 以上のオブジェクトの場合は、マルチパートアップロード機能を使うことをお考えください。
※よくある質問 - Amazon S3より
上記の通りAWS的には100M以上の場合に推奨しているようですね。なおマルチパートAPIはSDK、REST API、CLIからそれぞれ利用することが可能です。
マルチパートアップロードの上限値
1つのオブジェクトの最大サイズは先に述べた通りですが、分割可能な数や、分割した一つ一つのパートの最大サイズも以下のように定められています。
項目 | 仕様 |
---|---|
最大サイズ | 5 TB |
最大分割数 | 10,000 |
分割後のサイズ | 5 MB〜5 GB。最後のパートはサイズ制限なし |
料金の罠
ドキュメントを読み進めるとしれっと恐ろしいことが書かれています。
マルチパートアップロードの完了リクエストが正常に送信されなかった場合、Amazon S3 はパートを組み立てず、オブジェクトを作成しません。したがって、パートは Amazon S3 に残るため、Amazon S3 に保存されたパートに対して料金が発生します。
※マルチパートアップロードの概要 - Amazon Simple Storage Service より
つまり何らかの事情で、ファイルのアップロードが中途半端な状態で終了してしまった場合、中途半端なデータが裏側で残り課金され続けるということのようです。なにそれ怖いw
不完全なデータを確認&削除
Webブラウザからマネジメントコンソールを覗いても表示されないため、CLIなどから直接S3のAPIを叩きます。
$ aws s3api list-multipart-uploads --bucket (バケット名)
残骸が表示された場合にはCLIから以下のコマンドで削除することができます。
$ aws s3api abort-multipart-upload --bucket (バケット名) --key (ファイル名) --upload-id (アップロードID)
ライフサイクルを設定する
不完全なデータをCLIから毎回削除するのは流石に現実的ではないので、ライフサイクルの設定を行い常にお掃除がされるようにします。というわけでアップロードが途中で失敗しても一定時間が経過したら自動的に不完全なファイル(オブジェクト)が削除されるようライフサイクルを設定していきます。
マネジメントコンソールからS3のバケットを開き「管理」メニュー内にある「ライフサイクルルールを作成する」ボタンをクリック。
ページ下の方にある「期限切れの削除マーカーまたは不完全なマルチパートアップロードを削除する」にチェックすると新しい入力項目が登場しますので「不完全なマルチパートアップロードを削除」にチェックし、適当な日数を入力すれば完了です。
CLIからライフサイクルの設定を行う場合は以下のページを参照ください。 docs.aws.amazon.com
アップロード方法
AWS CLI
cpを利用するだけで、巨大なファイルを指定した場合は自動的にマルチパートアップロードが裏側で行われます。
$ aws s3 cp filename.mp4 s3://bucketname/
設定値はコマンドで変更可能です。
# スレッドの個数 $ aws configure set default.s3.max_concurrent_requests 10 # マルチパートアップロードを採用するファイルサイズの閾値 $ aws configure set default.s3.multipart_threshold 64MB # チャンクサイズ $ aws configure set default.s3.multipart_chunksize 16MB # 最大帯域幅 $ aws configure set default.s3.max_bandwidth 50MB/s
アプリ
Cyberduck
macOSでお馴染みのCyberduckがマルチパートアップロードに対応しています。Windows版もあり基本無料で利用可能です。MacAppStoreでも入手できますがこちらは有料。開発を支援したい方はぜひ協力してあげてください。 cyberduck.io
S3 Browser
Windows用のS3クライアントであるS3 Browserがマルチパートアップロードに対応しています。 s3browser.com
5Mを超えるファイルは自動的にマルチパートアップロードになるようですが、無料版ではスレッド数が2つまでに制限されます。より高速に転送したい、またはお仕事で利用する場合にはPro版を購入する必要があります(執筆時点で1ライセンス$29.99)
WinSCP
Windowsでのファイル転送ソフトとして有名なWinSCPでS3へアップロードすることは可能ですが、マルチパートアップロードには非対応のようです。 winscp.net
フォーラムを覗いてみると2020年6月時点の書き込みで、多くの声が集まれば実装するかも…との作者の方の発言があります。
(質問) Could you guys please improve the S3 interface to use multipart uploads?
※WinSCPフォーラムより
(回答) Thanks for your suggestion. We will see, if more people ask for this.
本当にスピードアップするの?
計測方法
AWS CLIを利用して実験します。今回利用するバージョンは以下になります。
$ aws --version aws-cli/2.1.7 Python/3.9.0 Darwin/19.6.0 source/x86_64 prompt/off
アップロードするファイルはddコマンドで100Mと1Gbyteのファイルを用意しました。
$ dd bs=100m count=1 if=/dev/urandom of=file1 $ dd bs=1g count=1 if=/dev/urandom of=file2
時間の計測にはtimeコマンドを利用します。
$ time aws s3 cp file1 s3://example.com/
通常のアップロードへの切り替えは単純にmultipart_thresholdのサイズを大きく設定した状態で行いました。
$ aws configure set default.s3.multipart_threshold 10GB
計測中はネットワークを利用しないようルンバが掃除してくれるのを眺めながら待ちます。あとテトリスミニに最近ハマってるのですがこちらも完全オフラインなのでおすすめw
計測結果
100M byte
単位は秒です。なるほど、これは誤差だw
回数 | 通常 | マルチパート |
---|---|---|
1 | 18.543 | 19.490 |
2 | 12.572 | 21.362 |
3 | 21.744 | 17.342 |
4 | 23.294 | 16.962 |
5 | 16.589 | 15.538 |
平均 | 18.5484 | 18.1388 |
1G Byte
こちらはマルチパートアップロードがおよそ25%ほど高速にアップロード出来ていますね。やはりファイルサイズが大きくならないと明確な差は生まれないようです。
回数 | 通常 | マルチパート |
---|---|---|
1 | 140.09 | 106.44 |
2 | 147.30 | 105.10 |
3 | 171.38 | 111.97 |
4 | 135.47 | 115.63 |
5 | 144.93 | 107.41 |
平均 | 147.834 | 109.31 |
このあとmax_concurrent_requestsなども調整してみましたが今回は明確な差は見られませんでした。