Amazon Simple Storage Service

S3オブジェクトが多すぎて手作業で操作できなぁぁぁぁぁい

はじめに

こんにちは、渡辺omkです。
すこし前にS3のオブジェクトに変更をかけようと思ったのですがあまりに量が多くて手作業で操作できない状態になってしまいました。
そんなとき、S3バッチオペレーションでオブジェクトを一括で操作出来るので必要な対応をまとめていきます。

S3バッチオペレーションとは

S3上のオブジェクトを一括で操作するのに利用出来る機能です。
標準でオブジェクトのコピーやタグの削除・置き換え、オブジェクトロック等が提供されるので自分で実装が不要な点も魅力的ですが何と言ってもLambdaで独自の処理が利用できる点で非常に便利です。

対象のオブジェクトもS3インベントリのレポートを流用できたり、独自にCSVが利用できたりと汎用性が高いです。

やってみた

参考
https://docs.aws.amazon.com/AmazonS3/latest/userguide/batch-ops-invoke-lambda.html

Lambda関数の作成

Lambdaを利用する場合の注意点として、
「呼び出しスキーマのバージョン」がバッチジョブとLambda側で想定するものとで合致している必要があります。
ファイルを参照する際にバージョンが異なると上手く参照できなかったりします。

スキーマバージョン2.0では以下のフォーマットでイベントに渡されます。

{'invocationId': 'AAAAAAAAAASug0ku+nagai1d', 'job': {'id': 'hoge1-huga2-piyo3-foo4-bar555', 'userArguments': None}, 'tasks': [{'taskId': 'AAAAAAAAAAT0tem0+nAgai1D==', 's3Bucket': 's3-batch-sample-bucket', 's3Key': 'path/file.yml', 's3VersionId': None}], 'invocationSchemaVersion': '2.0'}

出来上がったのがこちら、対象オブジェクトのバージョンを1つ戻すLambda関数です。

import json
import boto3
from botocore.exceptions import ClientError
import time
import sys

def rollback_to_previous_version(bucket_name, object_key):

    s3 = boto3.client('s3')

    # ファイルのバージョン一覧を取得
    versions = s3.list_object_versions(Bucket=bucket_name, Prefix=object_key)['Versions']

    # バージョンが2つ以上あることを確認
    if len(versions) < 2:
        print(f"エラー: {object_key} には十分なバージョンがありません。")
        sys.exit(1)

    # 最新のバージョンと1つ前のバージョンを取得
    current_version = versions[0]
    previous_version = versions[1]

    # 現在のバージョンを削除
    s3.delete_object(
        Bucket=bucket_name,
        Key=object_key,
        VersionId=current_version['VersionId']
    )

    print(f"{file_key} を以下のバージョンにロールバックしました:")
    print(f"バージョンID: {previous_version['VersionId']}")
    print(f"最終更新日時: {previous_version['LastModified']}")

def lambda_handler(event, context):
    # TODO implement
    print(event)
    s3_bucket_name = event['tasks'][0]['s3Bucket']
    s3_object_key = event['tasks'][0]['s3Key']

    try:
        rollback_to_previous_version(s3_bucket_name, s3_object_key)
        return {
            'statusCode': 200,
            'body': json.dumps('success')
        }
    except ClientError as e:
        print(f"エラーが発生しました: {e}")
        return {
            'statusCode': 500,
            'body': json.dumps('error')
        }

何があったかは訊かないでください……

バッチオペレーションの実行ロール作成

IAMロールにバッチを実行するにあたって必要な権限を割り当てます。
今回はLambdaを利用するのでマニフェストファイルの参照とLambdaの起動、完了レポートのアップロードの権限を設定しました。

信頼ポリシーは以下になります。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "batchoperations.s3.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}

対象オブジェクトのリストアップ

バッチオペレーションを実行するにはバッチの対象とするオブジェクトをマニフェストに記載してS3にアップロードする必要があります。

マニフェストのフォーマットとして現在「S3インベントリレポート」「CSV」「S3レプリケーション」の3種類が設定可能です。

特定領域をごっそり操作するような場合はインベントリでリストする方法が簡単です。
対象バケットにインベントリを設定して出力されたレポートをバッチオペレーションから参照するだけです。

特殊な条件に沿ってリストアップする場合にはAWS CLIでリストして条件にかけてCSVにパースする方法がおすすめです。
その場合は以下のフォーマットにCSVを記載します。

"バケット名", "オブジェクトパス"

もっと複雑ならLambda側で条件をかけてもいいかもですがオブジェクト数によってはLambdaの起動回数がすごいことになるので手前で減らせるようにしておきたさはあります。

作成したCSVをS3に設置しておきます。

バッチジョブの作成

バッチオペレーションのコンパネから画面に従って設定していきます。
ジョブを作成して「実行のための確認待ち」状態になればジョブを実行できます。

これが実行前です。

実行後はこうなりました。

ちゃんと最新バージョンが削除されて前のバージョンに戻りました。
オブジェクト量にもよりますがfor文を回すよりよっぽど速いですね。

おわりに

S3オブジェクトを一括で操作するときにはバッチオペレーションがおすすめです。
個人的にも結構使う機会が多くて便利に使えているので助かっています。

最後までお付き合いいただきありがとうございました。

返信を残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA