당신의 Cloud Storage Bucket에 6개월 동안 아무도 손대지 않은 2TB 데이터가 있습니다 (당신은 16배나 과다하게 지불하고 있습니다) 💾

발행: (2026년 2월 23일 오전 10:39 GMT+9)
13 분 소요
원문: Dev.to

Source: Dev.to

비용 비교

ClassCost/GB / monthMin DurationBest ForRetrieval Fee
Standard$0.020없음핫 데이터, 빈번한 접근무료
Nearline$0.01030 일덜 자주 접근
Coldline$0.00490 일드물게 접근 (분기별)
Archive$0.0012365 일드물게 접근 (연간)

Note: 모든 스토리지 클래스는 동일한 11 개의 9(99.999999999 %) 내구성과 밀리초 수준 접근 지연을 제공합니다. Archive 스토리지는 테이프와 다르며 – 데이터는 즉시 접근 가능하고, 단지 더 높은 검색 비용만 지불하면 됩니다.

라이프사이클 정책이 중요한 이유

  • 모든 객체는 Standard 스토리지 클래스에서 시작하며, 개입하지 않으면 영원히 그곳에 머뭅니다.
  • GCP 자동으로 데이터를 더 저렴한 티어로 이동시키지 않습니다.
  • 정책이 없으면, 6개월 동안 손대지 않은 2 TB 데이터셋은 월 $40이 들지만, 실제로는 $2.40 정도만 비용이 들 수 있습니다.

라이프사이클 정책은 객체가 오래될수록 자동으로 더 저렴한 클래스로 전환합니다—수동 작업, 스크립트, 또는 cron 작업이 필요 없습니다. Terraform을 사용하면 몇 분 안에 배포할 수 있습니다.

일반적인 티어‑다운 패턴

Standard → Nearline → Coldline → Archive → Delete

Terraform 예시: 간단한 티어‑다운

resource "google_storage_bucket" "data" {
  name          = "${var.project_id}-app-data"
  location      = "US"
  storage_class = "STANDARD"   # start in Standard

  # 30 days → Nearline
  lifecycle_rule {
    action {
      type          = "SetStorageClass"
      storage_class = "NEARLINE"
    }
    condition {
      age                   = 30
      matches_storage_class = ["STANDARD"]
    }
  }

  # 90 days → Coldline
  lifecycle_rule {
    action {
      type          = "SetStorageClass"
      storage_class = "COLDLINE"
    }
    condition {
      age                   = 90
      matches_storage_class = ["NEARLINE"]
    }
  }

  # 365 days → Archive
  lifecycle_rule {
    action {
      type          = "SetStorageClass"
      storage_class = "ARCHIVE"
    }
    condition {
      age                   = 365
      matches_storage_class = ["COLDLINE"]
    }
  }

  # 730 days (2 years) → Delete
  lifecycle_rule {
    action {
      type = "Delete"
    }
    condition {
      age = 730
    }
  }

  labels = local.common_labels
}

⚠️ 주의: 라이프사이클 규칙에 의해 수행되는 전환은 조기 삭제 비용이 발생하지 않습니다. 객체를 수동으로 다른 클래스로 다시 쓰면 검색 비용과 조기 삭제 비용이 모두 발생하므로, 규칙에 맡겨 처리하십시오.

Terraform 예제: 접두사 기반 다계층

resource "google_storage_bucket" "multi_tier" {
  name     = "${var.project_id}-multi-tier-data"
  location = "US"

  # ---------- Logs ----------
  # 7 days → Nearline
  lifecycle_rule {
    action {
      type          = "SetStorageClass"
      storage_class = "NEARLINE"
    }
    condition {
      age                = 7
      matches_prefix     = ["logs/"]
      matches_storage_class = ["STANDARD"]
    }
  }

  # 30 days → Coldline
  lifecycle_rule {
    action {
      type          = "SetStorageClass"
      storage_class = "COLDLINE"
    }
    condition {
      age                = 30
      matches_prefix     = ["logs/"]
      matches_storage_class = ["NEARLINE"]
    }
  }

  # 90 days → Delete
  lifecycle_rule {
    action {
      type = "Delete"
    }
    condition {
      age            = 90
      matches_prefix = ["logs/"]
    }
  }

  # ---------- Backups ----------
  # 30 days → Coldline
  lifecycle_rule {
    action {
      type          = "SetStorageClass"
      storage_class = "COLDLINE"
    }
    condition {
      age                = 30
      matches_prefix     = ["backups/"]
      matches_storage_class = ["STANDARD"]
    }
  }

  # 90 days → Archive
  lifecycle_rule {
    action {
      type          = "SetStorageClass"
      storage_class = "ARCHIVE"
    }
    condition {
      age                = 90
      matches_prefix     = ["backups/"]
      matches_storage_class = ["COLDLINE"]
    }
  }

  # 730 days → Delete (keep backups for 2 years)
  lifecycle_rule {
    action {
      type = "Delete"
    }
    condition {
      age            = 730
      matches_prefix = ["backups/"]
    }
  }

  # ---------- User uploads ----------
  # 90 days → Nearline
  lifecycle_rule {
    action {
      type          = "SetStorageClass"
      storage_class = "NEARLINE"
    }
    condition {
      age                = 90
      matches_prefix     = ["uploads/"]
      matches_storage_class = ["STANDARD"]
    }
  }

  labels = local.common_labels
}

하나의 버킷, 세 가지 데이터 패턴:

  • 로그는 적극적으로 단계가 낮아지며 90 일 후 삭제됩니다.
  • 백업은 한 달 동안은 고온(Hot) 상태를 유지하고, 그 다음에는 냉각(Cold) 상태, 마지막으로 최대 2 년까지 보관되는 아카이브(Archive) 상태가 됩니다.
  • 사용자 업로드는 3개월 동안 Standard 상태를 유지한 뒤 Nearline으로 이동합니다.

Terraform 예제: 버전 관리 버킷

resource "google_storage_bucket" "versioned" {
  name     = "${var.project_id}-versioned-data"
  location = "US"

  versioning {
    enabled = true
  }

  # Keep only the 3 most recent versions
  lifecycle_rule {
    action {
      type = "Delete"
    }
    condition {
      num_newer_versions = 3
      with_state         = "ARCHIVED"
    }
  }

  # Delete non‑current versions older than 90 days
  lifecycle_rule {
    action {
      type = "Delete"
    }
    condition {
      age        = 90
      with_state = "ARCHIVED"
    }
  }

  # Abort incomplete multipart uploads after 7 days
  lifecycle_rule {
    action {
      type = "AbortIncompleteMultipartUpload"
    }
    condition {
      age = 7
    }
  }

  labels = local.common_labels
}

Why? 버전 관리는 데이터를 보호하지만 정리 작업이 없으면 저장 비용이 두 배 또는 세 배로 증가할 수 있습니다. 위 규칙들은 버킷을 자동으로 깔끔하게 유지해 줍니다.

요약

  • 모든 클래스가 동일한 내구성과 지연 시간을 공유합니다—필요한 액세스 패턴에 대해서만 비용을 지불합니다.
  • 수명 주기 규칙은 데이터를 더 저렴한 계층으로 이동하고 최종적으로 삭제하는 가장 간단하고 안전한 방법입니다.
  • Terraform으로 몇 분 안에 배포하고, GCP가 나머지를 처리하도록 하세요.

비용 효율적인 GCP 클라우드 스토리지와 Terraform

⚠️ 숨겨진 비용 요인

Incomplete multipart uploads는 콘솔에 표시되지 않지만 여전히 스토리지를 차지합니다. 애플리케이션이 대용량 파일을 업로드하고 중간에 실패하는 경우, 이러한 조각들이 조용히 쌓이게 됩니다.
AbortIncompleteMultipartUpload 규칙은 무료 보험입니다.

Autoclass – 자동 티어링

데이터가 얼마나 자주 액세스되는지 모를 경우, GCP의 Autoclass 기능은 실제 액세스 패턴에 따라 스토리지 클래스를 자동으로 이동시킵니다.

resource "google_storage_bucket" "auto_tiered" {
  name     = "${var.project_id}-auto-tiered"
  location = "US"

  autoclass {
    enabled                = true
    terminal_storage_class = "ARCHIVE"  # Lowest tier it can reach
  }

  labels = local.common_labels
}

Autoclass는 자주 액세스되는 데이터를 Standard 클래스로 위로 이동시키고(라이프사이클 규칙으로는 할 수 없습니다), 사용되지 않은 데이터를 자동으로 티어를 아래로 이동시킵니다. 관리 비용이 약간 발생하지만, 예측할 수 없는 액세스 패턴에서는 비용보다 절감 효과가 더 큽니다.

Autoclass와 수동 라이프사이클 규칙을 언제 사용할까

시나리오권장 방법
접근 패턴이 알려진 경우(로그, 백업)수동 라이프사이클 규칙
접근 패턴이 알 수 없거나 혼합된 경우Autoclass
몇 개월 후에 다시 접근될 가능성이 있는 데이터Autoclass
규정 준수 요구사항(특정 클래스에 있어야 함)수동 라이프사이클 규칙
비용을 최대한 통제하고 싶을 때수동 라이프사이클 규칙

모든 팀 버킷에 동일한 라이프사이클 규칙 적용

variable "buckets" {
  type = map(object({
    location       = string
    lifecycle_type = string  # "logs", "backups", "general", "autoclass"
  }))
  default = {
    "team-alpha-logs" = {
      location       = "US"
      lifecycle_type = "logs"
    }
    "team-beta-backups" = {
      location       = "US"
      lifecycle_type = "backups"
    }
    "ml-training-data" = {
      location       = "US"
      lifecycle_type = "autoclass"
    }
  }
}

locals {
  lifecycle_configs = {
    logs = {
      nearline_age = 7
      coldline_age = 30
      delete_age   = 90
    }
    backups = {
      nearline_age = 30
      coldline_age = 90
      delete_age   = 730
    }
    general = {
      nearline_age = 30
      coldline_age = 90
      delete_age   = 365
    }
  }
}

관리형 버킷 (Autoclass 비활성)

resource "google_storage_bucket" "managed" {
  for_each = {
    for k, v in var.buckets : k => v
    if v.lifecycle_type != "autoclass"
  }

  name          = "${var.project_id}-${each.key}"
  location      = each.value.location
  storage_class = "STANDARD"

  # → Move to NEARLINE
  lifecycle_rule {
    action {
      type          = "SetStorageClass"
      storage_class = "NEARLINE"
    }
    condition {
      age                    = local.lifecycle_configs[each.value.lifecycle_type].nearline_age
      matches_storage_class = ["STANDARD"]
    }
  }

  # → Move to COLDLINE
  lifecycle_rule {
    action {
      type          = "SetStorageClass"
      storage_class = "COLDLINE"
    }
    condition {
      age                    = local.lifecycle_configs[each.value.lifecycle_type].coldline_age
      matches_storage_class = ["NEARLINE"]
    }
  }

  # → Delete
  lifecycle_rule {
    action {
      type = "Delete"
    }
    condition {
      age = local.lifecycle_configs[each.value.lifecycle_type].delete_age
    }
  }

  # → Abort incomplete multipart uploads
  lifecycle_rule {
    action {
      type = "AbortIncompleteMultipartUpload"
    }
    condition {
      age = 7
    }
  }

  labels = local.common_labels
}

새 버킷이 필요하신가요? buckets 맵에 항목을 하나 추가하고 terraform apply를 실행하세요. 라이프사이클 규칙이 즉시 적용됩니다. ✅

빠른 성과 작업

작업노력절감
기존 로그 버킷에 수명 주기 규칙 추가5 분로그 스토리지 73‑94 % 절감
AbortIncompleteMultipartUpload 활성화2 분눈에 보이지 않는 비용 누수 차단
버전 관리 정리 규칙 추가5 분버전이 있는 버킷 50‑70 % 절감
재사용 가능한 수명 주기 모듈 생성15 분조직 전체에 일관된 규칙 적용
알 수 없는 패턴에 대해 Autoclass 활성화2 분자동 최적화 티어 적용

로그 버킷부터 시작하세요 – 보통 가장 큰 원인입니다 (일주일이 지나면 아무도 읽지 않는 방대한 데이터량). 🎯

Pricing Snapshot (US region)

클래스가격 / GB
Standard$0.020 (핫 데이터, 빈번한 접근)
Nearline$0.010 (≤ 1×/월, 최소 30일)
Coldline$0.004 (≤ 1×/분기, 최소 90일)
Archive$0.0012 (≤ 1×/년, 최소 365일)

모든 클래스는 동일한 내구성과 지연 시간을 제공하며, 차이는 가격뿐입니다.

왜 라이프사이클이 중요한가

  • 라이프사이클이 없음 → 모든 객체가 영원히 Standard에 머무름 → 오래된 데이터에 대해 16배까지 높은 비용 발생.
  • 몇 분짜리 Terraform → 수년간의 낭비를 없앨 수 있습니다. 🪣

다음 단계

  1. 식별 가장 큰 버킷을. 콘솔에서 “last accessed”(마지막 액세스) 기준으로 정렬합니다.
  2. 적용 적절한 lifecycle(수명 주기) 또는 Autoclass 구성을.
  3. 모니터링 다음 달 동안 비용 절감 효과를.

핵심 요점: 버킷에 lifecycle 규칙이 없으면 과도하게 비용을 지불하고 있습니다. 몇 분만 Terraform을 사용하면 오래된 데이터에서 80 % 이상의 절감을 달성할 수 있습니다.

이 내용이 도움이 되었나요? Terraform을 활용한 GCP 비용 최적화 팁을 더 보려면 팔로우하세요! 💬

0 조회
Back to Blog

관련 글

더 보기 »