Azure를 처음 쓰기 시작한 조직에서 첫 청구서가 예상의 몇 배로 나오는 일은 드물지 않습니다. 원인은 대개 거창한 실수가 아니라 몇 가지 기본 설정입니다. 그리고 대부분 구독을 만든 첫 주에 30분이면 막을 수 있습니다.
순서대로 다섯 가지를 점검하겠습니다. 이미 운영 중인 구독에도 그대로 적용됩니다.
점검 1 — 예산 알림, 그중에서도 “예측” 알림
가장 먼저, 리소스를 만들기 전에 해야 할 일입니다. 그런데 많은 경우 알림을 걸어도 소용없게 설정합니다.
실제 비용 기준으로만 알림을 걸면, 이미 다 쓴 뒤에 알림이 옵니다. “예산의 80%를 썼습니다”를 월 28일에 받으면 할 수 있는 게 없습니다. 예측(Forecasted) 기준 알림이 필요합니다.
# 예산 생성
az consumption budget create \
--budget-name "monthly-guard" \
--amount 1000 \
--time-grain Monthly \
--category Cost \
--start-date 2026-08-01 \
--end-date 2027-08-01
포털의 비용 관리 → 예산에서 알림 조건을 추가할 때 최소 세 개를 거세요.
| 유형 | 임계값 | 의미 |
|---|---|---|
| 예측 | 100% | 이 속도면 예산을 넘습니다 — 월 중반에 옴 |
| 실제 | 80% | 이미 80% 썼습니다 |
| 실제 | 100% | 예산 초과 |
첫 번째가 실질적으로 유일하게 유용한 알림입니다. 나머지 둘은 기록용에 가깝습니다.
예산은 지출을 막지 않는다는 점도 알아두세요. 알림만 옵니다. 실제로 차단하려면 Action Group에 Automation Runbook을 연결해 리소스를 중지시키는 구성을 별도로 만들어야 합니다. 개발 구독에는 고려할 만합니다.
점검 2 — “중지됨”과 “할당 취소” 구분
신규 사용자가 가장 많이 당하는 함정입니다. VM 안에서 shutdown으로 껐다고 요금이 멈추지 않습니다.
| 상태 | 어떻게 되는가 | 컴퓨팅 요금 | 디스크 요금 |
|---|---|---|---|
Stopped | 게스트 OS에서 종료 | 계속 청구 | 청구 |
Stopped (deallocated) | Azure가 자원 회수 | 중지 | 청구 |
차이는 하드웨어 자원이 예약된 채로 남아 있느냐입니다. Stopped 상태는 VM이 쓰던 호스트 자원이 그대로 잡혀 있어서 요금이 계속 나갑니다.
# 껐다고 생각했지만 과금 중인 VM 찾기
az vm list -d \
--query "[?powerState=='VM stopped'].{name:name, rg:resourceGroup, size:hardwareProfile.vmSize}" \
-o table
# 제대로 끄기 (컴퓨팅 요금 중지)
az vm deallocate --name <VM> -g <RG>
# 구독 전체를 한 번에 정리
az vm list -d --query "[?powerState=='VM stopped'].[name,resourceGroup]" -o tsv \
| while read NAME RG; do
echo "deallocating $NAME ..."
az vm deallocate --name "$NAME" -g "$RG" --no-wait
done
할당을 취소해도 디스크 요금은 계속 나갑니다. 완전히 안 쓸 VM이라면 디스크까지 지워야 합니다. 그리고 할당 취소 시 임시 디스크(/mnt, D:) 데이터는 사라집니다.
점검 3 — 배포만 해도 돈이 나가는 서비스들
“트래픽이 없으면 요금도 없겠지”라고 생각하기 쉬운데, 존재 자체로 시간당 요금이 붙는 서비스들이 있습니다. 테스트로 만들어보고 지우지 않으면 그대로 청구됩니다.
| 서비스 | 과금 방식 | 주의 |
|---|---|---|
| Azure Firewall | 시간당 고정 + 데이터 | 소규모 구독에서 최대 비용 항목이 되기 쉬움 |
| Application Gateway | 시간당 + 용량 단위 | WAF 켜면 추가 |
| VPN / ExpressRoute 게이트웨이 | 시간당 고정 | SKU에 따라 차이 큼 |
| Azure Bastion | 시간당 고정 + 데이터 | VM 1~2대면 배보다 배꼽 |
| 공용 IP (Standard) | 시간당 | 연결 안 돼 있어도 청구 |
| 관리 디스크 | 프로비저닝 용량 | 사용량이 아니라 할당 용량 기준 |
| Log Analytics | 수집량 + 보존 | 진단 설정 켜면 급증 |
마지막 두 개를 특히 강조하고 싶습니다.
관리 디스크는 프로비저닝 용량으로 과금됩니다. 1TB 디스크를 만들고 10GB만 써도 1TB 값을 냅니다. 그리고 Premium SSD는 크기 구간(P10, P20, P30…)마다 정가가 정해져 있어서, 513GB를 만들면 1TB 구간(P30) 요금이 나갑니다. 구간 경계를 살짝 넘기지 않도록 크기를 잡는 게 요령입니다.
Log Analytics는 문제 조사한다고 진단 설정에서 로그 카테고리를 전부 켜두고 잊는 경우가 흔합니다. 수집량이 그대로 요금입니다.
// 어떤 테이블이 용량을 먹는지
Usage
| where TimeGenerated > ago(30d)
| summarize GB = round(sum(Quantity)/1000, 2) by DataType
| sort by GB desc
| take 10
지금 구독에 이런 고정비 리소스가 있는지 한 번 훑어보세요.
# 유휴 공용 IP (아무데도 연결 안 됨)
az network public-ip list \
--query "[?ipConfiguration==null].{name:name, rg:resourceGroup, sku:sku.name}" -o table
# 연결 안 된 디스크
az disk list \
--query "[?diskState=='Unattached'].{name:name, rg:resourceGroup, GB:diskSizeGb, sku:sku.name}" -o table
# 고정비 게이트웨이류
az network vnet-gateway list --query "[].{name:name, rg:resourceGroup, sku:sku.name}" -o table
az network firewall list --query "[].{name:name, rg:resourceGroup}" -o table
점검 4 — 개발 환경 자동 종료
가장 효과가 큰 절감 조치인데 설정이 가장 간단합니다. 개발 VM을 평일 9시~19시만 켜면 가동률이 730시간 중 220시간, 약 30%가 됩니다.
이건 3년 예약 최대 할인(약 72%)보다 큰 절감입니다. 그리고 약정과 달리 위험이 없습니다.
# VM 자동 종료 (한국 시간 19시)
az vm auto-shutdown \
--name vm-api-dev-krc-01 -g rg-dev-app \
--time 1900 \
--email "oliver@example.com"
자동 시작은 이 기능에 없습니다. 필요하면 Azure Automation이나 Logic Apps로 별도 구성하거나, 아침에 필요할 때 직접 켜는 방식도 나쁘지 않습니다. 켜는 데 1~2분이면 됩니다.
여기서 주의할 점 하나. 자동 종료와 백업 시각이 겹치면 백업이 실패합니다. 백업을 종료 시각보다 앞으로 옮겨두세요. 관련 내용은 백업 전략 글에 정리했습니다.
정책으로 강제하고 싶다면 비운영 구독에 VM 크기 제한도 함께 거는 것을 권합니다. 개발 환경에서 실수로 대형 VM을 띄우는 사고를 막습니다. 설정 방법은 태그·Policy 거버넌스에 있습니다.
점검 5 — 무료 평가판 이후 요금제 확인
마지막은 계정 설정입니다. Azure 무료 계정은 30일간 크레딧을 주고, 12개월 무료 서비스가 별도로 있습니다. 이 둘의 종료 시점이 다르고, 지나면 자동으로 종량제로 넘어갑니다.
여기서 흔한 상황이 있습니다. 무료 크레딧으로 이것저것 만들어보고 지우지 않은 상태로 크레딧이 소진되면, 그때부터 그 리소스들이 전부 실제 요금으로 청구되기 시작합니다.
# 현재 구독의 요금제 확인
az account show --query "{name:name, state:state, tenantId:tenantId}" -o table
# 이번 달 리소스별 비용 상위 20건
az costmanagement query \
--type ActualCost \
--scope "/subscriptions/$(az account show --query id -o tsv)" \
--timeframe MonthToDate \
--dataset-aggregation '{"totalCost":{"name":"Cost","function":"Sum"}}' \
--dataset-grouping name=ResourceId type=Dimension \
-o json | head -60
평가판 기간이 끝나기 일주일 전에 리소스를 한 번 정리하는 일정을 잡아두시길 권합니다.
태그를 지금 붙여두면 나중이 편하다
첫 주 점검에 하나 더 넣을 만한 것이 있습니다. 리소스 그룹에 태그를 다는 것입니다. 지금은 리소스가 몇 개 없어서 필요를 못 느끼지만, 나중에 비용을 나눠 봐야 할 때 태그가 없으면 방법이 없습니다.
중요한 건 태그는 소급 적용되지 않는다는 점입니다. 오늘 태그를 달아도 지난달 청구 데이터에는 반영되지 않습니다. 그래서 빨리 달수록 좋습니다.
# 리소스 그룹에 태그 (하위 리소스는 상속 정책으로 자동 처리 가능)
az group update --name rg-dev-app \
--tags Owner=oliver@example.com Environment=dev Project=api-renewal
# 태그별 비용 조회
az costmanagement query \
--type ActualCost \
--scope "/subscriptions/$(az account show --query id -o tsv)" \
--timeframe MonthToDate \
--dataset-aggregation '{"totalCost":{"name":"Cost","function":"Sum"}}' \
--dataset-grouping name=Environment type=TagKey \
-o table
태그 4개(Owner, Environment, CostCenter, Project)면 충분합니다. 많으면 아무도 안 채웁니다.
비용 데이터를 누가 볼 수 있는지도 확인
의외로 막히는 지점입니다. 구독의 소유자(Owner)나 기여자(Contributor) 권한이 있어도 비용 데이터는 안 보일 수 있습니다. 청구 계정 수준의 설정이 별도로 있기 때문입니다.
포털의 비용 관리 → 비용 분석에 들어갔는데 데이터가 비어 있다면 두 가지를 확인하세요.
- Cost Management Reader 역할이 부여되어 있는가
- EA·MCA 계약이라면 청구 계정에서 “부서 관리자에게 요금 표시” 옵션이 켜져 있는가
az role assignment create \
--assignee "user@example.com" \
--role "Cost Management Reader" \
--scope "/subscriptions/$(az account show --query id -o tsv)"
비용을 관리해야 할 사람이 데이터를 못 보는 상태로 몇 달이 지나는 경우가 있습니다. 첫 주에 확인해두면 좋습니다.
30분 점검 체크리스트
- 예산 알림을 예측 100% + 실제 80%로 건다
Stopped상태 VM을 찾아 할당 취소한다- 유휴 공용 IP·미연결 디스크·게이트웨이류를 조회해 정리한다
- 개발 VM에 자동 종료를 건다 (백업 시각과 겹치지 않게)
- Log Analytics 수집량을 확인하고 불필요한 진단 설정을 끈다
- 무료 평가판이라면 종료 시점을 달력에 표시한다
여섯 개 다 해도 30분입니다. 첫 청구서를 받고 놀란 뒤에 하는 것보다 훨씬 낫습니다.
이미 비용이 늘어난 상태라면 Cost Analysis로 원인 찾는 법부터 보세요. 구조를 잡는 단계라면 미니멀 Azure 랜딩 존이, 안정화 후 약정을 검토한다면 Reserved·Savings Plan·Spot 선택 기준이 이어집니다.
※ 이 글의 명령들은 Azure CLI 최신 버전 기준입니다. az upgrade로 버전을 맞춘 뒤 실행하세요. 일부 명령은 구독 권한(소유자 또는 기여자)이 필요합니다.