突然ですが
皆さん。
S3へサイズの大きいオブジェクトをアップロードする時に、こんなイラっとしたことありませんか?
長時間のアップロード中にネットワークエラーで処理失敗。また最初からかよ・・・
そのようなときには「マルチパートアップロード」を利用することで、中断したアップロードをリトライすることができます。
今回は、S3のパフォーマンスを最適化する機能「マルチパートアップロード」についてまとめてみました。
マルチパートアップロードとは
簡単にいうと
サイズの大きいオブジェクトを分割して効率よくアップロードするS3機能のことです。
S3のユーザーガイドによるとオブジェクトサイズが100MB以上の場合に利用を推奨しています。
アップロード完了までの道のり
大まかに下記の流れでマルチパートアップロードできます。
コマンド等の詳細は「マルチパートアップロードを試してみる」に記載してます。
1.オブジェクトを分割する
2.マルチパートアップロードを作成する
3.分割ファイルごとにアップロードする(最終パートまで繰り返す)
4.JSONファイルを作成する
5.マルチパートアップロードを完了する
もし、普通のアップロードをすると、
アップロード中に処理が失敗(中断)したときに送っていたデータが削除されてしまいます。
なので、また一からアップロードし直す必要があります。
しかし、マルチパートアップロードの場合は、
アップロード中に処理が失敗しても、
失敗した部分からアップロード再開することができます。
S3のクォータ
S3は最大5TBのオブジェクトを扱うことができますが、
1度にアップロードできるサイズは最大5GBまでとなっているのです。
そのため、5TBのオブジェクトをアップロードしたい場合は、マルチパートアップロードを利用してコツコツアップロードする必要があります。
マルチパートアップロードの注意点
アップロードが失敗した時、それまで送れていたパート(部分ファイル)にもストレージ料金が発生してしまいます。
マルチパートアップロードの完了リクエストが正常に送信されなかった場合、Amazon S3 はパートを組み立てず、オブジェクトも作成しません。
アップロードされたパートに関連のあるすべてのストレージに対して料金が請求されます。
参照:マルチパートアップロードの中止
このパートはオブジェクトとして未完成のためマネコンから確認することや削除することができません。
そのため、無駄に料金が発生していることに気づかない方もいるのではないでしょうか?
そうならないために、ライフサイクルポリシーを設定することをオススメします。(AWSのベストプラクティス)
マルチパートアップロードを試してみる
マルチパートアップロードのことがだいたい分かってきたところで、仕上げにハンズオンをしてみます。
実行タスク
1.ダミーファイルの作成&分割
2.マルチパートアップロードの開始
3.マルチパートアップロードの終了
下記事前準備は割愛しています。
・アップロード先S3の作成
・AWS CLIインストール
このハンズオンでは、300MBのファイルを100MB毎に分割しアップロードします。
では、ハンズオン開始です
1.ダミーファイルの作成&分割 <Windowsの場合>
1-1.コマンドプロンプトを起動し、下記コマンドを実行します。
fsutil file createnew (ファイル名) (ファイルサイズ(B))
PS C:\Users\g1322\multipartup> fsutil file createnew testfile.dat 314572800
ファイル C:\Users\g1322\multipartup\testfile.dat が作成されました
1-2.7zipでファイル分割を実施します。(Linuxの場合は、splitコマンドを使用します)
フォルダ上に分割ファイルが作成されました。
以上で、アップロードファイルの分割まで完了です。
2.マルチパートアップロードの開始
2-1.下記コマンドを実行し、アップロードIDを取得します。
aws s3api create-multipart-upload --bucket (バケット名) --key (ファイル名)
PS C:\Users\g1322\multipartup> aws s3api create-multipart-upload --bucket ozaki-test2 --key testfile.dat
"ServerSideEncryption": "AES256",
"Bucket": "ozaki-test2",
"Key": "testfile.dat",
"UploadId": "k_7lV1IWRNPKtdmEfQ9oBzRM8OJ3B2IuG_J4OcjF4ShYv0YPQdDCVXEVVvGk9aq37oUZPBkPxK5q_G3Q3TMG2ZtLDxeQAE0eAdrWyFgjJ7S0wGua2y5kFXlZjH_BW.gcmjfXEIjacDD.6zakHzZyQQ--"
}
UploadIdは後続で使用するためコピーしておきます。
2-2.1つ目の分割ファイルをアップロードします。
aws s3api upload-part --bucket (バケット名) --key (ファイル名) --part-number 1 --body (分割ファイル名) --upload-id (アップロードID)
PS C:\Users\g1322\multipartup> aws s3api upload-part --bucket ozaki-test2 --key testfile.dat --part-number 1 --body testfile.dat.001 --upload-id k_7lV1IWRNPKtdmEfQ9oBzRM8OJ3B2IuG_J4OcjF4ShYv0YPQdDCVXEVVvGk9aq37oUZPBkPxK5q_G3Q3TMG2ZtLDxeQAE0eAdrWyFgjJ7S0wGua2y5kFXlZjH_BW.gcmjfXEIjacDD.6zakHzZyQQ--
{
"ServerSideEncryption": "AES256",
"ETag": "\"2f282b84e7e608d5852449ed940bfc51\""
}
結果のETagをコピーしておきます。
-【メモ】------------------------------------
この時点ではまだオブジェクトの一部しかアップロードできていないため、マネコン上ではオブジェクト0件となっています。
下記コマンドを実行すると、不完全なデータとしてS3上に存在していることが確認できます。
aws s3api list-multipart-uploads --bucket (バケット名)
PS C:\Users\g1322\multipartup> aws s3api list-multipart-uploads --bucket ozaki-test2
{
"Uploads": [
{
"UploadId": "k_7lV1IWRNPKtdmEfQ9oBzRM8OJ3B2IuG_J4OcjF4ShYv0YPQdDCVXEVVvGk9aq37oUZPBkPxK5q_G3Q3TMG2ZtLDxeQAE0eAdrWyFgjJ7S0wGua2y5kFXlZjH_BW.gcmjfXEIjacDD.6zakHzZyQQ--",
"Key": "testfile.dat",
"Initiated": "2022-05-06T09:43:54+00:00",
"StorageClass": "STANDARD",
"Owner": {
"DisplayName": "*********************",
"ID": "*********************"
},
"Initiator": {
"ID": "arn:aws:iam::*********************",
"DisplayName": "*********************"
}
}
]
}
-----------------------------------------
2-3.残りの部分ファイルに対して手順2-2を繰り返します。
アップロードする各部分ごとにpart-number
を増やしてください。
2-4.対象の部分ファイルがすべてアップロードできていることを確認します。
aws s3api list-parts --bucket (バケット名) --key (ファイル名) --upload-id (アップロードID)
PS C:\Users\g1322\multipartup> aws s3api list-parts --bucket ozaki-test2 --key testfile.dat --upload-id k_7lV1IWRNPKtdmEfQ9oBzRM8OJ3B2IuG_J4OcjF4ShYv0YPQdDCVXEVVvGk9aq37oUZPBkPxK5q_G3Q3TMG2ZtLDxeQAE0eAdrWyFgjJ7S0wGua2y5kFXlZjH_BW.gcmjfXEIjacDD.6zakHzZyQQ--
{
"Parts": [
{
"PartNumber": 1,
"LastModified": "2022-05-06T09:52:29+00:00",
"ETag": "\"2f282b84e7e608d5852449ed940bfc51\"",
"Size": 104857600
},
{
"PartNumber": 2,
"LastModified": "2022-05-06T16:40:47+00:00",
"ETag": "\"2f282b84e7e608d5852449ed940bfc51\"",
"Size": 104857600
},
{
"PartNumber": 3,
"LastModified": "2022-05-06T16:41:48+00:00",
"ETag": "\"2f282b84e7e608d5852449ed940bfc51\"",
"Size": 104857600
}
],
"ChecksumAlgorithm": null,
"Initiator": {
"ID": "arn:aws:iam::*********************",
"DisplayName": "*********************"
},
"Owner": {
"DisplayName": "*********************",
"ID": "*********************"
},
"StorageClass": "STANDARD"
}
2-5.接着剤的な役割のJSONファイルを作成します。
下記のようにJSONファイルにアップロードした各ファイル部分の ETag 値をコンパイルします。
{
"Parts": [{
"ETag": "\"2f282b84e7e608d5852449ed940bfc51\"",
"PartNumber":1
},
{
"ETag": "\"2f282b84e7e608d5852449ed940bfc51\"",
"PartNumber":2
},
{
"ETag": "\"2f282b84e7e608d5852449ed940bfc51\"",
"PartNumber":3
}]
}
ファイル名をfileparts.json とし保存します。
3.マルチパートアップロードの終了
3-1.下記コマンドを実行し、マルチパートアップロードを終了させます。
aws s3api complete-multipart-upload --multipart-upload file://fileparts.json --bucket (バケット名) --key (ファイル名) --upload-id (アップロードID)
PS C:\Users\g1322\multipartup> aws s3api complete-multipart-upload --multipart-upload file://fileparts.json --bucket ozaki-test2 --key testfile.dat --upload-id k_7lV1IWRNPKtdmEfQ9oBzRM8OJ3B2IuG_J4OcjF4ShYv0YPQdDCVXEVVvGk9aq37oUZPBkPxK5q_G3Q3TMG2ZtLDxeQAE0eAdrWyFgjJ7S0wGua2y5kFXlZjH_BW.gcmjfXEIjacDD.6zakHzZyQQ--
{
"ServerSideEncryption": "AES256",
"Location": "https://ozaki-test2.s3.ap-northeast-1.amazonaws.com/testfile.dat",
"Bucket": "ozaki-test2",
"Key": "testfile.dat",
"ETag": "\"a67c1d71179e7552ad55fe9c0b755e76-3\""
}
2-5で作成したJSONファイルを読み込むことで、分割ファイルが合体しオブジェクトとして正式にS3バケット上に認識されます。
-【メモ】------------------------------------
アップロードがすべて完了すると、不完全なデータが0件となります。
PS C:\Users\g1322\multipartup> aws s3api list-multipart-uploads --bucket ozaki-test2
PS C:\Users\g1322\multipartup>
-----------------------------------------
以上で、マルチパートアップロードのハンズオン終了となります。
まとめ
マルチパートアップロードには
「アップロード処理が中断しても再開できる」という最大のメリットがある一方、
「分割数が多くなるにつれ繰り返し手順が多くなる」というデメリットがあります。(個人の見解です)
この一連の対応手順を自動化できたらいいなーと思っています。
開発得意な方、「マルチパートアップロードを自動化してみた」記事期待してます!