# Как настроить ИИ-мониторинг расходов на облако: AWS, Azure и GCP

> AI Release · @ai_release1 · https://ai-release.org/guides/kak-nastroit-ii-monitoring-rashodov-na-oblako-aws-azure.html

## TL;DR

- Время выполнения: 60–90 минут (при готовых правах в трёх облаках)
- Уровень сложности: Intermediate
- Понадобится: AWS CLI v2, Azure CLI, Google Cloud CLI (`gcloud`), доступ к биллинг-аккаунтам AWS/Azure/GCP, S3-бакет или GCS-бакет для экспортов, проект BigQuery, любой HTTP-клиент к LLM API (endpoint и имя модели — сверьтесь с документацией вашего провайдера), Python 3.x

Идея схемы: нативные инструменты каждого облака собирают факты о расходах → складываем их в одно хранилище → LLM раз в сутки читает выгрузку и объясняет аномалии человеческим языком → алерт уходит в чат. ИИ здесь не заменяет биллинг, а интерпретирует его.

| Облако | Бюджеты | Аномалии | Детальная выгрузка |
|---|---|---|---|
| AWS | AWS Budgets | Cost Anomaly Detection | Cost and Usage Report (CUR) → S3 |
| Azure | Azure Budgets (Cost Management) | Cost Anomaly Alerts в Cost Management | Exports в Storage Account |
| GCP | Cloud Billing Budgets | оповещения бюджета по порогам | BigQuery Billing Export |

## Требования и подготовка
![Иллюстрация к разделу «Требования и подготовка»](https://ai-release.org/guides/img/kak-nastroit-ii-monitoring-rashodov-na-oblako-aws-azure-ru-1.jpg?v=1792778746)

- ОС: Linux/macOS или Windows с WSL2. Команды ниже — для bash.
- AWS CLI v2 с профилем, у которого есть права на `budgets:*`, `ce:*`, `s3:*`, `cur:*`.
- Azure CLI, вход через `az login`, роль Contributor (или custom-роль) на подписке.
- Google Cloud CLI, `gcloud auth login`, роль Billing Account Administrator на биллинг-аккаунте.
- Один S3-бакет и один GCS-бакет под выгрузки — имена уникальны в глобальном пространстве.
- Переменные окружения для LLM: `LLM_API_URL`, `LLM_API_KEY`, `LLM_MODEL`. Точные значения URL и имени модели сверьте с документацией провайдера — здесь они не фиксируются.

**Что сделать:** проверить, что CLI видят нужные аккаунты.

**Зачем:** 80% ошибок настройки — работа не в том аккаунте/подписке, где лежат расходы.

**Как проверить:**

```bash
aws sts get-caller-identity
az account show --query "{name:name, id:id}" -o table
gcloud config list --format="value(core.account,core.project)"
```

**Пример:** `aws sts get-caller-identity` печатает `"Account": "111122223333"` — этот номер понадобится в `--account-id`. Если номер не тот, что в консоли Billing, переключите профиль: `export AWS_PROFILE=<профиль>`.

Чек-лист перед стартом:

- [ ] Известны ID: AWS account ID, Azure subscription ID, GCP billing account ID
- [ ] Созданы бакеты для выгрузок
- [ ] Есть LLM-ключ и лимит расходов на сам API (см. [как настроить лимиты расходов и бюджеты для ИИ-агентов](https://ai-release.org/guides/kak-nastroit-limity-rashodov-i-byudzhety-dlya-ii-agento.html?utm_source=tg&utm_medium=channel&utm_campaign=guide_inline&utm_content=guide_to_guide))
- [ ] Есть чат/канал, куда бот будет писать алерты

## Шаг 1. Включить нативные бюджеты и экспорты в AWS, Azure и GCP
![Иллюстрация к разделу «Шаг 1. Включить нативные бюджеты и экспорты в AWS, Azure и GCP»](https://ai-release.org/guides/img/kak-nastroit-ii-monitoring-rashodov-na-oblako-aws-azure-ru-2.jpg?v=1792778771)

**Что сделать:** создать по одному бюджету с порогами и включить детальные выгрузки.

**Зачем:** LLM не умеет опрашивать биллинг «напрямую» — ему нужны файлы с данными. Нативные бюджеты дают быстрый алерт, выгрузки — материал для анализа.

**Как проверить:** после каждого блока — команда `list`/`describe` возвращает созданный объект.

AWS: бюджет с двумя порогами (50% и 100% от суммы, которую вы задаёте сами).

```bash
cat > budget.json <<'JSON'
{
  "BudgetName": "monthly-guard",
  "BudgetLimit": {"Amount": "500", "Unit": "USD"},
  "TimeUnit": "MONTHLY",
  "BudgetType": "COST"
}
JSON

cat > notifications.json <<'JSON'
[
  {"Notification": {"NotificationType": "ACTUAL", "ComparisonOperator": "GREATER_THAN", "Threshold": 50, "ThresholdType": "PERCENTAGE"},
   "Subscribers": [{"SubscriptionType": "EMAIL", "Address": "<ваш-email>"}]},
  {"Notification": {"NotificationType": "ACTUAL", "ComparisonOperator": "GREATER_THAN", "Threshold": 100, "ThresholdType": "PERCENTAGE"},
   "Subscribers": [{"SubscriptionType": "EMAIL", "Address": "<ваш-email>"}]}
]
JSON

aws budgets create-budget --account-id 111122223333 \
  --budget file://budget.json \
  --notifications-with-subscribers file://notifications.json
```

Проверка: `aws budgets describe-budgets --account-id 111122223333` — в выводе `"BudgetName": "monthly-guard"`. Сумма `500` — пример, подставьте свой месячный лимит.

Детальная выгрузка AWS (CUR) в S3:

```bash
aws cur put-report-definition --report-definition '{
  "ReportName": "cur-daily",
  "TimeUnit": "DAILY",
  "Format": "Parquet",
  "Compression": "Parquet",
  "S3Bucket": "<ваш-бакет>",
  "S3Prefix": "cur",
  "S3Region": "eu-central-1",
  "AdditionalArtifacts": ["ATHENA"],
  "RefreshClosedReports": true,
  "ReportVersioning": "OVERWRITE_REPORT"
}'
```

Проверка: `aws cur describe-report-definitions` — статус отчёта. Первая выгрузка появляется не мгновенно; точный SLA и текст bucket policy для бакета сверьте с документацией AWS.

Azure: бюджет на подписке.

```bash
az consumption budget create \
  --budget-name monthly-guard \
  --amount 500 \
  --time-grain Monthly \
  --start-date 2025-01-01 --end-date 2026-01-01 \
  --category Cost
```

Проверка: `az consumption budget list -o table` — строка `monthly-guard`. Набор флагов в разных версиях Azure CLI отличается — при ошибке разбора аргументов сверьтесь с `az consumption budget create -h`.

GCP: бюджет с порогами и публикацией в Pub/Sub (топик нужен, чтобы алерт можно было обработать кодом, а не только письмом).

```bash
gcloud pubsub topics create budget-alerts
gcloud billing budgets create \
  --billing-account=<BILLING_ACCOUNT_ID> \
  --display-name=monthly-guard \
  --budget-amount=500USD \
  --threshold-rule=percent=0.5 \
  --threshold-rule=percent=1.0 \
  --pubsub-topic=projects/<ваш-проект>/topics/budget-alerts
```

Проверка: `gcloud billing budgets list --billing-account=<BILLING_ACCOUNT_ID>` — бюджет в списке. Почему это работает: бюджеты — это правила на стороне биллинга, они срабатывают независимо от вашего кода, поэтому не «замолчат», если упадёт скрипт.

## Шаг 2. Свести данные в BigQuery и подключить ИИ-разбор

**Что сделать:** включить экспорт биллинга GCP в BigQuery, а сводку по трём облакам раз в сутки отправлять в LLM.

**Зачем:** BigQuery — самый простой «общий стол» для агрегатов; из AWS и Azure забираем уже готовые итоги по сервисам и складываем их туда же или в локальный JSON.

**Как проверить:** таблица в BigQuery существует и по ней проходит запрос за вчера.

В консоли GCP: Billing → Billing export → BigQuery export → выбрать датасет. После включения появится таблица вида `<проект>.<датасет>.gcp_billing_export_v1_<BILLING_ACCOUNT_ID>`.

```bash
bq query --use_legacy_sql=false '
SELECT service.description AS service, ROUND(SUM(cost), 2) AS cost_usd
FROM `<проект>.<датасет>.gcp_billing_export_v1_<BILLING_ACCOUNT_ID>`
WHERE invoice.month = FORMAT_DATE("%Y%m", CURRENT_DATE())
GROUP BY service
ORDER BY cost_usd DESC
LIMIT 10'
```

**Пример вывода:** строки `Compute Engine | 214.37`, `Cloud Storage | 31.02` — это топ сервисов за текущий месяц.

Теперь сборщик сводки. Он берёт AWS-итоги через Cost Explorer, Azure — через CLI, GCP — через `bq`, склеивает в один JSON и отправляет в LLM:

```python
import json, os, subprocess, urllib.request

def aws_totals():
    out = subprocess.check_output([
        "aws", "ce", "get-cost-and-usage",
        "--time-period", "Start=2025-01-01,End=2025-02-01",
        "--granularity", "MONTHLY",
        "--metrics", "UnblendedCost",
        "--group-by", "Type=DIMENSION,Key=SERVICE"], text=True)
    return json.loads(out)

def azure_totals():
    out = subprocess.check_output([
        "az", "consumption", "usage", "list", "--top", "50", "-o", "json"], text=True)
    return json.loads(out)

payload = {"aws": aws_totals(), "azure": azure_totals()}
prompt = ("Ты аналитик облачных расходов. Найди в данных три самых дорогих сервиса, "
          "укажи долю каждого в процентах и одну гипотезу причины роста. "
          "Если данных для вывода не хватает — так и напиши. Данные: "
          + json.dumps(payload)[:20000])

req = urllib.request.Request(
    os.environ["LLM_API_URL"],
    data=json.dumps({"model": os.environ["LLM_MODEL"],
                     "messages": [{"role": "user", "content": prompt}]}).encode(),
    headers={"Content-Type": "application/json",
             "Authorization": "Bearer " + os.environ["LLM_API_KEY"]})

print(json.loads(urllib.request.urlopen(req).read())["choices"][0]["message"]["content"])
```

Формат тела запроса у разных провайдеров отличается — поля `messages`/`choices` сверьте с документацией. Обрезка `[:20000]` символов нужна, чтобы не упереться в лимит контекста и не платить за мегабайты JSON.

Почему это работает: LLM не считает деньги — она получает уже посчитанные суммы и ищет в них закономерности. Если суммы неверны, ответ тоже будет неверным, поэтому проверка из Шага 3 обязательна.

## Шаг 3. Проверка работы и финальный результат
![Иллюстрация к разделу «Шаг 3. Проверка работы и финальный результат»](https://ai-release.org/guides/img/kak-nastroit-ii-monitoring-rashodov-na-oblako-aws-azure-ru-3.jpg?v=1792778786)

**Что сделать:** прогнать сборщик и убедиться, что алерт доходит до чата.

**Зачем:** «настроено» и «работает» — разные вещи; нужен наблюдаемый результат.

**Как проверить:**

```bash
python collect.py > report.json 2>err.log
head -c 400 report.json
```

Успех выглядит так: в `report.json` три строки с суммами по облакам и абзац текста от модели, где названы самые дорогие сервисы. `err.log` пуст.

Дальше проверяем путь алерта. Простейший способ доставки в Telegram — бот, тот же приём, что в гайде [как настроить мониторинг VPS через Telegram-бота с Mini App](https://ai-release.org/guides/kak-nastroit-monitoring-vps-cherez-telegram-bota-s-mini.html?utm_source=tg&utm_medium=channel&utm_campaign=guide_inline&utm_content=guide_to_guide).

```bash
curl -s -X POST "https://api.telegram.org/bot<ТОКЕН>/sendMessage" \
  -d chat_id=<CHAT_ID> \
  --data-urlencode text="$(python -c 'import json;print(json.load(open("report.json"))["summary"])')"
```

Проверка: сообщение появилось в чате в течение нескольких секунд. Если появилось — цепочка «выгрузка → сводка → LLM → чат» замкнута.

Финальный результат: раз в сутки приходит сообщение с тремя самыми дорогими сервисами и гипотезой роста, а при превышении 50% и 100% бюджета срабатывает нативный алерт облака. Расписание ставим через cron:

```bash
0 7 * * * cd /opt/costbot && /usr/bin/python3 collect.py >> run.log 2>&1
```

## Возможные проблемы и решения

**Проблема. Бюджет AWS не создаётся**

- Ошибка: `An error occurred (AccessDeniedException) when calling the CreateBudget operation: User: arn:aws:iam::111122223333:user/bot is not authorized to perform: budgets:CreateBudget`
- Причина: у IAM-пользователя нет прав на сервис Budgets, даже если есть доступ к Billing в консоли.
- Решение: добавить политику и повторить команду.

```bash
aws iam attach-user-policy --user-name bot \
  --policy-arn arn:aws:iam::aws:policy/AWSBudgetsActionsWithAWSResourceControlAccess
```

Точный список действий, если нужна своя политика, сверьте с документацией AWS.

**Проблема. Azure отклоняет создание бюджета**

- Ошибка: `(InsufficientPermissions) The client '<guid>' with object id '<guid>' does not have authorization to perform action 'Microsoft.Consumption/budgets/write' over scope '/subscriptions/<id>'`
- Причина: у субъекта нет роли на запись бюджетов в этой подписке.
- Решение: выдать роль и повторить.

```bash
az role assignment create --assignee <object-id> \
  --role "Cost Management Contributor" \
  --scope /subscriptions/<SUBSCRIPTION_ID>
```

**Проблема. GCP не принимает бюджет с Pub/Sub**

- Ошибка: `ERROR: (gcloud.billing.budgets.create) PERMISSION_DENIED: The caller does not have permission`
- Причина: аккаунт не имеет прав на биллинг-аккаунте, либо топик создан в другом проекте.
- Решение: проверить аккаунт и выдать роль на биллинг-аккаунте, затем повторить создание бюджета.

```bash
gcloud auth list
gcloud billing accounts list
```

## Резюме и следующие шаги

Нативные бюджеты и выгрузки трёх облаков дают проверяемые цифры, а LLM превращает их в короткое объяснение раз в сутки. Дальше стоит связать ИИ-разбор с лимитами на сам API и добавить в отчёт разбивку по тегам проектов, чтобы аномалия сразу указывала на команду.

## FAQ

**Чем AWS Budgets отличается от Cost Anomaly Detection?**
Budgets сравнивает фактические расходы с заданным вами порогом, а Cost Anomaly Detection ищет отклонения от обычного поведения без ручного порога. Обычно включают оба: бюджет ловит общий перерасход, детектор — локальный всплеск по сервису. Точные настройки детектора сверьте с документацией AWS.

**Нужен ли отдельный платный сервис для ИИ-мониторинга облака?**
Нет. Схема из гайда собирается из нативных бюджетов, экспортов и обычного вызова LLM API. Платите только за токены и за хранение выгрузок.

**Можно ли получать алерты в Telegram, а не на email?**
Да. Нативные уведомления оставляем как страховку, а текст от модели отправляем ботом через `sendMessage`. Готовый рецепт бота — в связанном гайде по мониторингу VPS.

**Как часто обновляются данные о расходах?**
Зависит от облака и типа выгрузки: экспорт GCP в BigQuery и CUR в AWS обновляются не мгновенно, у Azure Exports тоже есть задержка. Для суточного отчёта это не критично, но точный интервал сверьте с документацией каждого провайдера.
