왜 오브젝트 스토리지 비용이 방치되는가
오브젝트 스토리지는 "일단 넣어두면 싸다"는 인식 때문에 정리 없이 무한정 쌓이는 경우가 많다. 문제는 저장 용량뿐이 아니다. 멀티파트 업로드가 실패한 뒤 남는 파편(incomplete multipart upload), 버저닝을 켠 뒤 삭제되지 않는 이전 버전(noncurrent version), 삭제 마커(delete marker)는 목록에도 잘 안 보이면서 매달 과금된다. AWS S3든 MinIO·Ceph RGW 같은 S3 호환 스토리지든, 청구서의 상당 부분이 "쓰지 않는데 지우지도 않은" 데이터에서 나온다.
수명주기 정책의 3가지 축
비용을 줄이는 수명주기(lifecycle) 규칙은 세 축으로 나뉜다. 첫째, 오래된 객체를 저렴한 스토리지 클래스로 전환(transition). 둘째, 일정 기간 뒤 만료(expiration)로 삭제. 셋째, 버저닝 환경의 이전 버전·삭제 마커·미완료 멀티파트 정리. 세 번째가 실무에서 가장 자주 누락되며 효과가 크다.
접근 패턴별 스토리지 클래스
| 클래스 | 적합 데이터 | 주의점 |
|---|---|---|
| Standard | 수시 접근 로그·에셋 | 기본값, 가장 비쌈 |
| Standard-IA / Infrequent | 월 단위 접근 | 최소 보관 30일, 조회비 발생 |
| Glacier / Archive | 규정상 장기 보관 | 복원 지연·복원비, 최소 90~180일 |
IA·아카이브는 최소 보관 기간과 조회 비용이 있어서, 짧게 머물다 삭제될 데이터를 전환하면 오히려 손해다. 전환 임계값은 실제 접근 로그를 근거로 정해야 한다.
수명주기 규칙 설정 예시
버킷에 JSON 규칙을 적용한다. 로그는 30일 뒤 IA로 전환, 90일 뒤 삭제, 이전 버전은 30일 뒤 정리, 미완료 멀티파트는 7일 뒤 폐기한다.
{
"Rules": [{
"ID": "logs-cost-optimize",
"Filter": { "Prefix": "logs/" },
"Status": "Enabled",
"Transitions": [
{ "Days": 30, "StorageClass": "STANDARD_IA" }
],
"Expiration": { "Days": 90 },
"NoncurrentVersionExpiration": { "NoncurrentDays": 30 },
"AbortIncompleteMultipartUpload": { "DaysAfterInitiation": 7 }
}]
}
# AWS CLI (MinIO는 --endpoint-url 추가로 동일하게 동작)
aws s3api put-bucket-lifecycle-configuration \
--bucket my-bucket \
--lifecycle-configuration file://lifecycle.json
# 적용 확인
aws s3api get-bucket-lifecycle-configuration --bucket my-bucket
숨은 비용 진단: 미완료 업로드와 버전
규칙을 걸기 전에 현재 낭비를 측정한다. 미완료 멀티파트는 일반 목록에 안 잡히므로 별도 API로 조회한다.
import boto3
s3 = boto3.client("s3")
def scan_waste(bucket):
paginator = s3.get_paginator("list_multipart_uploads")
incomplete = 0
for page in paginator.paginate(Bucket=bucket):
incomplete += len(page.get("Uploads", []))
ver = s3.get_paginator("list_object_versions")
noncurrent, markers = 0, 0
for page in ver.paginate(Bucket=bucket):
for v in page.get("Versions", []):
if not v["IsLatest"]:
noncurrent += v["Size"]
markers += len(page.get("DeleteMarkers", []))
print(f"미완료 업로드: {incomplete}건")
print(f"이전 버전 용량: {noncurrent/1e9:.2f} GB")
print(f"삭제 마커: {markers}개")
scan_waste("my-bucket")
운영상 주의점
수명주기 삭제는 되돌릴 수 없다. 규칙 적용 전 반드시 좁은 Prefix에서 소규모로 검증하고, 규정·감사 대상 데이터는 Object Lock(WORM)으로 보호해 실수 삭제를 원천 차단한다. 만료 삭제는 비동기로 처리되므로 지정일 당일에 즉시 사라지지 않을 수 있고, 삭제 시점까지는 계속 과금된다는 점도 기억해야 한다.
정기 점검 자동화
수명주기는 한 번 걸고 끝이 아니다. Prefix 구조가 바뀌거나 새 데이터 유형이 생기면 규칙이 비껴간다. 위 진단 스크립트를 주간 배치로 돌려 이상 증가를 감시하고, S3 Storage Lens나 스토리지 자체 메트릭으로 클래스별 용량 추이를 대시보드화한다. "규칙이 실제로 객체를 줄이고 있는가"를 지표로 확인해야 정책이 유명무실해지지 않는다.