AWSの運用・導入、システム開発・アプリケーション開発のことならクロスパワーのクラウド

電話番号 電話番号 お問い合わせ

受付時間 平日10:00~18:00

TOP

サービスはこちら

クロスパワーブログ

S3の使用状況を拡張子別に取得してみた~Lambda編~

こんにちは。katoです。

 

今回はS3の使用状況を拡張子別に取得するLambda関数のご紹介を致します。

概要

 

今回はS3バケットにおける拡張子別の使用量を取得するLambda関数をご紹介致します。

 

分析ツールのように使用したり、煩雑になりがちなS3バケットを整理したりするのに役立つかと思います。

 

なお、今回ご紹介する拡張子別のS3使用状況の取得というのは、もともと、Step Functionsでパラレル処理を実行したいと思って、処理に時間の掛かりそうなタスクを考えていたら思いついたものとなります。

 

そのため、S3をがっつり利用しているような方だと、Lambdaがタイムアウトする可能性が御座いますのでご注意ください。

 

今回の内容をStep Functionsでパラレル化したものを次回以降に記載させていただきたいと思います。

手順

 

それでは実際に設定を行っていきましょう。

 

とはいっても今回は、Lambda関数を作成するのみとなります。

 

import json
import boto3
import re

s3 = boto3.client('s3')
sns = boto3.client('sns')

def lambda_handler(event, context):
    buckets = []
    listbucket = s3.list_buckets()
    for i in range(len(listbucket['Buckets'])):
        buckets.append(listbucket['Buckets'][i]['Name'])
    
    message = ""
    for j in range(len(buckets)):
        size = 0
        count = 0
        extentions = []
        counts = []
        sizes = []
        listobj = s3.list_objects_v2(
            Bucket=buckets[j]
        )
        if "Contents" in listobj:
            for num in range(len(listobj['Contents'])):
                key = listobj['Contents'][num]['Key']
                prefix = key.rsplit("/", 1)
                if len(prefix) != 1:
                    keys = prefix[1].rsplit(".", 1)
                    if len(keys) != 1:
                        extention = keys[1]
                    else:
                        extention = "other"
                else:
                    keys = prefix[0].rsplit(".", 1)
                    if len(keys) != 1:
                        extention = keys[1]
                    else:
                        extention ="other"
                if extention in extentions:
                    indexnum = extentions.index(extention)
                    sizes[indexnum] = sizes[indexnum] + listobj['Contents'][num]['Size']
                    counts[indexnum] += 1
                else:
                    extentions.append(extention)
                    sizes.append(listobj['Contents'][num]['Size'])
                    counts.append(1)
                size = size + listobj['Contents'][num]['Size']
                count += 1
            objlen = len(listobj['Contents'])
            
            while (objlen % 1000 == 0):
                startlen = objlen -1
                lastkey = listobj['Contents'][startlen]['Key']
                listobj = s3.list_objects_v2(
                    Bucket=buckets[j],
                    StartAfter=lastkey
                )
                for num in range(len(listobj['Contents'])):
                    key = listobj['Contents'][num]['Key']
                    prefix = key.rsplit("/", 1)
                    if len(prefix) != 1:
                        keys = prefix[1].rsplit(".", 1)
                        if len(keys) != 1:
                            extention = keys[1]
                        else:
                            extention = "other"
                    else:
                        keys = prefix[0].rsplit(".", 1)
                        if len(keys) != 1:
                            extention = keys[1]
                        else:
                            extention ="other"
                    if extention in extentions:
                        indexnum = extentions.index(extention)
                        sizes[indexnum] = sizes[indexnum] + listobj['Contents'][num]['Size']
                        counts[indexnum] += 1
                    else:
                        extentions.append(extention)
                        sizes.append(listobj['Contents'][num]['Size'])
                        counts.append(1)
                    size = size + listobj['Contents'][num]['Size']
                    count += 1
                objlen = len(listobj['Contents'])
            message = message + "\n\nBucketName: " + buckets[j]
            message = message + "\nTotalSize: " + str(size) + " byte"
            message = message + "\nObjectCount: " + str(count)
            rankcount = 0
            message = message + "\n[Details]"
            while (len(extentions) != 0 and rankcount < 5 ):
                maxindex = sizes.index(max(sizes))
                message = message + "\n" + extentions[maxindex] + " object : " + str(sizes[maxindex]) + " byte"
                message = message + "\n" + extentions[maxindex] + " object count: " + str(counts[maxindex])
                del extentions[maxindex]
                del sizes[maxindex]
                del counts[maxindex]
                rankcount += 1
        else:
            message = "\n\n" + buckets[j]
            message = message + "\n0"
    
    request = {
        'TopicArn': "arn:aws:sns:ap-northeast-1:123456789000:xxxxxxxxxx",
        'Message': message,
        'Subject': "[MESSAGE]S3 Usage"
    }
    sns.publish(**request)

 

コードの内容といたしましては、オブジェクトのキーとサイズを取ってごちゃごちゃしているだけとなりますが、簡単な説明と注意点を以下に記載させていただきます。

 

・list_objects  APIに関して

オブジェクトの一覧取ってきてfor文回せば行けるだろーって感じでLambda関数を作成しはじめましたが、どうもTotal Sizeがコンソールの表示と合わない。

おかしいなーと思ってboto3のページを見ていたら説明にしっかり書いてありました。

 

最大1000個。

 

見落としてました。

StartAfterのオプションで始点を指定してあげるようにしたら普通にうまくいきました。

 

少し処理は面倒になりますが、オブジェクト数が1000を超える場合には、どこからオブジェクトの取得を開始するのか指定してあげる必要があります。

 

今回はlist_objectsの返り値が1000個である限り、処理を繰り返すような条件にしております。

 

なお、今回はlist_objects_v2 APIを利用していますが、古いほうのlist_objects APIではStartAfterの指定ができなさそうでしたのでご注意ください。

 

・出力の制限に関して

今回のLambda関数では出力を、使用量が多い拡張子、5個に制限しております。

 

最初はすべての拡張子を出力するように設定していたのですが、思っていたよりも拡張子が多く、出力メッセージがかなり見づらくなってしまったので、このような制限を設けております。

 

情報自体はすべての拡張子で取得してありますので、最後の方に記載してあるwhile文の条件式を変えれば、データの利用は可能です。

 

DynamoDBやAmazon ES等を使うのであれば、このような制限は不要かと思われます。

 

・otherに関して

拡張子の無いオブジェクトになります。

 

・対象リージョン

全リージョンのバケットが対象となります。

動作確認

 

それでは実際にLambda関数を実行して動作を確認してみましょう。

 

実行が完了するのには結構時間がかかりますが、処理が完了するとSNS経由で以下のようなメールが届きます。

 

 

バイト表示なので少し見づらいですが、拡張子ごとにしっかりデータが取れました。

まとめ

 

今回は拡張子別のS3利用量を取得するLamdba関数をご紹介いたしました。

 

使用状況の分析やバケットの整理などにご利用いただけるかと思います。

 

Step Functionsを利用したパターンでも記事を作成する予定となっておりますので、宜しければそちらもご覧ください。

 

 

このブログの著者

最新記事

  • S3の使用状況を拡張子別に取得してみた~Lambda編~

    こんにちは。katoです。   今回はS3の使用状況を拡張子別に取得するLambda関数のご紹介を致します。 概要   今回はS3バケットにおける拡張子別の使用量を取得するLambda関数をご紹介致し …

  • DynamoDBのTransactionを使った取得処理

    DynamoDBのトランザクションを使った取得処理を見ていきます。   使うテーブルは新しく用意しました。 ・テーブル名「XpTable」 ・キー名「XpKey」   実行環境 ・Lambda(nod …

  • Step Functions~Call Amazon SNS and DynamoDB APIs~

    こんにちは。katoです。   今回は先日のre:Invent 2018にて発表された、Step FunctionsのSNSとDynamoDBとの連携の方法についてご紹介させていただきます。   概要 …

  • DynamoDBのTransactionを使った更新処理

    DynamoDBのトランザクション関係のメソッドは取得系と更新系の2メソッドがあります。 今回は更新系のtransactWriteItemsメソッドについてお話していきます。 取得系のTransactGetItemsは別 …

  • DynamoDBでTransactionの使い方(異常系)

    DynamoDBに実装されたトランザクション機能ですが、 前回は主に正常系の動作、及び使い方を紹介しました。   DynamoDBでTransactionの使い方(正常系)   今回は異常系の挙動につ …

Copyright 2016 Cross Power Corporation. All Rights Reserved.