Azure 구독에서 비용이 조용히 새는 곳을 꼽으라면 저는 항상 Blob Storage를 1순위로 봅니다. VM은 꺼져 있으면 눈에 띄지만, 스토리지는 아무도 읽지 않는 데이터가 Hot 티어에 몇 년씩 쌓여도 청구서에 한 줄로만 찍히기 때문입니다.
이 글은 “수명 주기 정책을 켜세요”라는 원론적인 조언이 아니라, 20TB 로그 데이터를 기준으로 실제 금액을 끝까지 계산해보고, 그대로 복사해서 쓸 수 있는 정책 JSON 전문과 검증 스크립트까지 정리한 글입니다. 그리고 2026년 7월부터 시행된 최소 과금 단위 변경 때문에, 예전 방식대로 정책을 짜면 오히려 비용이 늘어나는 경우가 생겼습니다. 이 부분을 먼저 짚고 시작하겠습니다.
먼저: 2026년 7월 바뀐 최소 과금 단위 128 KiB
Microsoft는 Cool·Cold·Archive 티어에 128 KiB 최소 과금 단위를 도입했습니다. 해당 티어에 있는 블롭이 128 KiB보다 작아도 128 KiB로 간주해 과금합니다. 적용 일정은 두 단계입니다.
- 2026년 7월 1일 — 이 날짜 이후 새로 만든 스토리지 계정에 즉시 적용
- 2027년 7월 1일 — 기존 스토리지 계정까지 전면 적용
왜 이게 중요하냐면, 로그·IoT 텔레메트리처럼 작은 파일이 수백만 개인 워크로드에서는 티어를 낮추는 순간 비용이 오히려 뛰기 때문입니다. 뒤에서 숫자로 보여드리겠습니다.
티어별 단가와, 단가표에 안 보이는 비용 3가지
아래는 계산의 기준으로 삼을 단가입니다. LRS·미국 동부 기준 근사치이며, 리전과 중복 옵션(LRS/ZRS/GRS)에 따라 달라집니다. 실제 견적은 반드시 Azure 요금 계산기에서 Korea Central로 다시 확인하세요.
| 티어 | 저장 단가 (USD/GB/월) | 최소 보관 기간 | 읽기 특성 |
|---|---|---|---|
| Hot | 약 $0.018 | 없음 | 즉시, 읽기 비용 최저 |
| Cool | 약 $0.010 | 30일 | 즉시, 읽기 비용 발생 |
| Cold | 약 $0.0045 | 90일 | 즉시, 읽기 비용 높음 |
| Archive | 약 $0.00099 | 180일 | 해동 필요(최대 15시간) |
여기서 단가표만 보고 정책을 짜면 반드시 세 군데서 걸립니다.
1) 조기 삭제 위약금
Cool은 30일, Cold는 90일, Archive는 180일의 최소 보관 기간이 있습니다. 그 전에 삭제하거나 다른 티어로 옮기면 남은 기간만큼 요금이 그대로 청구됩니다. “30일 후 Cool, 60일 후 삭제” 같은 정책을 짜면 Cool 30일치를 고스란히 물게 됩니다. 정책의 단계 간격은 항상 최소 보관 기간 이상으로 잡아야 합니다.
2) 티어 이동 자체가 트랜잭션 비용
티어를 내리는 동작은 쓰기 트랜잭션으로 과금됩니다. 블롭 1개당 1건이므로, 1,000만 개를 한꺼번에 Cool로 내리면 1,000만 건의 쓰기 트랜잭션이 한 달에 몰립니다. 개수가 많은 계정이라면 정책을 켜기 전에 트랜잭션 단가를 한 번 곱해보고 들어가야 합니다.
3) Archive의 해동 시간과 비용
Archive는 바로 읽을 수 없습니다. 해동(Rehydration)을 걸어야 하고 표준 우선순위는 최대 15시간이 걸립니다. 높은 우선순위로 올리면 10GB 미만 기준 1시간 이내로 줄지만 비용이 크게 뜁니다. 감사·규정 대응처럼 “요청이 오면 며칠 안에 제출”이 허용되는 데이터만 Archive로 보내야 합니다. 장애 조사용 로그를 Archive에 넣으면, 정작 장애가 났을 때 15시간을 기다리게 됩니다.
20TB 로그 시나리오: 계산 과정 전부 공개
가정을 명확히 하겠습니다. 계산은 누구나 검산할 수 있어야 의미가 있습니다.
- 총 용량 20TB = 20,480 GB (애플리케이션 로그, 3년 보관 규정)
- 블롭 평균 크기 4MB (일 단위로 묶어 업로드한 압축 로그)
- 환율 1 USD = 1,400원
- 중복 옵션 LRS, 읽기는 사실상 장애 조사 시에만 발생
Before: 전량 Hot 방치
20,480 GB × $0.018 = $368.64/월 → 약 516,000원/월
After: 나이별로 티어를 내렸을 때
3년치가 균등하게 쌓여 있다고 보면 데이터 나이 분포는 대략 아래와 같습니다.
| 데이터 나이 | 티어 | 비중 | 용량 | 월 비용 |
|---|---|---|---|---|
| 0~30일 | Hot | 10% | 2,048 GB | $36.86 |
| 31~90일 | Cool | 20% | 4,096 GB | $40.96 |
| 91~180일 | Cold | 30% | 6,144 GB | $27.65 |
| 181일 이상 | Archive | 40% | 8,192 GB | $8.11 |
| 합계 | $113.58 | |||
$113.58/월 → 약 159,000원/월. 516,000원에서 159,000원이니 69% 절감, 연간 약 428만원입니다.
흥미로운 건 비중 40%를 차지하는 Archive 구간의 비용이 전체의 7%밖에 안 된다는 점입니다. 절감 효과의 대부분은 “가장 오래된 데이터를 Archive로 보내는 것”에서 나옵니다. 반대로 Hot에 남은 10%가 전체 비용의 32%를 차지합니다. 정책을 설계할 때 Hot 구간을 며칠로 잡느냐가 생각보다 큰 변수입니다.
역효과 사례: 작은 파일 1,000만 개를 Cool로 내리면
이번엔 같은 정책을 평균 8KB짜리 로그 파일 1,000만 개에 적용해보겠습니다. 128 KiB 최소 과금 단위가 적용되는 계정이라고 가정합니다.
- 실제 용량: 10,000,000 × 8KB = 약 76.3 GB
- Hot에 그대로 둘 때: 76.3 × $0.018 = $1.37/월
- Cool로 내렸을 때 과금 용량: 10,000,000 × 128 KiB = 약 1,220.7 GB
- Cool 요금: 1,220.7 × $0.010 = $12.21/월
비용을 줄이려고 티어를 내렸는데 약 8.9배가 됐습니다. 여기에 1,000만 건의 티어 변경 트랜잭션 비용까지 별도로 붙습니다.
결론은 단순합니다. 수명 주기 정책을 켜기 전에 평균 블롭 크기부터 재야 합니다. 평균이 128 KiB에 못 미치면, 티어를 내리는 게 아니라 파일을 묶는 것(압축·일 단위 병합)이 먼저입니다. 이 순서를 바꾸면 정책이 비용을 만들어냅니다.
0단계: 평균 블롭 크기부터 측정하기
정책을 만들기 전에 반드시 돌려야 할 스크립트입니다. 컨테이너의 블롭 개수·총 용량·평균 크기를 한 번에 뽑습니다. 평균이 128 KiB 미만이면 그 컨테이너는 티어 하향 대상이 아닙니다.
#!/usr/bin/env bash
set -euo pipefail
ACCOUNT="stlogprod001"
RG="rg-log-prod"
CONTAINER="logs"
az storage blob list \
--account-name "$ACCOUNT" \
--container-name "$CONTAINER" \
--auth-mode login \
--query "[].{size:properties.contentLength, tier:properties.blobTier}" \
-o tsv > blobs.tsv
awk -F'\t' '
{ n++; total += $1 }
END {
printf "blob 개수 : %d\n", n
printf "총 용량 : %.2f GiB\n", total/1024/1024/1024
printf "평균 크기 : %.1f KiB\n", (total/n)/1024
if ((total/n)/1024 < 128)
printf "\n[경고] 평균이 128 KiB 미만입니다. 티어 하향 시 비용이 늘어납니다.\n"
}' blobs.tsv
블롭이 수백만 개인 계정에서는 az storage blob list가 오래 걸립니다. 그럴 때는 컨테이너 하나를 표본으로 잡거나, 스토리지 계정의 Insights → 용량 메트릭에서 “Blob Count”와 “Blob Capacity”를 나눠 평균을 구해도 충분합니다.
수명 주기 정책 JSON 전문
앞의 20TB 시나리오를 그대로 구현한 정책입니다. lifecycle.json으로 저장해서 쓰시면 됩니다. 단계 간격은 앞서 설명한 최소 보관 기간(Cool 30일, Cold 90일, Archive 180일)을 위반하지 않도록 잡았습니다.
{
"rules": [
{
"enabled": true,
"name": "logs-tiering-3year",
"type": "Lifecycle",
"definition": {
"filters": {
"blobTypes": [ "blockBlob" ],
"prefixMatch": [ "logs/" ]
},
"actions": {
"baseBlob": {
"tierToCool": { "daysAfterModificationGreaterThan": 30 },
"tierToCold": { "daysAfterModificationGreaterThan": 90 },
"tierToArchive": { "daysAfterModificationGreaterThan": 180 },
"delete": { "daysAfterModificationGreaterThan": 1095 }
},
"snapshot": {
"delete": { "daysAfterCreationGreaterThan": 90 }
},
"version": {
"delete": { "daysAfterCreationGreaterThan": 90 }
}
}
}
}
]
}
몇 가지 짚어둘 점이 있습니다.
prefixMatch를 반드시 지정하세요. 생략하면 계정 전체에 적용됩니다. 운영 중인 컨테이너까지 Archive로 내려가는 사고가 여기서 납니다.snapshot·version삭제 규칙을 빼먹는 경우가 많습니다. 버전 관리가 켜져 있으면 지운 줄 알았던 데이터가 이전 버전으로 계속 과금됩니다. 실제로 원본보다 버전이 더 많은 용량을 차지하는 계정을 종종 봅니다.tierToCold는 비교적 최근 추가된 액션이라 오래된 Azure CLI에서는 인식하지 못합니다.az upgrade로 최신 버전을 맞춘 뒤 적용하세요.daysAfterModificationGreaterThan은 마지막 수정 시각 기준입니다. 마지막 접근 기준으로 하려면daysAfterLastAccessTimeGreaterThan을 쓰되, 계정에 접근 시간 추적을 먼저 켜야 합니다.
적용과 검증
정책을 적용하고, 적용됐는지 확인하고, 며칠 뒤 실제로 티어가 내려갔는지까지 봐야 끝입니다.
# 1) 정책 적용
az storage account management-policy create \
--account-name "$ACCOUNT" \
--resource-group "$RG" \
--policy @lifecycle.json
# 2) 등록 확인
az storage account management-policy show \
--account-name "$ACCOUNT" \
--resource-group "$RG" \
--query "policy.rules[].{rule:name, enabled:enabled}" \
-o table
# 3) 48시간 뒤 - 티어 분포가 실제로 바뀌었는지 확인
az storage blob list \
--account-name "$ACCOUNT" \
--container-name "$CONTAINER" \
--auth-mode login \
--query "[].properties.blobTier" -o tsv | sort | uniq -c
3번 명령의 출력이 Hot 일색에서 Hot / Cool / Cold / Archive로 갈라지면 정상 동작하는 것입니다.
정책은 즉시 실행되지 않습니다. Azure가 하루 1회 평가하며, 처음 적용 시 반영까지 최대 48시간이 걸립니다. 적용하고 10분 뒤에 티어가 그대로라고 정책이 잘못된 게 아닙니다. 이걸 모르고 정책을 지웠다 다시 만들기를 반복하면 평가 주기만 계속 초기화됩니다.
비용이 실제로 줄었는지 확인하는 법
Cost Analysis에서 스토리지 계정 하나만 떼어 보려면 필터를 두 개 걸면 됩니다.
- 비용 관리 → 비용 분석으로 이동
- 필터: Resource = 대상 스토리지 계정
- 그룹화: Meter — 여기서 티어별 라인이 분리돼 보입니다
- 세분성을 일별로 두고 정책 적용일 전후를 비교
주의할 점은 적용 첫 달에는 비용이 오히려 올라 보일 수 있다는 것입니다. 티어 이동 트랜잭션이 그 달에 한꺼번에 잡히기 때문입니다. 판단은 두 번째 달 청구서부터 하셔야 합니다. 첫 달 그래프만 보고 정책을 되돌리면, 트랜잭션 비용만 내고 절감 효과는 못 보는 최악의 결과가 됩니다.
적용 전 점검 체크리스트
- 평균 블롭 크기가 128 KiB 이상인가? (미만이면 병합이 먼저)
- 계정 생성일이 2026년 7월 1일 이후인가? (이후면 최소 과금 단위가 이미 적용 중)
prefixMatch로 대상 컨테이너를 한정했는가?- 단계 간격이 최소 보관 기간(30/90/180일)을 위반하지 않는가?
- Archive로 보내는 데이터가 15시간을 기다려도 되는 성격인가?
- 스냅샷·이전 버전 삭제 규칙을 넣었는가?
- 규정상 보관 의무 기간과
delete일수가 충돌하지 않는가?
정리
수명 주기 정책은 Azure 비용 최적화 중에서 투입 시간 대비 효과가 가장 확실한 축에 듭니다. 20TB 기준으로 월 516,000원이 159,000원이 되는 계산을 위에서 끝까지 따라가 보셨을 겁니다. 다만 순서를 지켜야 합니다.
- 평균 블롭 크기를 잰다 (128 KiB 기준선)
- 작으면 병합·압축부터 한다
- 최소 보관 기간을 지켜 단계를 설계한다
prefixMatch로 범위를 좁혀 적용한다- 48시간 뒤 티어 분포로 검증하고, 두 번째 달 청구서로 판단한다
비용 최적화를 이어서 보실 분들께는 비용이 갑자기 늘었을 때 Cost Analysis로 원인 찾는 법과 Reserved·Savings Plan·Spot 선택 기준을 함께 권합니다. 스토리지는 새는 곳을 막는 쪽이고, 컴퓨팅 약정은 정가를 깎는 쪽이라 성격이 다릅니다.
※ 본문의 단가는 LRS·미국 동부 기준 근사치이며 리전·중복 옵션에 따라 달라집니다. 실제 도입 전에는 Azure 요금 계산기에서 Korea Central 기준으로 재확인하시기 바랍니다.