概要
S3バケットに保管されているオブジェクト(STANDARD・GLACIER・GLACIER_IR・DEEP_ARCHIVE)を丸ごとダウンロードするスクリプトです。
S3に保管しているオブジェクトがライフサイクルルールでGLACIERストレージクラスにアーカイブされてしまったので書きました。
対応ストレージクラスは以下のとおりです。
| ストレージクラス | 内部名 | 復元の要否 | 復元時間の目安 |
|---|---|---|---|
| S3 Standard など |
STANDARD 他 |
不要 | 即時 |
| S3 Glacier Instant Retrieval | GLACIER_IR |
不要 | 即時 |
| S3 Glacier Flexible Retrieval | GLACIER |
必要 | 3〜5時間(Standard tier) |
| S3 Glacier Deep Archive | DEEP_ARCHIVE |
必要 | 最大48時間 |
使い方
サンプルのバケット名と保存ディレクトリ名を書き換え、スクリプトを実行します。
$ python restore_n_download.py
GLACIER(Flexible Retrieval)からの復元には通常3〜5時間、DEEP_ARCHIVEからの復元には最大48時間かかります。スクリプトを一回実行して復元リクエストを送信し、復元完了後にもう一回実行すればダウンロードできます。ダウンロード済みのファイルはスキップされ、復元中のオブジェクトも適切にハンドリングされます。
import boto3
import os
from botocore.exceptions import ClientError
# 設定
CONFIG = {
'bucket_name': 'awsexamplebucket',
'filter_prefix': '',
'base_dir': '/Users/name/awsexamplebucket',
'restore_days': 1,
'restore_tier': 'Standard' # 'Expedited'(1-5分), 'Standard'(3-5時間), 'Bulk'(5-12時間)
}
# 復元が必要なストレージクラス
GLACIER_CLASSES = {'GLACIER', 'DEEP_ARCHIVE'}
def download_object(s3_client, bucket, key, base_dir):
save_path = os.path.join(base_dir, key)
if os.path.exists(save_path):
print(f'Already exists: {key}')
return
os.makedirs(os.path.dirname(save_path), exist_ok=True)
try:
s3_client.download_file(bucket, key, save_path)
print(f'Downloaded: {key}')
except ClientError as e:
print(f'Error downloading {key}: {e}')
def main():
s3 = boto3.client('s3')
paginator = s3.get_paginator('list_objects_v2')
for page in paginator.paginate(Bucket=CONFIG['bucket_name'], Prefix=CONFIG['filter_prefix']):
for obj in page.get('Contents', []):
key = obj['Key']
try:
obj_info = s3.head_object(Bucket=CONFIG['bucket_name'], Key=key)
storage_class = obj_info.get('StorageClass', 'STANDARD')
if storage_class == 'GLACIER_IR':
# Glacier Instant Retrieval: 復元不要、即ダウンロード可
download_object(s3, CONFIG['bucket_name'], key, CONFIG['base_dir'])
elif storage_class in GLACIER_CLASSES:
restore_status = obj_info.get('Restore', '')
wait_hours = '3〜5時間' if storage_class == 'GLACIER' else '最大48時間'
if 'ongoing-request="true"' in restore_status:
print(f'Restoration in-progress: {key}')
elif 'ongoing-request="false"' in restore_status:
download_object(s3, CONFIG['bucket_name'], key, CONFIG['base_dir'])
else:
print(f'Submitting restoration request ({wait_hours}): {key}')
s3.restore_object(
Bucket=CONFIG['bucket_name'],
Key=key,
RestoreRequest={
'Days': CONFIG['restore_days'],
'GlacierJobParameters': {'Tier': CONFIG['restore_tier']}
}
)
else:
# STANDARD, STANDARD_IA, ONEZONE_IA など
download_object(s3, CONFIG['bucket_name'], key, CONFIG['base_dir'])
except ClientError as e:
print(f'Error processing {key}: {e}')
if __name__ == '__main__':
main()
注意事項
DEEP_ARCHIVEでBulk tierを使う場合
DEEP_ARCHIVEの復元リクエストには GlacierJobParameters(Tier指定)を含めることができますが、Bulk tier を指定した場合はパラメータを省略しなければエラーになります。DEEP_ARCHIVEを対象にする場合はrestore_tierを'Standard'のままにしておくか、ストレージクラスに応じてTierを切り替えるロジックを別途追加してください。
# DEEP_ARCHIVE + Bulk の場合は GlacierJobParameters を渡さない
RestoreRequest={'Days': CONFIG['restore_days']}