S3のバケット毎の使用量が欲しいんです

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

最近、S3のバケット毎の使用量が欲しくてたまらない菅野です。
aws-cliを駆使して取得する必要がでてきたのでスクリプトを作成しました。

今回の目標

  • 全てのS3バケットのバケット毎の使用量を取得する

では早速スクリプト作成開始です。

バケットの一覧を取得

不採用になったスクリプト

最初はこのようにしてみました。

# バケット一覧を取得
bucket_list=`aws cloudwatch list-metrics --namespace AWS/S3`
# バケット名だけを取り出し
bucket_list_json=`echo "${bucket_list}" | jq -r '.Metrics[] | select(.MetricName == "BucketSizeBytes") | .Dimensions[] | select(.Name == "BucketName") | .Value'`
  • select(.MetricName == "BucketSizeBytes")
    これが無いと同じバケット名が2個ずつ表示されます
  • select(.Name == "BucketName") | .Value
    他にもキーがValueの要素があるのでselectを使って絞り込んでいます

うん、取れた・・・と思ったら実際のバケット数より少ないので調べたところ
デフォルトリージョン以外のバケットが取得できてませんでした。
これでは全バケット名を取得するために全リージョンを配列にして回さないといけないので不採用とします。

採用したスクリプト

以下の方法でバケット一覧を取得しました。
(こんなに簡単なら最初からこっちにしておけばよかったと後悔)

bucket_name_list=(`aws s3 ls | sed -E 's/[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} //g'`)
  • sedコマンドを使って結果の文字列から日時を消しています

バケットのリージョンを取得

バケットのリージョンを取得するには「s3api」を使いました。

for bucket_name in ${bucket_name_list[@]}
do
  # バケットのリージョン情報を取得
  region_info=`aws s3api get-bucket-location --bucket ${bucket_name}`
  # バケットのリージョン名を取得
  region_name=`echo ${region_info} | jq -r '.LocationConstraint'`
 # 表示
 echo ${region_name}
done

簡単ですね。結果は以下となります。

ap-northeast-1
ap-northeast-2
ap-southeast-1
ap-southeast-2
eu-central-1
EU
sa-east-1
null
us-west-1
us-west-2
ap-northeast-1
ap-northeast-1
ap-northeast-1
ap-northeast-1
ap-northeast-1
ap-northeast-1
ap-northeast-1

「EU」とか「null」とか混ざってます・・・
「EU」は以下のページを拝見させていただき、「eu-west-1」に置換すればいいとわかりました。
S3 の Regions(リージョン)一覧
「null」はなんだろうと、該当バケットの情報をコンソールで見ると「US Standard」となっています。
スクリーンショット 2016-03-31 20.50.39−2
こちらも先ほどのページを拝見させていただくと、「us-east-1」に置換すればいいようです。
置換するスクリプトも追加するとこんな感じになりました。

for bucket_name in ${bucket_name_list[@]}
do
  # バケットのリージョン情報を取得
  region_info=`aws s3api get-bucket-location --bucket ${bucket_name}`
  # バケットのリージョン名を取得
  region_name=`echo ${region_info} | jq -r '.LocationConstraint'`

  # us-east-1の場合、nullが返ってくるので上書き
  if [ ${region_name} == null ]
  then
    region_name="us-east-1"
  fi

  # eu-west-1の場合、EUが返ってくるので上書き
  if [ ${region_name} == "EU" ]
  then
    region_name="eu-west-1"
  fi

 # 表示
 echo ${region_name}
done

バケットの使用量を取得

不採用になったスクリプト

最初は以下のスクリプトで試しました。

aws s3 ls s3://${bucket_name} --recursive --human-readable --summarize --region ${region_name}
  • --summarize
    このオプションを付けると合計サイズが取得できます
    スクリーンショット 2016-04-01 12.01.00−2

一見問題なさそうなのですが、以下のようなエラーが発生するバケットがたまにあったので不採用になりました

encode() argument 1 must be string, not None

詳しくは追ってませんが、日本語のファイル名を使ったオブジェクトがあった時に発生するようです。

採用したスクリプト

以下の方法でバケットの使用量を取得しました。

# バケット指定で容量を取得
bucket_size_info=`aws cloudwatch get-metric-statistics --region ${region_name} --namespace AWS/S3 --metric-name BucketSizeBytes --dimensions Name=BucketName,Value=${bucket_name} Name=StorageType,Value=StandardStorage --statistics Sum --start-time ${start_time} --end-time ${end_time} --period ${period}`
# バケットの容量を取得
bucket_size=`echo ${bucket_size_info} | jq -r '.Datapoints[].Sum'`

このスクリプトではCloudWatchから情報を取得しています。

  • --region
    リージョンを指定
  • --dimensions
    バケット名を指定
  • --metric-name
    取得するメトリクスを指定。今回は「BucketSizeBytes」
  • --statistics
    取得する値を指定。今回は「Sum」
  • --start-time、--end-time、--period
    取得する期間とデータの間隔を指定

この方法で取得できるバケットサイズの単位はバイトとなります。
「MB」もしくは「GB」にしないと見栄えが悪いのでそこを修正するスクリプトも追加しておきます。

  # 単位をMBに
  unit="MB"
  bucket_size=`echo "scale=2; ${bucket_size} / 1024 / 1024" | bc`
  # 小数点以下の場合は0を付ける
  bucket_size=`echo ${bucket_size} | sed -E 's/^[.]/0./'`

  # 1024MB以上だったら単位をGBに
  if [ `echo "${bucket_size} >= 1000" | bc` == 1 ]
  then
    unit="GB"
    bucket_size=`echo "scale=2; ${bucket_size} / 1024" | bc`
  fi

完成したスクリプト

スクリプトのソースは以下となります。 ※ Macのターミナルで実行する前提ですので、一部コマンドのオプションを修正する必要があるかもしれません

#!/bin/sh

# デフォルトリージョンを指定
REGION="ap-northeast-1"

# 集計開始日時
start_time="2016-03-31T00:00:00Z"
# 集計終了日時
end_time="2016-03-31T23:59:59Z"
# 集計単位
period=86400 #1日

# アクセスキー等をexport
export AWS_ACCESS_KEY_ID=【アクセスキー】
export AWS_SECRET_ACCESS_KEY=【シークレットアクセスキー】
export AWS_DEFAULT_REGION=${REGION}

echo " ●バケット別のデータ保存量"

# バケット一覧を取得
bucket_name_list=(`aws s3 ls | sed -E 's/[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} //g'`)

# バケット名の配列でループ
for bucket_name in ${bucket_name_list[@]}
do
  # バケットのリージョン情報を取得
  region_info=`aws s3api get-bucket-location --bucket ${bucket_name}`
  # バケットのリージョン名を取得
  region_name=`echo ${region_info} | jq -r '.LocationConstraint'`

  # us-east-1の場合、nullが返ってくるので上書き
  if [ ${region_name} == null ]
  then
    region_name="us-east-1"
  fi

  # eu-west-1の場合、EUが返ってくるので上書き
  if [ ${region_name} == "EU" ]
  then
    region_name="eu-west-1"
  fi

  # バケット指定で容量を取得
  bucket_size_info=`aws cloudwatch get-metric-statistics --region ${region_name} --namespace AWS/S3 --metric-name BucketSizeBytes --dimensions Name=BucketName,Value=${bucket_name} Name=StorageType,Value=StandardStorage --statistics Sum --start-time ${start_time} --end-time ${end_time} --period ${period}`

  # バケットの容量を取得
  bucket_size=`echo ${bucket_size_info} | jq -r '.Datapoints[].Sum'`
  # 単位をMBに
  unit="MB"
  bucket_size=`echo "scale=2; ${bucket_size} / 1024 / 1024" | bc`
  # 小数点以下の場合は0を付ける
  bucket_size=`echo ${bucket_size} | sed -E 's/^[.]/0./'`

  # 1024MB以上だったら単位をGBに
  if [ `echo "${bucket_size} >= 1000" | bc` == 1 ]
  then
    unit="GB"
    bucket_size=`echo "scale=2; ${bucket_size} / 1024" | bc`
  fi

  # バケット名と使用量を表示
  echo "  ${bucket_name}:${bucket_size}${unit}"

done

今回もなんとかできました

結果は以下のようになります。

 ●バケット別のデータ保存量
  cloudtrail-ap-northeast-1-xxxxxxxxxxxx:13.64MB
  cloudtrail-ap-northeast-2-xxxxxxxxxxxx:0.01MB
  cloudtrail-ap-southeast-1-xxxxxxxxxxxx:0.39MB
  cloudtrail-ap-southeast-2-xxxxxxxxxxxx:0.39MB
  cloudtrail-eu-central-1-xxxxxxxxxxxx:0.38MB
  cloudtrail-eu-west-1-xxxxxxxxxxxx:0.38MB
  cloudtrail-sa-east-1-xxxxxxxxxxxx:0.38MB
  cloudtrail-us-east-1-xxxxxxxxxxxx:3.56MB
  cloudtrail-us-west-1-xxxxxxxxxxxx:0.38MB
  cloudtrail-us-west-2-xxxxxxxxxxxx:0.39MB
  backet-01:130.69MB
  backet-02:10.41MB
  backet-03:68.05GB
  backet-04:8.23GB
  backet-05:0MB
  backet-06:2.27GB
  backet-07:836.56MB

今回作成したことで、以下の事がわかりました。

  • バケット名の一覧が欲しければ素直に「aws s3 ls」を使おう
  • 「aws s3 ls」コマンドに「--summarize」オプションを付けるとバケット使用量が取得できる
  • 「aws s3 ls --summarize」コマンドでは日本語のファイル名を使っているとエラーになる場合がある(修正される可能性大)
  • CloudWatchのメトリクスにS3バケットの使用量があった
    【新機能】S3のファイル数とバケットサイズがCloudWatchに追加されました
  • jqはやっぱり便利

今回バケット毎の使用量取得をスクリプト化したので自動で監視させるといった用途にも使えますし、
CloudWatchの他のメトリクスをスクリプトから取得する時の参考にもなると思います。
是非ご利用ください。

参考ページ

これらのページを参考にさせていただきました。
ありがとうございました。
S3 の Regions(リージョン)一覧
【新機能】S3のファイル数とバケットサイズがCloudWatchに追加されました
Amazon CloudWatch の概念
get-metric-statistics
CloudWatchからメトリクスを取得する
配列の全要素をループで取得する
s3api
bashで小数点比較