🕌

AWS GlueのJobごとのコストを調べるスクリプト

2025/02/14に公開

コスト

コスト = 使用 DPU 数 × ジョブ実行時間(時間) × (1 DPU 時間あたりの単価)

スクリプト

aws sso loginした状態で、以下実行。

#!/bin/bash
# usage: ./glue.sh YYYY-MM
# 例: ./glue.sh 2025-01

if [ "$#" -ne 1 ]; then
    echo "Usage: $0 YYYY-MM"
    exit 1
fi

TARGET_MONTH="$1"
# 1 DPU 時間あたりの単価 (USD)
DPU_HOUR_RATE=0.44

echo "対象月: ${TARGET_MONTH}"
echo "1 DPU 時間あたりの単価: USD ${DPU_HOUR_RATE}"
echo "-----------------------------------------"

# サマリー用の配列 (bash 3.x でも利用可能な通常の配列)
job_names_summary=()
job_costs_summary=()

# 全ジョブ名を取得
job_names=$(aws glue list-jobs --query 'JobNames' --output text)
if [ -z "$job_names" ]; then
    echo "Glue ジョブが見つかりません。"
    exit 1
fi

for job in $job_names; do
    total_cost=0
    echo "ジョブ: ${job}"
    next_token=""

    while :; do
        if [ -z "$next_token" ]; then
            result=$(aws glue get-job-runs --job-name "$job" --output json)
        else
            result=$(aws glue get-job-runs --job-name "$job" --next-token "$next_token" --output json)
        fi

        # JSON出力が有効かチェック
        if ! echo "$result" | jq . > /dev/null 2>&1; then
            echo "Error: job '$job' の実行履歴で無効な JSON が返されました。"
            break
        fi

        # JobRuns が存在しない場合はループ終了
        runs=$(echo "$result" | jq -c '.JobRuns[]?')
        if [ -z "$runs" ]; then
            break
        fi

        for run in $runs; do
            started=$(echo "$run" | jq -r '.StartedOn')
            completed=$(echo "$run" | jq -r '.CompletedOn')

            # 開始時刻・終了時刻が空または "null" ならスキップ
            if [ -z "$started" ] || [ "$started" = "null" ] || [ -z "$completed" ] || [ "$completed" = "null" ]; then
                echo "  Skipping run: 開始または終了時刻が取得できません。"
                continue
            fi

            # タイムスタンプの変換処理(fractional seconds やタイムゾーンの形式に対応)
            if echo "$started" | grep -q "Z$"; then
                clean_started="$started"
                start_format="%Y-%m-%dT%H:%M:%SZ"
            else
                clean_started=$(echo "$started" | sed -E 's/\.[0-9]+//; s/([+-][0-9]{2}):([0-9]{2})/\1\2/')
                start_format="%Y-%m-%dT%H:%M:%S%z"
            fi

            if echo "$completed" | grep -q "Z$"; then
                clean_completed="$completed"
                comp_format="%Y-%m-%dT%H:%M:%SZ"
            else
                clean_completed=$(echo "$completed" | sed -E 's/\.[0-9]+//; s/([+-][0-9]{2}):([0-9]{2})/\1\2/')
                comp_format="%Y-%m-%dT%H:%M:%S%z"
            fi

            # 対象月のチェック: StartedOn の YYYY-MM 部分を抽出
            run_month=$(date -j -f "$start_format" "$clean_started" "+%Y-%m" 2>/dev/null)
            if [ -z "$run_month" ]; then
                echo "  Skipping run: 開始時刻のパースに失敗: $clean_started"
                continue
            fi
            if [ "$run_month" != "$TARGET_MONTH" ]; then
                continue
            fi

            # エポック秒へ変換
            start_epoch=$(date -j -f "$start_format" "$clean_started" "+%s" 2>/dev/null)
            end_epoch=$(date -j -f "$comp_format" "$clean_completed" "+%s" 2>/dev/null)
            if [ -z "$start_epoch" ] || [ -z "$end_epoch" ]; then
                echo "  Skipping run: タイムスタンプ変換に失敗。"
                continue
            fi
            runtime_seconds=$(( end_epoch - start_epoch ))
            runtime_hours=$(echo "scale=4; $runtime_seconds / 3600" | bc)

            # NumberOfWorkers を取得。無い場合はジョブ定義から取得。なければ 1 とする。
            num_workers=$(echo "$run" | jq -r '.NumberOfWorkers')
            if [ -z "$num_workers" ] || [ "$num_workers" = "null" ]; then
                job_def=$(aws glue get-job --job-name "$job" --output json)
                num_workers=$(echo "$job_def" | jq -r '.Job.DefaultArguments."--NumberOfWorkers"')
                if [ -z "$num_workers" ] || [ "$num_workers" = "null" ]; then
                    num_workers=1
                fi
            fi

            # WorkerType の取得。無ければ "G.1X" と仮定
            worker_type=$(echo "$run" | jq -r '.WorkerType')
            if [ -z "$worker_type" ] || [ "$worker_type" = "null" ]; then
                worker_type="G.1X"
            fi

            if [ "$worker_type" = "G.2X" ]; then
                dpu_multiplier=2
            else
                dpu_multiplier=1
            fi

            # コスト計算: cost = NumberOfWorkers * dpu_multiplier * runtime_hours * DPU_HOUR_RATE
            run_cost=$(echo "scale=4; $num_workers * $dpu_multiplier * $runtime_hours * $DPU_HOUR_RATE" | bc)
            total_cost=$(echo "scale=4; $total_cost + $run_cost" | bc)

            echo "  Run開始: $started / 終了: $completed / 時間: ${runtime_hours}h / Workers: ${num_workers} (${worker_type}) -> コスト: USD ${run_cost}"
        done

        next_token=$(echo "$result" | jq -r '.NextToken')
        if [ -z "$next_token" ] || [ "$next_token" = "null" ]; then
            break
        fi
    done

    echo ">> ジョブ [${job}] の ${TARGET_MONTH} の合計コスト: USD ${total_cost}"
    echo "-----------------------------------------"
    # サマリー用に格納
    job_names_summary+=("$job")
    job_costs_summary+=("$total_cost")
done

# 全ジョブのサマリー出力
echo ""
echo "=== 全ジョブサマリー ==="
for i in "${!job_names_summary[@]}"; do
    echo "Job: ${job_names_summary[$i]} -> 合計コスト: USD ${job_costs_summary[$i]}"
done

Discussion