Проекты и биллинг

Как устроена «коробка», в которую складывают ресурсы GCP, откуда берутся деньги на счёт и почему бюджет с оповещением не останавливает трату.

Проект (project) — основная единица изоляции в Google Cloud. Ресурсы, права доступа, квоты, включённые API и счёт — всё живёт внутри проекта. Ресурс не может существовать «просто в облаке», он всегда принадлежит какому-то проекту.

Зачем нужен проект

Проект — это одновременно четыре границы, и понимание этого экономит нервы:

  • Граница доступа. Права (IAM) выдаются на проект: дали человеку роль Editor в проекте shop-dev — в shop-prod он всё ещё никто.
  • Граница денег. Счёт разбивается по проектам, и это самый простой способ понять, куда ушли деньги.
  • Граница квот. Лимиты (сколько vCPU можно поднять, сколько запросов в минуту слать) считаются на проект.
  • Граница API. Каждый сервис нужно включить в конкретном проекте. Включили Compute Engine в одном — в соседнем он всё ещё выключен.

Практический вывод: не сваливайте всё в один проект. Классический минимум — три проекта: -dev, -staging, -prod. Тогда неудачный эксперимент физически не может уронить или обчистить прод.

У проекта три имени, и это важно

ИдентификаторПримерСвойства
Project name (имя)Магазин — продПросто подпись для людей. Можно менять, можно повторяться.
Project IDshop-prod-471203Глобально уникален на весь Google Cloud, задаётся один раз и навсегда. Именно его требуют CLI и API.
Project number759214883012Числовой ID, генерируется автоматически. Встречается в сообщениях об ошибках и в адресах сервисных аккаунтов.

Project ID нельзя переименовать — только удалить проект и создать новый. Поэтому не называйте проект test-1, если он потом станет продом. Разумная схема: компания-система-среда, например codechick-api-prod.

# создать проект с явным ID
gcloud projects create codechick-api-dev --name="CodeChick API — dev"

# посмотреть свои проекты
gcloud projects list

# сделать проект активным для всех последующих команд
gcloud config set project codechick-api-dev

Результат:

PROJECT_ID           NAME                  PROJECT_NUMBER
codechick-api-dev    CodeChick API — dev   759214883012
codechick-api-prod   CodeChick API — prod  412007765531

Иерархия: организация → папка → проект

Пока вы учитесь с личного Google-аккаунта, проекты просто лежат рядом, без родителя. Как только компания подключает Cloud Identity или Google Workspace, появляется корневой узел — организация (Organization), а между ней и проектами можно вставлять папки (Folders).

Organization: codechick.io
├── Folder: Платформа
│   ├── Project: codechick-api-dev
│   ├── Project: codechick-api-staging
│   └── Project: codechick-api-prod
└── Folder: Аналитика
    └── Project: codechick-bigquery

Смысл иерархии — в наследовании. Права и организационные политики (Org Policies), выданные на папку, автоматически действуют на все проекты внутри. Дали команде платформы роль на папку «Платформа» — она получила доступ ко всем трём проектам сразу и ничего не увидела в аналитике. А политика вроде «запретить создание ключей сервисных аккаунтов», навешенная на организацию, действует везде и её нельзя обойти внутри отдельного проекта.

Биллинг-аккаунт — отдельная сущность

Новички часто думают, что «оплата привязана к проекту». На самом деле Cloud Billing Account — самостоятельный объект: у него своя карта (или договор), свои администраторы и свои права. Отношение простое: один биллинг-аккаунт оплачивает много проектов, но у проекта в каждый момент времени ровно один биллинг-аккаунт.

# какие биллинг-аккаунты мне доступны
gcloud billing accounts list

# привязать проект к биллинг-аккаунту
gcloud billing projects link codechick-api-dev \
  --billing-account=01A2B3-C4D5E6-7F8G9H

# проверить, к чему привязан проект
gcloud billing projects describe codechick-api-dev

Проект без биллинга — «мёртвый»: почти любой сервис откажется работать с ошибкой вида The billing account for the owning project is disabled in state absent. Ровно поэтому многие тратят полдня на «загадочные» ошибки в свежесозданном проекте: он просто не привязан к деньгам.

Чтобы привязать проект, нужны права в двух местах сразу: роль на проекте (Project Owner или Billing-роль) и роль Billing Account User на самом биллинг-аккаунте. В компаниях биллингом обычно владеет финансовый отдел, и разработчику дают именно эту вторую роль.

Квоты: почему нельзя поднять сто машин

Квоты защищают и Google (от исчерпания мощностей и от ботнетов), и вас (от бесконечного счёта из-за бага в скрипте). Их два вида:

  • Rate quotas — ограничение скорости: например, столько-то запросов к API в минуту. Сбрасываются автоматически; если упёрлись, увидите 429 RESOURCE_EXHAUSTED.
  • Allocation quotas — ограничение количества: сколько vCPU можно держать в регионе, сколько внешних IP, сколько ТБ дисков. Не сбрасываются — освобождаются, когда вы удаляете ресурсы.

Новый проект (особенно на пробном периоде) получает очень скромные лимиты: например, GPU в нём обычно ноль, а число vCPU в регионе измеряется единицами. Это нормально. Повышение запрашивается в консоли, в разделе IAM & Admin → Quotas: выбираете нужную квоту, указываете новое значение и обоснование. Мелкие повышения часто одобряются автоматически за минуты, крупные — за пару дней.

# посмотреть квоты Compute Engine по текущему проекту
gcloud compute project-info describe --format="table(quotas.metric,quotas.limit,quotas.usage)"

Как это работает: бюджеты, метки и экспорт счёта

Самое опасное заблуждение звучит так: «я поставил бюджет 10 $, значит больше не спишут». Это не так. Бюджет в GCP — это оповещение, а не предохранитель. При достижении 50%, 90%, 100% порога вам приходит письмо, но ресурсы продолжают работать и тратить деньги.

Настоящий «стоп-кран» строится вручную: бюджет отправляет сообщение в Pub/Sub, его подхватывает Cloud Function и вызывает API, отвязывающий биллинг от проекта. Звучит сурово — потому что это и есть суровая мера: отвязка биллинга останавливает всё в проекте. Для учебного аккаунта достаточно бюджета с алертами и привычки удалять созданное.

Понять, куда именно уходят деньги, помогают labels (метки вида env=dev, team=backend) — они попадают в детализацию счёта. А для серьёзного анализа биллинг экспортируют в BigQuery и считают затраты обычным SQL.

Прикинуть счёт можно и до его получения. Вот прямолинейная арифметика того, во что обходится «забытая» лаборатория (цены условные, для порядка величин):

hours = 730  # часов в среднем месяце

vm_hour = 0.034            # $/час за e2-medium
disk_gb, disk_price = 10, 0.04   # 10 ГБ по $0.04 за ГБ в месяц
ip_hour = 0.010            # $/час за неиспользуемый статический IP

vm = vm_hour * hours
disk = disk_gb * disk_price
ip = ip_hour * hours
total = vm + disk + ip

print(f"Виртуальная машина e2-medium: {vm:7.2f} $")
print(f"Диск 10 ГБ (даже когда VM выключена): {disk:7.2f} $")
print(f"Статический IP, ни к чему не привязанный: {ip:7.2f} $")
print(f"ИТОГО за месяц: {total:7.2f} $")
print(f"Забыли на 3 месяца: {total * 3:.2f} $")

Результат:

Виртуальная машина e2-medium:   24.82 $
Диск 10 ГБ (даже когда VM выключена):    0.40 $
Статический IP, ни к чему не привязанный:    7.30 $
ИТОГО за месяц:   32.52 $
Забыли на 3 месяца: 97.56 $

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

И ещё одна приятная деталь про удаление: проект не исчезает мгновенно. Он переходит в состояние pending deletion и лежит там около 30 дней — за это время его можно восстановить командой gcloud projects undelete. А чтобы важный проект нельзя было снести случайно, на него вешают lien (замок):

gcloud resource-manager liens create \
  --project=codechick-api-prod \
  --restrictions=resourcemanager.projects.delete \
  --reason="Продакшен: удаление только через тимлида"

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

  • Бюджет считают предохранителем. Он только шлёт письма. Без Pub/Sub и функции-выключателя трата не остановится.
  • Один проект на всё. Дев, прод и эксперименты в одной коробке — значит, общий IAM, общие квоты и общий счёт, в котором ничего не разобрать.
  • Плохой project ID. Его нельзя изменить. my-project-12345 из туториала имеет привычку доживать до продакшена.
  • Забыли привязать биллинг и полдня ищут причину странных ошибок в свежем проекте.
  • Просят у поддержки квоту, вместо того чтобы навести порядок. Часто «кончились vCPU» означает не малую квоту, а десяток забытых машин.
  • Удалили проект и думают, что деньги перестали течь. Пока проект в pending deletion, часть ресурсов может ещё дотарифицироваться; и наоборот — «удалил случайно» лечится undelete, но только в течение месяца.
  • Дают роль Owner всем подряд. Owner в проекте с биллингом — это право потратить любые деньги. Начинающим хватает Editor, а чаще — точечных ролей.

Итоги

  • Проект — контейнер для ресурсов и одновременно граница прав, квот, включённых API и счёта.
  • Из трёх имён проекта в командах и API используется project ID; он глобально уникален и не меняется.
  • Иерархия организация → папка → проект даёт наследование прав и политик; у личного аккаунта организации просто нет.
  • Биллинг-аккаунт — отдельный объект: один платит за много проектов, у проекта он один. Без привязки проект почти не работает.
  • Квоты бывают на скорость (rate) и на количество (allocation); повышаются запросом из консоли.
  • Бюджет = оповещение, а не стоп-кран. Реальная защита — привычка удалять ресурсы, метки для разбора счёта и, при желании, автоматическая отвязка биллинга.
Проверьте себя
1. Вы настроили бюджет на 20 $ с оповещением на 100%. Скрипт по ошибке поднял дорогие машины и счёт дошёл до 20 $. Что произойдёт?
AGoogle остановит все ресурсы проекта и списание прекратится
BПридёт письмо-оповещение, но машины продолжат работать и тратить деньги
CПроект автоматически перейдёт в состояние pending deletion
DКарта будет заблокирована до подтверждения владельцем
2. Какое утверждение о связке проекта и биллинг-аккаунта верно?
AУ каждого проекта обязательно свой отдельный биллинг-аккаунт
BОдин биллинг-аккаунт может оплачивать много проектов, но у проекта он ровно один
CПроект может быть привязан сразу к нескольким биллинг-аккаунтам для распределения затрат
DБиллинг-аккаунт создаётся автоматически внутри проекта и не существует отдельно