E 会計/ファイナンス・コスト効率 — GCP コスト最適化 Bad→Good 6点(BigQuery スキャン削減 × Cloud Run CPU idle × GKE Spot+Autoscaler × ROI 779% ペイバック 46日 × CFO 提案)

2026-07-03 (Day 92) 金曜 E: 会計/ファイナンス・コスト効率 ★★★★☆ BigQuery partition_by / cluster_by / SELECT * Cloud Run cpu_idle / GKE Spot / Storage Lifecycle

概要

💰

BigQuery: スキャン量課金を正しく理解する

BigQuery On-Demand は スキャン量 $5/TB。パーティション未設定 + SELECT * の組み合わせは最悪。partition_by(ingestion_time または DATE 列)+cluster_by で WHERE 句による partition pruning が効き、スキャン量を 80〜95% 削減できる。SELECT * の廃止は列プルーニングで効果が倍増する。

Cloud Run: CPU idle vs CPU always-on

cpu_idle = true(旧 cpu = "requests")はリクエスト処理中のみ CPU を課金。min-instances > 0 の場合はコンテナは起動し続けるが、アイドル中は CPU 無課金。always-on(cpu = "cpu")はアイドル中も CPU 課金発生。深夜 13 時間アイドルがある MOps 配信 API では 65% 削減が見込める。

🖥️

GKE: Spot + Cluster Autoscaler でバッチコストを最小化

Spot インスタンスは On-Demand 比最大 91% 安価。バッチジョブは中断許容度が高く Spot との親和性が高い。Cluster Autoscaler でバッチ時間帯のみノードをスケールアップし、アイドル時間帯はゼロに。Argo Workflows の retryStrategy で中断時の自動リトライを設計する。

📊

CFO 言語: コスト削減を投資案件として提案する

エンジニアは「技術的最適化」で語りがちだが、CFO には ROI・ペイバック・年間キャッシュフロー改善額 で話す。「$7,500 の投資で $58,440 のリターン、ペイバック 46 日」は証券マン時代の投資案件分析と同じ論理構造。コスト削減 = 利益改善(EBITDA 直撃)として位置づける。

問題

ECサイト MOps チームのシニアエンジニアとして、四半期末のクラウドコストレビューで CFO から「Q2 の GCP 請求額が予算比 38% 超過している。原因と削減策をエンジニア視点で提示してほしい」と依頼を受けた。コスト最適化 Bad パターン(6点)を洗い出し、削減後の月次コスト試算、ROI・ペイバック期間の計算、CFO への投資提案まで一気通貫で回答せよ。

Q2 GCP コスト内訳(月次平均、予算比 +38%)

サービス現状コスト/月問題の有無
BigQuery(スキャン課金)$4,800???
Cloud Run(キャンペーン配信 API)$1,240???
GKE ノード(バッチ処理)$2,160???
Cloud Storage(ログ・メディア)$890???
Pub/Sub + Cloud Functions$340???
Cloud Armor + Load Balancer$280問題なし
合計$9,710/月予算: $7,040/月(+38%)

制約・前提条件

  • 削減施策の実装工数は最大 2 スプリント(4 週間)以内
  • 可用性・SLO は維持すること(エラー率 1.5% 以下 / P99 レイテンシ 500ms 以下)
  • 削減効果は月次コスト削減額($)と削減率(%)で示す
  • ROI = 年間削減額 ÷ 実装コスト(エンジニア 1名 × 月額 $15,000 換算)
  • ペイバック = 実装コスト ÷ 月次削減額(月数)
期待する回答形式: 問題点の列挙(番号付き)+ 削減施策(施策名・現状→改善後・削減額試算)+ 月次削減サマリー + ROI / ペイバック計算 + CFO 提案文(3文以内)

現状インフラ構成 (Before)

この構成には 6 つのコスト最適化 Bad パターン が潜んでいます。
現状構成(Bad)— 6 つの問題
# BigQuery: パーティション未設定 / クラスタリングなし
# テーブル全スキャン で日次集計クエリを実行中
# 1クエリあたり平均 800GB スキャン × 20クエリ/日
# 問題①: partition_by なし → WHERE 句が効かずフルスキャン
# 問題②: SELECT * → 不要列まで全スキャン(全クエリの43%)

# Cloud Run: CPU always-on 設定(min-instances: 3)
# 実際のリクエスト処理は営業時間(9:00-20:00)のみ
# 深夜帯(20:00-9:00, 13時間)はほぼ idle
# 問題③: cpu always-on → 深夜アイドル 13時間も CPU 課金

# GKE: n2-standard-8 × 5台(On-Demand)
# バッチ処理は 04:00-08:00 の4時間のみ実行
# 残り 20時間はアイドル(Cluster Autoscaler なし)
# 問題④: On-Demand 常時稼働 → アイドル 20時間分浪費

# Cloud Storage: 全データを Standard で保存
# ログ: 90日以降も Standard のまま
# 画像: 配信後 30日以降はほぼアクセスなし
# 問題⑤: ライフサイクルルール未設定 → 全 Standard 課金

# Cloud Functions: メモリ 2GB × 最大 100インスタンス
# 実際の最大同時実行数: 過去3ヶ月で 12インスタンス
# 問題⑥: リソース上限が実績の8倍以上 → 過剰課金
問題点サマリー(6点)
1BigQuery パーティション未設定 — 800GB × 20クエリ/日 × 30日 = 480TB/月 → $2,400/月が不要スキャン
2SELECT * 多用(全クエリの 43%) — 必要列のみ SELECT で列プルーニング。スキャン量さらに 20〜40% 削減
3Cloud Run CPU always-on — 深夜 13時間のアイドル CPU 課金。cpu_idle で解消
4GKE On-Demand × 5台(24時間稼働) — バッチ使用は 4時間/日のみ。Spot + Cluster Autoscaler で 70% 削減
5Cloud Storage ライフサイクル未設定 — 全データ Standard($0.02/GB)。Nearline/Coldline で 50〜80% 安
6コスト可視化・アラートなし — 予算超過が四半期末まで未発覚。Cloud Billing Budget アラート未設定

ヒント(段階的開示)

ヒント1 — 方向性
GCP コスト最適化の Bad パターンは 4 類型。(1) 使われていない時間帯でリソースが起動し続けている(Cloud Run CPU always-on / GKE On-Demand 全時間稼働)、(2) データスキャン量が最適化されていない(BigQuery パーティション未設定 / SELECT *)、(3) データのライフサイクルが管理されていない(Cloud Storage 全 Standard)、(4) リソース上限が実績と乖離している(Cloud Functions 過剰設定)。各サービスの課金モデル(スキャン量 vs 時間 vs リクエスト数)を正確に把握するのが最初のステップ。
ヒント2 — 課金モデルと削減方法
  • BigQuery: スキャン量 $5/TB。800GB × 20 × 30日 = 480TB/月 → $2,400。partition_by + cluster_by で 80% 削減 → -$1,920
  • Cloud Run: cpu_idle で深夜アイドル CPU 課金ゼロ。min-instances: 3 → 1 で常時起動コンテナも削減 → $1,240 の 65% 削減
  • GKE: Spot は On-Demand 比最大 91% 安。Cluster Autoscaler でアイドル時ゼロスケール → $2,160 の 70% 削減
  • Cloud Storage: Nearline $0.01/GB(Standard の 50%)、Coldline $0.004/GB(80% 安)。ライフサイクルルールで自動移行
  • Cloud Functions: メモリ 2GB → 512MB(課金 75% 削減)、max-instances 100 → 20(実績 12 の 1.67倍で安全マージン確保)
ヒント3 — 削減額の骨格とROI計算
BigQuery スキャン最適化: $4,800 → $960  = -$3,840(-80%)
Cloud Run CPU idle:      $1,240 → $434  = -$806(-65%)
GKE Spot + Autoscaler:  $2,160 → $648  = -$1,512(-70%)
Cloud Storage Lifecycle: $890  → $445  = -$445(-50%)
Cloud Functions 最適化:  $340  → $153  = -$187(-55%)
────────────────────────────────────────────────────
合計削減額:    -$4,870/月(-50%)
最適化後合計:  $4,840/月

実装コスト: 1名 × 0.5ヶ月 = $7,500
ROI = $4,870 × 12 ÷ $7,500 = 779%
ペイバック = $7,500 ÷ $4,870 = 1.54ヶ月(約46日)

問題点分析(6点)

#問題点分類削減施策削減額
1BigQuery パーティション未設定 → フルスキャン 480TB/月スキャン最適化partition_by + cluster_by + WHERE pruning-$3,840(-80%)
2SELECT * 多用(43%)→ 不要列もフルスキャンスキャン最適化必要列のみ SELECT(施策1に統合)(施策1に含む)
3Cloud Run CPU always-on → 深夜 13h アイドル課金時間課金最適化cpu_idle=true + min-instances: 1-$806(-65%)
4GKE On-Demand 5台 × 24h → アイドル 20h/日コンピュート最適化Spot + Cluster Autoscaler-$1,512(-70%)
5Cloud Storage ライフサイクル未設定 → 全 Standardストレージ最適化Nearline 30d / Coldline 90d ライフサイクル-$445(-50%)
6Cloud Functions 2GB × 100インスタンス(実績 12)リソース最適化512MB / max-instances: 20-$187(-55%)

GCP コスト削減フロー — Bad vs Good(SVG)

Bad — 現状 $9,710/月(予算比 +38%) BigQuery — $4,800/月(最大コスト) パーティション未設定 → フルスキャン 480TB/月 × $5/TB = $2,400 SELECT * 多用(43%)→ 不要列も全スキャン 問題①②: partition_by なし / SELECT * → スキャン過剰 $4,800 Cloud Run — $1,240/月 CPU always-on(cpu="cpu")+ min-instances: 3 深夜アイドル 13時間/日でも CPU 課金発生 問題③: CPU always-on → アイドル 54% の時間を課金 $1,240 GKE — $2,160/月 n2-standard-8 × 5台 On-Demand 24時間稼働 バッチ使用: 4時間/日のみ → 残り 20時間アイドル 問題④: Cluster Autoscaler なし + On-Demand → コスト最悪 $2,160 Cloud Storage — $890/月 全データ Standard($0.02/GB)。ライフサイクル未設定 問題⑤: ログ・画像がアーカイブされず Standard に残存 $890 Cloud Functions — $340/月 メモリ 2GB × max-instances 100(実績 12)過剰設定 問題⑥: リソース上限が実績の 8倍以上 → 無駄な課金 $340 合計: $9,710/月 予算 $7,040 比 +$2,670(+38% 超過) Good — 最適化後 $4,840/月(-50%) BigQuery — $960/月(-80%) partition_by(ingestion_time) + cluster_by(member_rank, campaign_id) WHERE 句で partition pruning → 800GB → 80GB/クエリ(-90%) SELECT column_list で列プルーニング / 年次 $46,080 節約 $960 Cloud Run — $434/月(-65%) cpu_idle=true → アイドル中 CPU 無課金 min-instances: 1(コールドスタート 1台のみ常時起動) カナリアデプロイで SLO(P99 500ms)影響確認 $434 GKE — $648/月(-70%) Spot インスタンス(On-Demand 比最大 91% 安) Cluster Autoscaler: 04-08時のみスケールアップ → 他は 0 台 Argo retryStrategy.limit: 3 で Spot 中断を自動リトライ $648 Cloud Storage — $445/月(-50%) Lifecycle: Standard → Nearline(30日後)→ Coldline(90日後) Nearline $0.01/GB / Coldline $0.004/GB(Standard $0.02 比 50〜80% 安) $445 Cloud Functions — $153/月(-55%) メモリ 2GB → 512MB / max-instances: 100 → 20 実績 12インスタンスの 1.67倍で安全マージン確保 $153 合計: $4,840/月(-$4,870 / -50%) 予算 $7,040 比 -$2,200(-31%)ROI 779% ペイバック 46日 最適化 -$4,870/月
ROI サマリー
指標数値
月次削減額$4,870/月(-50%)
年間削減額$58,440/年
実装コスト$7,500(1名 × 0.5ヶ月)
ROI779%(= $58,440 ÷ $7,500)
ペイバック期間1.54 ヶ月(約 46 日)

模範解答

## 問題点の列挙(6点)

### 問題①: BigQuery パーティション未設定 → フルスキャン
Bad:  テーブル全体をスキャン / 800GB × 20クエリ/日
理由: partition_by がないと WHERE 句が効かずフルスキャン
     480TB/月 × $5/TB = $2,400/月 の不要コスト
Fix:  partition_by(ingestion_time or DATE 列) + cluster_by(member_rank, campaign_id)
     → 80GB/クエリ(-90%)→ -$1,920/月

### 問題②: SELECT * 多用(全クエリの 43%)
Bad:  不要列も全スキャン
理由: SELECT * は列プルーニングが効かない
Fix:  SELECT 必要列のみ記述(施策①と組み合わせて効果倍増)

### 問題③: Cloud Run CPU always-on(深夜アイドル 13h/日)
Bad:  cpu="cpu"(always-on)+ min-instances: 3
理由: アイドル中も CPU 課金。13時間/日 × 3インスタンスが無駄
Fix:  cpu_idle=true + min-instances: 1 → -$806/月(-65%)

### 問題④: GKE On-Demand × 5台 × 24時間稼働
Bad:  n2-standard-8 × 5台 On-Demand / Cluster Autoscaler なし
理由: バッチ使用は 4時間/日のみ。残り 20時間 × 5台が無駄
Fix:  Spot + Cluster Autoscaler(04-08時のみスケールアップ)→ -$1,512/月(-70%)

### 問題⑤: Cloud Storage ライフサイクル未設定
Bad:  全データ Standard($0.02/GB/月)
理由: ログ・画像の古いデータが Standard に残存
Fix:  Nearline(30日後 $0.01/GB)→ Coldline(90日後 $0.004/GB)
     → -$445/月(-50%)

### 問題⑥: Cloud Functions 過剰リソース設定(実績の 8倍)
Bad:  メモリ 2GB × max-instances 100(実績 max 12)
理由: メモリ × 実行時間 × インスタンス数で課金。全て過剰
Fix:  512MB / max-instances: 20(実績 12の 1.67倍でマージン確保)
     → -$187/月(-55%)

### ボーナス問題⑥+: コスト可視化・アラートなし
Bad:  予算超過が四半期末の CFO レビューまで未発覚
Fix:  Cloud Billing Budget アラート(80%・100%・120%)
     + BigQuery INFORMATION_SCHEMA.JOBS 日次スキャン量 Top10 監視
# BigQuery コスト最適化: dbt モデル変更例

# ── Bad: パーティション・クラスタリングなし ──────────────────
# models/mart/campaign_summary.sql(Bad)
{{
  config(
    materialized='table'
    # partition_by なし → 全スキャン確定
  )
}}
SELECT
  *  # 問題②: SELECT * で不要列もスキャン
FROM {{ ref('stg_campaigns') }} c
JOIN {{ ref('stg_orders') }} o ON c.campaign_id = o.campaign_id
WHERE c.status = 'active'  # partition pruning が効かない

# ── Good: partition_by + cluster_by + 必要列 SELECT ─────────
# models/mart/campaign_summary.sql(Good)
{{
  config(
    materialized='incremental',
    # 修正①: ingestion_time パーティションで partition pruning を有効化
    partition_by={
      "field": "report_date",
      "data_type": "date",
      "granularity": "day"
    },
    # cluster_by で同一パーティション内をさらに絞る
    cluster_by=["member_rank", "campaign_id"],
    on_schema_change='fail'
  )
}}
SELECT
  -- 修正②: 必要列のみ明示的に SELECT(列プルーニング有効)
  c.campaign_id,
  c.campaign_name,
  c.member_rank,
  DATE(o.created_at) AS report_date,  -- partition 列
  COUNT(o.order_id)  AS order_count,
  SUM(o.amount)      AS total_amount,
  AVG(o.amount)      AS avg_order_value
FROM {{ ref('stg_campaigns') }} c
JOIN {{ ref('stg_orders') }} o
  ON c.campaign_id = o.campaign_id
  -- partition pruning: WHERE に partition 列(report_date)を指定
  AND DATE(o.created_at) >= DATE_SUB(CURRENT_DATE(), INTERVAL 90 DAY)
WHERE c.status = 'active'
  AND c.member_rank IN ('platinum', 'gold', 'silver', 'bronze')

{% if is_incremental() %}
  -- incremental: 過去 3日分のみ再計算(フルスキャン回避)
  AND DATE(o.created_at) >= DATE_SUB(CURRENT_DATE(), INTERVAL 3 DAY)
{% endif %}

# スキャン量変化:
# Before: 800GB/クエリ × 20クエリ/日 × 30日 = 480TB/月 → $2,400
# After:  80GB/クエリ × 20クエリ/日 × 30日 = 48TB/月 → $240(-90%)
# SELECT * 廃止でさらに 30〜50% 削減可能

# INFORMATION_SCHEMA 監視クエリ(日次実行)
SELECT
  user_email,
  query,
  total_bytes_processed / POW(1024, 3) AS gb_scanned,  -- GB 換算
  total_bytes_processed / POW(1024, 4) * 5 AS cost_usd  -- $5/TB
FROM `region-asia-northeast1`.INFORMATION_SCHEMA.JOBS
WHERE DATE(creation_time) = CURRENT_DATE()
  AND statement_type = 'SELECT'
ORDER BY total_bytes_processed DESC
LIMIT 10  -- Top 10 スキャン量クエリを監視
# ── Cloud Run: CPU idle 化 ────────────────────────────────
# terraform/cloud_run.tf(Bad → Good)

# Bad: CPU always-on
resource "google_cloud_run_v2_service" "campaign_api_bad" {
  template {
    containers {
      resources {
        cpu_idle = false  # always-on: アイドル中も CPU 課金
      }
    }
    scaling {
      min_instance_count = 3  # 常時 3台起動
      max_instance_count = 20
    }
  }
}

# Good: CPU idle + min-instances: 1
resource "google_cloud_run_v2_service" "campaign_api" {
  template {
    containers {
      resources {
        limits = { cpu = "2", memory = "1Gi" }
        cpu_idle          = true  # 修正③: アイドル中 CPU 無課金
        startup_cpu_boost = true  # 起動時のみ CPU ブースト(レイテンシ改善)
      }
    }
    scaling {
      min_instance_count = 1  # コールドスタート防止に 1台のみ常時起動
      max_instance_count = 20
    }
  }
}

# ── GKE: Spot + Cluster Autoscaler ──────────────────────────
# terraform/gke.tf(Bad → Good)

# Bad: On-Demand 固定ノードプール
resource "google_container_node_pool" "batch_bad" {
  node_count = 5  # 固定 5台
  node_config {
    machine_type = "n2-standard-8"
    # Spot なし / On-Demand
  }
}

# Good: Spot + Cluster Autoscaler
resource "google_container_node_pool" "batch_spot" {
  # 修正④: Cluster Autoscaler でバッチ時間帯のみスケール
  autoscaling {
    min_node_count = 0  # アイドル時はゼロ台
    max_node_count = 8  # バッチ最大 8台(余裕)
  }
  node_config {
    machine_type = "n2-standard-8"
    spot         = true  # Spot インスタンス(最大 91% 安)
    # Spot 中断時の taint で Argo Workflows が retryStrategy でリカバリ
    taint {
      key    = "cloud.google.com/gke-spot"
      value  = "true"
      effect = "NO_SCHEDULE"
    }
  }
}

# Argo Workflows: Spot 中断対応
# workflow.yaml
# retryStrategy:
#   limit: 3
#   retryPolicy: OnError  # Spot 中断(preemption)時も再試行
#   backoff:
#     duration: "30s"
#     factor: 2

# ── Cloud Storage: ライフサイクルルール ──────────────────────
# terraform/storage.tf

resource "google_storage_bucket" "mops_logs" {
  name     = "mops-logs-prod"
  location = "asia-northeast1"
  # 修正⑤: ライフサイクルルールで自動ダウングレード
  lifecycle_rule {
    condition { age = 30 }  # 30日後: Standard → Nearline
    action { type = "SetStorageClass"; storage_class = "NEARLINE" }
  }
  lifecycle_rule {
    condition { age = 90 }  # 90日後: Nearline → Coldline
    action { type = "SetStorageClass"; storage_class = "COLDLINE" }
  }
  lifecycle_rule {
    condition { age = 365 }  # 1年後: Coldline → 削除(ログのみ)
    action { type = "Delete" }
  }
}

# Cloud Functions: メモリ・インスタンス最適化
resource "google_cloudfunctions2_function" "event_processor" {
  build_config { ... }
  service_config {
    # 修正⑥: 2GB → 512MB、max-instances 100 → 20
    available_memory   = "512Mi"  # 実績ピーク: 320MB / マージン 60%
    max_instance_count = 20       # 実績最大 12 の 1.67倍
    min_instance_count = 0        # コスト最小化(コールドスタート許容)
  }
}
## ROI・ペイバック計算

### 削減施策サマリー
┌─────────────────────────────────────┬──────────┬──────────┬──────────┬──────────┐
│ 施策                                │ 現状/月  │ 最適化後  │ 削減額   │ 削減率   │
├─────────────────────────────────────┼──────────┼──────────┼──────────┼──────────┤
│ ① BigQuery partition_by + SELECT最適化│ $4,800   │ $960     │ -$3,840  │ -80%     │
│ ② Cloud Run cpu_idle + min-inst: 1  │ $1,240   │ $434     │ -$806    │ -65%     │
│ ③ GKE Spot + Cluster Autoscaler     │ $2,160   │ $648     │ -$1,512  │ -70%     │
│ ④ Cloud Storage Lifecycle           │ $890     │ $445     │ -$445    │ -50%     │
│ ⑤ Cloud Functions 512MB / max: 20   │ $340     │ $153     │ -$187    │ -55%     │
│ ⑥ Cloud Armor + LB(変更なし)       │ $280     │ $280     │ $0       │ 0%       │
├─────────────────────────────────────┼──────────┼──────────┼──────────┼──────────┤
│ 合計                                │ $9,710   │ $4,840   │ -$4,870  │ -50%     │
└─────────────────────────────────────┴──────────┴──────────┴──────────┴──────────┘

### 実装コスト内訳
BigQuery 最適化(dbt config + クエリ書き換え): 3日
Cloud Run Terraform 変更 + カナリア検証:         1日
GKE Spot + Autoscaler + Argo retry 設定:        4日
Cloud Storage ライフサイクル設定:                1日
Cloud Functions リソース変更 + 負荷テスト:       1日
Cloud Billing Budget アラート設定:              0.5日
─────────────────────────────────────────────────
合計: 10.5日 ≒ 0.5ヶ月(エンジニア 1名)
実装コスト: $15,000 × 0.5 = $7,500

### ROI 計算(12ヶ月ベース)
年間削減額  = $4,870/月 × 12 = $58,440
ROI         = $58,440 ÷ $7,500 = 7.79 = 779%
ペイバック  = $7,500 ÷ $4,870 = 1.54ヶ月(約 46日)

### 優先順位(ICE ライクスコア)
施策            | Impact | Ease | Priority
BigQuery 最適化 |  高    |  中  | 最優先(削減額最大)
GKE Spot       |  高    |  中  | 次点(工数中)
Cloud Run idle  |  中    |  高  | 即実施(設定変更のみ)
Storage LF      |  中    |  高  | 即実施(Terraform 変更)
Functions       |  低    |  高  | 最後(削減額最小)

CFO 提案文(3文以内)

Q2 GCP 費用 $9,710/月(予算比 +38%)の主因は、BigQuery 全スキャン・GKE 常時起動・Cloud Run CPU アイドル課金の 3 点で、これらは構成変更で即座に解消できます。2 スプリント(実装コスト $7,500)で月次 $4,870(-50%)の削減が見込まれ、年間 $58,440 のコスト圧縮・ROI 779%・ペイバック 46 日を達成できます。あわせて Cloud Billing Budget アラートを設定し、予算超過を月次レビュー前に検知できる体制を整備します。

提案構造(エンジニア → CFO の変換ロジック)

エンジニア言語CFO 言語
BigQuery partition_by 設定スキャンコスト 80% 削減 → 年間 $46,080 節約
GKE Spot + Autoscalerコンピュートコスト 70% 削減 → 年間 $18,144 節約
Cloud Run cpu_idleアイドル課金ゼロ → 年間 $9,672 節約
実装工数 0.5ヶ月投資額 $7,500(1回限り)
月次削減 $4,870ROI 779% / ペイバック 46日 / EBITDA 直接改善

ポイント解説

1 BigQuery コスト最適化の最大効果: partition_by + cluster_by
スキャン削減はクエリチューニングより partition_by(ingestion_time / DATE 列)設定が即効性が高い。WHERE 句に partition 列を含めると partition pruning が働き、対象 partition のみスキャンされる。cluster_by は同一 partition 内をさらに絞る(BigQuery の block pruning)。SELECT * の廃止は列プルーニングで効果が倍増。800GB → 80GB/クエリ(-90%)は現実的な削減幅。
2 Cloud Run の CPU 課金モデルを正確に理解する
cpu_idle = true はリクエスト処理中のみ CPU を課金。false(always-on)はアイドル中も CPU 課金発生。min-instances > 0 のコンテナは常時起動するが、cpu_idle ならアイドル中は CPU 無課金。コールドスタートの影響は startup_cpu_boost = true(起動時一時 CPU ブースト)で軽減できる。カナリアデプロイで P99 レイテンシへの影響を必ず確認する。
3 GKE Spot の中断リスクとバッチ設計の親和性
Spot インスタンスは最大 91% 安価だが、中断時(preemption)最大 60 秒で停止する。バッチジョブは中断許容度が高く Spot との親和性が最も高いワークロード。Argo Workflows の retryStrategy.limit: 3 + retryPolicy: OnError で中断時の自動リトライを設計する。Cluster Autoscaler の min_node_count: 0 で非バッチ時間帯のノードをゼロにし、アイドル課金を完全に排除できる。
4 Cloud Storage ライフサイクルの ROI は長期で最大化(複利的)
ライフサイクルルールは設定後 24 時間以内に反映。データが蓄積するほど削減効果が増加する(複利的に効く)。ストレージクラスのコスト: Standard $0.02/GB、Nearline $0.01/GB(-50%)、Coldline $0.004/GB(-80%)、Archive $0.0012/GB(-94%)。Coldline は読み取りコスト($0.01/GB)が Standard より高いため、アクセス頻度が確実にゼロになるデータのみ対象とする。
5 ROI・ペイバック計算を CFO 言語で話す
エンジニアはコスト削減を「技術的な最適化」として語りがちだが、CFO には 投資案件 として提示する。「$7,500 の投資で $58,440 のリターン、ペイバック 46 日」は証券マン時代の投資分析と同じ論理構造。コスト削減は EBITDA を直接改善する。営業利益率 5% の EC 会社では「$58,440 のコスト削減 = $1,168,800 の売上増加と同等のインパクト」という換算も有効。
6 コスト可視化はコスト最適化の前提条件
今回のように四半期末に初めて予算超過が発覚するのは可視化の失敗。対策は 3 層: Cloud Billing Budget アラート(80%・100%・120% でアラート通知)、 BigQuery INFORMATION_SCHEMA.JOBS の日次スキャン量 Top 10 モニタリング、 DataDog へのコストメトリクス転送(GCP コスト指標をエンジニア向けダッシュボードに統合)。
gcloud billing budgets create --billing-account=ACCOUNT_ID --display-name="GCP Monthly Budget" --budget-amount=7040USD --threshold-rule=percent=0.8 --threshold-rule=percent=1.0 --threshold-rule=percent=1.2

実務への応用

  • BigQuery コスト削減の dbt 実装: dbtconfig(partition_by={"field": "report_date"}, cluster_by=["member_rank", "campaign_id"]) を既存モデルに追加するだけで partition pruning が有効化。既存クエリの SELECT * は dbt-utils.star() マクロで除外列指定も可能。dbt Unit Tests(1.8+)で集計精度を検証してからリリース。
  • Cloud Run のカナリアデプロイ確認: cpu_idle 変更後は traffic split(10% カナリア → 100% 切り替え)で P99 レイテンシへの影響を確認。DataDog の p99(trace.web.request.duration) メトリクスを 2 スプリント間監視してから全切り替え。
  • GKE Spot バッチの Argo 設定: Argo CronWorkflow に tolerations: [{key: "cloud.google.com/gke-spot", value: "true", effect: "NoSchedule"}] を追加。Spot 中断時の retryStrategy.limit: 3 で自動リトライ。04:00-08:00 の時間帯に Node のスケールアップを先行させる PodDisruptionBudget も合わせて設定。
  • CFO への月次コストレポート: Cloud Billing エクスポート(BigQuery)から Looker Studio で「サービス別コスト推移・予算比・前月差」ダッシュボードを自動生成。月次 CFO レビューでは必ず ROI ベースの投資対効果を報告し、コスト削減施策を「投資案件」として継続的に提案する習慣をつける。

今日のまとめ

GCP コスト最適化の本質は「リソースが実際に使われている時間・量・サイズに課金を合わせる」こと。BigQuery のパーティション+クラスタリング・Cloud Run の CPU idle 化・GKE の Spot+Autoscaler という 3 つの施策だけで月次 $4,870(-50%)、年間 $58,440 の削減が可能。CFO 言語では「$7,500 投資・ROI 779%・ペイバック 46日の投資案件」として提案し、コスト削減 = EBITDA 直接改善として位置づける。コスト可視化(Cloud Billing Budget アラート + INFORMATION_SCHEMA 監視)がなければ問題が四半期末まで潜伏するため、最適化と同時に監視基盤を整備すること。

自己評価

自分の回答

気づき・メモ