S3 の利用状況を表示する必要があったので、備忘として残します。
AWS CLI
パターン1
このコマンドだと、S3 に存在するオブジェクトを取得できます。
aws s3 ls s3://{対象バケット} --recursive --human --sum
YYYY-MM-DD HH:MM:SS xxx Bytes {対象ファイル名}
YYYY-MM-DD HH:MM:SS xxx Bytes {対象ファイル名}
Total Objects: xxx
Total Size: xxx.x GiB
パターン 2
このコマンドだと、S3 に存在するオブジェクトのバイト数だけを取得できます。
aws s3 ls s3://{対象バケット} --summarize --recursive | tail -n 1 | cut -d':' -f2
→xxxxxxxxxx(バイト数)
ポイント
- aws s3 ls: AWS CLIのS3コマンドで、S3バケットの内容をリストアップします。
- s3://{対象バケット}: 対象となるS3バケットのURIを指定します。
--summarize: コマンドの最後に合計オブジェクト数とサイズの要約を出力します。
--recursive: バケット内のすべてのサブディレクトリを再帰的に探索します。
tail -n 1 :入力の最後の1行だけを取得します。この場合、要約情報の最後の行(総サイズ)を取得します。
cut -d':' -f2 :再度パイプを使用して、前のコマンドの出力を次のコマンドに渡します。
cut: テキストを特定の区切り文字で分割し、特定のフィールドを抽出するコマンドです。
-d':': コロン(:)を区切り文字として使用することを指定します。
-f2: 2番目のフィールド(コロンの後ろの部分)を抽出します。
パターン 2 の python コード
Lambda で動かす前提でデフォルトのライブラリだけで記述しています。
import subprocess
import csv
from datetime import datetime, timedelta
import os
BUCKET = "bucket-name"
OBJECT_PATH = "object/path"
def run_aws_command():
command = f"aws s3 ls s3://{BUCKET}/{OBJECT_PATH}/ --recursive --human --sum"
result = subprocess.run(command, shell=True, capture_output=True, text=True)
return result.stdout.splitlines(), result.stderr.splitlines()
def parse_output(output_lines):
print("Debug: Output lines:")
for line in output_lines:
print(f" {line}")
total_size = None
for line in reversed(output_lines):
if "Total Size:" in line:
print(f"Debug: Found Total Size line: {line}")
size_str = line.split("Total Size:")[1].strip()
size_value = float(size_str.split()[0])
size_unit = size_str.split()[1]
print(f"Debug: Size value: {size_value}, Size unit: {size_unit}")
if size_unit == "MiB":
total_size = size_value / 1024
elif size_unit == "GiB":
total_size = size_value
elif size_unit == "KiB":
total_size = size_value / (1024 * 1024)
break
return total_size
def generate_monthly_data(execution_date, total_size):
first_day = execution_date.replace(day=1)
next_month = first_day.replace(month=first_day.month % 12 + 1, day=1)
last_day = next_month - timedelta(days=1)
monthly_data = []
current_day = first_day
while current_day <= last_day:
if current_day == execution_date.date():
size = f"{total_size:.2f}GB"
else:
size = "0.00GB"
monthly_data.append((current_day.strftime("%Y-%m-%d"), size))
current_day += timedelta(days=1)
return monthly_data
def save_to_csv(data, filename):
with open(filename, 'w', newline='') as csvfile:
writer = csv.writer(csvfile)
for row in data:
writer.writerow(row)
def load_existing_csv(filename):
if not os.path.exists(filename):
return {}
existing_data = {}
with open(filename, 'r') as csvfile:
reader = csv.reader(csvfile)
for row in reader:
if len(row) == 2:
existing_data[row[0]] = row[1]
return existing_data
def main():
output, error = run_aws_command()
if error:
print("Debug: AWS command error output:")
for line in error:
print(f" {line}")
total_size = parse_output(output)
if total_size is None:
print("Failed to parse total size from the command output.")
return
print(f"Debug: Total size parsed: {total_size:.2f} GiB")
execution_date = datetime.now()
filename = f"client_s3-data-size_{execution_date.strftime('%Y-%m')}.csv"
existing_data = load_existing_csv(filename)
monthly_data = generate_monthly_data(execution_date, total_size)
for date, size in monthly_data:
if date in existing_data and existing_data[date] != "0.00GB":
continue
existing_data[date] = size
sorted_data = sorted(existing_data.items())
save_to_csv(sorted_data, filename)
print(f"CSV file '{filename}' has been created/updated successfully.")
if __name__ == "__main__":
main()
参考
最後に
s3 ls のコマンドは実行にかなり時間がかかります。
ざっくり 2TB のバケットに対して実行するのにデフォルトの Lambda のメモリ 128MB だと 5 分程度かかります。
もし Lambda で定期実行するのであれば、タイムアウトをかなり余裕を持たせるか、メモリを上げる必要があります。
もう一つの手段としては、Cloudwatch でもバケットの容量をメトリクスベースで取得できます。
少し面倒ですが、上記よりは早く値を取得できるので、候補にしてもいいと思われます。
以上、どなたかの参考になれば幸いです。