Контроль затрат: как не получить счёт-сюрприз

Почему облако внезапно выставляет счёт на 400 $ за пустой проект — и как сделать так, чтобы этого не случилось с вами.

Контроль затрат в облаке — это не «экономить», а знать заранее: за что платим, сколько это будет к концу месяца и что произойдёт, если забыть выключить.

Классическая история: студент поднял виртуальную машину «на посмотреть», закрыл ноутбук и уехал на каникулы. Через месяц приходит счёт. Машина честно работала все 720 часов, публичный IP исправно резервировался, диск существовал. Никто ничего не сломал — облако просто делало ровно то, о чём его попросили. В этом и суть модели pay-as-you-go: вы платите за существование ресурса, а не за пользу от него.

Хорошая новость: Azure даёт полный набор инструментов, чтобы такого не случилось. Плохая: по умолчанию ни один из них не включён.

Откуда вообще берётся счёт

Каждый ресурс привязан к одному или нескольким метрам (meters) — счётчикам вида «часы работы VM размера B2s в West Europe», «гигабайты в Blob Storage уровня Hot», «исходящий трафик за пределы региона». Раз в час платформа записывает показания. В конце периода показания умножаются на цену метра — это и есть счёт.

Из этого следуют неочевидные вещи:

  • VM тарифицируется по времени существования, а не по загрузке. Простаивающая на 1% машина стоит столько же, сколько загруженная на 100%.
  • Диск тарифицируется отдельно от VM. Удалили машину, а диск остался — платите за диск.
  • Входящий трафик бесплатен, исходящий — нет. Первые 100 ГБ в месяц идут бесплатно, дальше порядка 0,08–0,12 $ за гигабайт. Раздача видео с VM легко даёт счёт больше, чем сама VM.

У новой подписки есть подушка: кредит примерно 200 $ на 30 дней, 12 месяцев бесплатных лимитов на популярные сервисы и набор «always free» (например, 1 млн вызовов Azure Functions в месяц). Пока действует кредит, у подписки включён spending limit — при исчерпании ресурсы просто отключаются. Как только вы переходите на pay-as-you-go, этот предохранитель исчезает навсегда. Именно в этот момент и нужны бюджеты.

Калькулятор цен: считать до, а не после

Первый инструмент — Azure Pricing Calculator (azure.microsoft.com/pricing/calculator). Собираете конфигурацию, видите месячную сумму. Занимает пять минут и убивает половину сюрпризов: например, вы обнаруживаете, что Application Gateway стоит ~0,25 $ в час — это ~180 $ в месяц просто за то, что он есть.

Второй, менее известный, — Retail Prices API: цены Azure доступны публично, без авторизации, и их можно спрашивать из скрипта.

curl -s "https://prices.azure.com/api/retail/prices?\$filter=serviceName eq 'Virtual Machines' and armRegionName eq 'westeurope' and armSkuName eq 'Standard_B2s' and priceType eq 'Consumption'" \
  | python3 -m json.tool | head -30

А оценку счёта удобно прикинуть обычным калькулятором. Ниже — маленькая модель, которая показывает главное недоразумение новичков: выключенная машина стоит денег.

HOURS = 730      # часов в среднем месяце
WORK = 190       # ~9 часов в день x 21 рабочий день

vm = 0.0416      # Standard_B2s, Linux, $/час
disk = 0.0263    # управляемый диск 128 ГБ, $/час
ip = 0.005       # статический публичный IP, $/час

always_on = (vm + disk + ip) * HOURS
scheduled = vm * WORK + (disk + ip) * HOURS
idle = (disk + ip) * HOURS

print("VM работает 24/7:     ", round(always_on, 2), "$/мес")
print("VM по расписанию:     ", round(scheduled, 2), "$/мес")
print("Экономия:             ", round(always_on - scheduled, 2), "$/мес")
print("Платим за выключенную:", round(idle, 2), "$/мес")

Результат:

VM работает 24/7:      53.22 $/мес
VM по расписанию:      30.75 $/мес
Экономия:              22.46 $/мес
Платим за выключенную: 22.85 $/мес

Последняя строка — та самая, которую стоит запомнить. Даже полностью остановленная машина продолжает тянуть почти 23 $ в месяц за диск и зарезервированный IP. Экономия от расписания реальна, но не бесконечна: чтобы платить ноль, нужно удалить, а не выключить.

Бюджеты и алерты

Бюджет в Azure Cost Management — это правило «если расходы по этой подписке (или группе ресурсов) достигнут N, пришли письмо». Ставится за минуту и стоит ноль.

SUB=$(az account show --query id -o tsv)

az consumption budget create \
  --budget-name budget-learning \
  --amount 20 \
  --category Cost \
  --time-grain Monthly \
  --start-date 2026-08-01 \
  --end-date 2027-08-01 \
  --resource-group rg-shop-demo

# посмотреть, сколько уже потрачено за период
az consumption usage list --top 5 --query "[].{res:instanceName, cost:pretaxCost, cur:currency}" -o table

Дальше в портале к бюджету добавляются пороги уведомлений: 50 %, 80 %, 100 % от суммы. Практика такая: 80 % — письмо себе, 100 % — письмо себе и тимлиду.

Главное заблуждение о бюджетах: бюджет НЕ останавливает трату денег. Это будильник, а не автомат защиты. Достигли 100 % — вам напишут, ресурсы продолжат работать и продолжат тарифицироваться.

Если нужен именно «рубильник», его собирают руками: бюджет отправляет событие в Action Group, тот дёргает Azure Function или Automation Runbook, а тот уже делает az vm deallocate или удаляет группу ресурсов. Для учебной подписки это оправданный час работы.

Главные пожиратели денег

Практически все счета-сюрпризы состоят из одного и того же набора. Вот он, отсортированный по частоте:

ЧтоПорядок ценыПочему забывают
Забытая VM30–200 $/мес«Я же её остановил» — остановил в ОС, а не deallocate
Диски без хозяина5–40 $/мес каждыйУдалили VM, диски по умолчанию остались
Публичные IP (Standard)~3,6 $/мес каждыйТарифицируются даже неприсоединённые
Исходящий трафик0,08–0,12 $/ГБРаздача файлов/видео, бэкапы наружу
Application Gateway / NAT Gatewayот 30 до 200+ $/месСтоят по факту существования, не по нагрузке
Cosmos DB с provisioned RU/sот 25 $/мес и вверхПлатите за зарезервированную пропускную способность, даже при нуле запросов
Снимки и бэкапыкопятся месяцамиПро политику хранения никто не вспоминает
Log Analytics / App Insights~2,3 $/ГБ логовВключили verbose-логирование «на время отладки»

Отдельно про «я остановил VM». Остановка изнутри гостевой ОС (shutdown -h now) переводит машину в состояние Stopped, но вычислительные ресурсы за ней остаются зарезервированы — и счёт идёт. Освобождает их только deallocate:

az vm deallocate -g rg-lab -n vm-lab     # правильно: вычисления больше не тарифицируются
az vm stop       -g rg-lab -n vm-lab     # НЕ то же самое, если вызывать выключение внутри ОС

# найти диски-сироты (не подключены ни к одной VM)
az disk list --query "[?diskState=='Unattached'].{name:name, gb:diskSizeGb, rg:resourceGroup}" -o table

# найти публичные IP без владельца
az network public-ip list --query "[?ipConfiguration==null].{name:name, rg:resourceGroup}" -o table

Эти две команды стоит гонять раз в месяц. У любой команды, работающей с Azure дольше года, они что-нибудь да находят.

Автоотключение и гигиена

Самое дешёвое решение — не полагаться на память. Для учебных и dev-машин есть встроенное автовыключение по расписанию:

# гасить машину каждый день в 19:00 по указанной таймзоне
az vm auto-shutdown -g rg-lab -n vm-lab --time 1900 --email me@example.com

# проверить, что расписание включено
az vm auto-shutdown -g rg-lab -n vm-lab --query "{status:status, time:dailyRecurrence.time}" -o table

Второй приём — теги. Помечайте всё, что создаёте, владельцем и сроком жизни. Тогда «чей это ресурс и можно ли его снести» перестаёт быть детективом.

az group create -n rg-lab -l westeurope --tags owner=ivan purpose=learning delete-after=2026-08-01

# отчёт: что в подписке не помечено владельцем — кандидаты на разбор
az resource list --query "[?tags.owner==null].{name:name, type:type, rg:resourceGroup}" -o table

И третий, самый действенный для учёбы: одна группа ресурсов на один эксперимент. Тогда уборка — это az group delete --yes --no-wait, и вы не оставляете за собой хвостов вроде одинокого IP-адреса, который будет капать три года.

Как это работает

Данные о расходах не появляются мгновенно. Показания метров попадают в систему учёта пачками, и в Cost Management цифры отстают от реальности примерно на 8–24 часа. Практическое следствие важное: алерт бюджета приходит с задержкой. Если вы случайно подняли восемь машин серии D в пятницу вечером, письмо «превышено 100 %» может прийти в субботу днём — когда набежит уже вдвое больше.

Поэтому защита строится слоями: калькулятор до запуска (не создавать заведомо дорогое), бюджет с порогом 50 % (ранний сигнал, а не финальный), автоотключение (снижает цену ошибки), удаление группы после эксперимента (обнуляет). Ни один слой не заменяет остальные.

Ещё одна деталь: цены в калькуляторе и в счёте — розничные. Реальные скидки в Azure получают не за торг, а за обязательства: Reserved Instances (обещаете использовать VM год или три — минус 30–70 %) и Savings Plans. Для учебных подписок это неактуально, для рабочих — первое, куда смотрит финансовый отдел.

Частые ошибки

  • Считать, что бюджет остановит расходы. Не остановит. Бюджет уведомляет. Рубильник надо собирать самому.
  • Путать stop и deallocate. Машина, выключенная изнутри ОС, продолжает тарифицироваться как работающая.
  • Удалять VM без дисков. Диски переживают машину и молча тянут деньги годами.
  • Считать неиспользуемое бесплатным. Публичный IP, зарезервированные RU/s в Cosmos DB, Application Gateway — платные по факту существования.
  • Тестировать на «взрослых» SKU. Разница между B-серией и D-серией на учебной задаче не видна, а в счёте — в разы.
  • Не ставить бюджет вообще. Пять минут работы, которые страхуют от трёхзначного счёта. Это первое, что делают в новой подписке.

Итоги

  • Облако тарифицирует существование ресурса, а не пользу от него: простаивающая машина стоит как работающая.
  • Бюджет — это будильник, а не автомат защиты. Ставьте пороги 50/80/100 % и не рассчитывайте, что расходы остановятся сами.
  • deallocate ≠ выключение из гостевой ОС. Только deallocate снимает плату за вычисления — но не за диск и IP.
  • Регулярно ищите сирот: неприсоединённые диски и публичные IP. Две команды az — и минус десятки долларов в месяц.
  • Для учёбы: один эксперимент — одна группа ресурсов, автоотключение на VM, удаление группы в тот же день. Тогда счёт-сюрприз становится невозможен.
Проверьте себя
1. Вы поставили бюджет 20 $ в месяц с уведомлением на 100 %. Расходы дошли до 20 $. Что произойдёт?
AAzure автоматически остановит все ресурсы в подписке
BПридёт уведомление, но ресурсы продолжат работать и тарифицироваться
CСоздание новых ресурсов заблокируется, старые продолжат работать
DПодписка перейдёт в режим только для чтения до конца месяца
2. Виртуальную машину выключили командой shutdown внутри гостевой ОС. Что со счётом?
AПлата прекращается полностью — машина выключена
BПлата за вычисления идёт как за работающую машину; освобождает ресурсы только deallocate
CПлата уменьшается вдвое, это тариф «простой»
DПлата идёт только за диск, вычисления не тарифицируются