
Закрытие периода. Вы открываете инвойс: видите счет за облако и ловите приступ паники от увиденной цифры. Обещали же, что будет меньше, почему так много?
Дальше начинается охота на ведьм: кто-то из инженеров поднял лишнее, кто-то забыл выключить виртуалку. Может показаться, что это единичный случай, аномалия у одной-двух компаний. А мы, как провайдер, скажем: это повторяющийся паттерн.
Меня зовут Александр Либкинд, я руковожу направлением FinOps в Cloud.ru. В этой статье я расскажу, что мы реально видим в биллинге и тикетах поддержки, какие ошибки повторяются у разных клиентов, как эти ошибки зависят от уровня зрелости компаний и что можно сделать без перестройки всей корпоративной культуры за год.
Сразу честно отвечу на вопрос, который наверняка возникнет у части читателей: зачем облачному провайдеру вообще учить клиентов тратить меньше денег у него же? У нас есть свой коммерческий интерес. Когда у клиента в облаке бардак, ресурсы крутятся вхолостую, а счет растет без причины, то наше облако для него выглядит дорогим. А если облако кажется дорогим, то клиент уходит к конкурентам или возвращается в on-premise. Поэтому в том, чтобы клиент платил адекватную цену за то, что реально решает его задачи, мы заинтересованы не меньше, чем сам клиент.
Уровни зрелости FinOps: где вы находитесь
Для начала определимся с тем, на каком уровне находится ваша компания.
Я выделил четыре уровня:
Уровень 0 (Хаос). Приходит счет за прошлый период, начинается сверка и паника. Тегирование отсутствует. Вопрос «Почему так дорого?» остается без ответа.
Уровень 1 (Видимость). Внедряется базовое тегирование: владелец, команда, среда. Понятно, кто потратил, но непонятно, почему так много и как это все оптимизировать.
Уровень 2 (Инжиниринг и защита). Начинается выбор правильной архитектуры, настройка жестких лимитов, политик разметки, расписаний и автоскейлинга. Исключается человеческий фактор инженерными guardrails (защитниками).
Уровень 3 (Юнит-экономика). Стоимость облака привязана к бизнес-метрикам. Инженер смотрит не только на доступность сервиса, но и на объем мусорных трат своего проекта и стоимость единицы бизнеса.
Понимать свой уровень зрелости важно, и вот почему.
Если вы находитесь на уровне 0, бессмысленно обсуждать юнит-экономику или тонкую настройку потребления GPU. У вас сначала должен появиться фундамент: понятные владельцы ресурсов, прозрачность расходов и минимальная дисциплина в инфраструктуре.
Если вы на уровне 1, то уже можете видеть, кто тратит деньги. Но этого все еще недостаточно, чтобы управлять архитектурой. А вот уровень 2 — это уже переход от наблюдения к действию: именно там появляются возможности guardrails, лимиты, политики отключения, тирирование хранения (распределения данных — Storage Tiering) и защита от аномального расхода.
Уровень 3 — рубеж, где вы учитесь не путать экономию со зрелостью. Можно сократить счет за облако и одновременно ухудшить юнит-экономику бизнеса и/или надежность инфраструктуры. Надо не только уменьшать расходы, но и понимать, что в это время происходит со стоимостью заказа, транзакции или активного пользователя.
В этой статье мы сознательно большую часть времени проведем на уровне 2, то есть на уровне инженерии и guardrails. Именно на нем мы сможем сформулировать лучшие практики того, как ставить архитектурные ограничения и не допускать при этом перерасхода при ошибках и аномалиях.
А в конце доберемся до уровня 3 и поговорим о том, как все эти решения отражаются на юнит‑экономике.
Как покупать ресурсы под разный тип нагрузки

Вопрос один, а ответ для каждого — свой.
Одна из самых дорогих ошибок уровня 0 — держать и продакшен, и пиковую нагрузку, и тестовые среды в одной модели потребления.
На уровне 0 инфраструктура почти всегда работает в режиме on-demand. Вы покупаете 100% мощностей по запросу просто потому, что так привычно и удобно. Это действительно гибко, но при стабильной продакшен-нагрузке почти всегда будет вести к переплатам.
На уровне 1 логика меняется. Если ядро продакшена (базы данных, критичные Kubernetes-ноды и постоянно работающие сервисы) живет 24/7, то имеет смысл переводить его в модель со скидкой за гарантированное потребление. Логика такая: «Мы используем ваши мощности 2–3 года, а вы нам, может быть, дадите дисконт». Пиковую нагрузку при этом можно оставлять в более гибком режиме.
Для зрелых клиентов (уровни 2 и 3) правила выглядят так:
базовая, предсказуемая нагрузка покрывается скидками или долгосрочными моделями потребления;
пиковая нагрузка берется on-demand;
тестовые и dev-среды переводятся на более дешевые тарифы, включая прерываемые ВМ и варианты с гарантированной долей vCPU 10–30%.
О том, как работает гарантированная доля vCPU и почему это спасает бюджет тестовых сред, поговорим дальше.
Как ставить инженерные guardrails
Второй вопрос: как сделать так, чтобы сама архитектура не раздувала счет при любой ошибке, аномалии или мусорном трафике? Сейчас расскажу и даже частично покажу.
На уровне 0 вы надеетесь, что инженеры не ошибаются, а на уровне 2 проектируете архитектуру так, чтобы даже весомая ошибка не превратилась в огромный счет.
Разберем, как это работает, на трех типовых кейсах: Kubernetes, GPU и хранилища.
Kubernetes: где перерасход рождается автоматически
Как это может выглядеть на уровне 0: инженеры ставят requests = limits, закладывают ресурсы с запасом, а Cluster Autoscaler настраивают без верхнего предела. Любая ошибка в конфиге, нагрузочном тесте или DDoS-подобный всплеск превращает кластер в фабрику по печати новых нод.
Важный нюанс Cloud.ru: входящий и исходящий трафик в сервисе Evolution Compute временно не тарифицируется, поэтому при аномалиях вы не получите «счет за трафик» — вы заплатите только за вычислительные ресурсы (ВМ), которые автоматически создаст кластер под эту нагрузку. Именно их и нужно ограничивать.
А вот на уровне 2 появляются первые настоящие предохранители: HPA и Cluster Autoscaler, VPA в режиме рекомендаций и управление гарантированной долей vCPU.
HPA и Cluster Autoscaler
Для HPA (ограничение подов) задается жесткий maxReplicas, чтобы приложение не могло бесконтрольно разрастаться. А для node-пула — maxCount, чтобы даже при всплеске нагрузка упиралась в контролируемый потолок, а не в бездонный бюджет.
Уровень | Параметр | Что ограничивает |
HPA | maxReplicas | Количество подов приложения |
Node Pool | maxCount | Количество воркер-узлов |
Пример, как задать maxReplicas:
apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: my-app-hpa spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: my-app minReplicas: 2 maxReplicas: 7 # ← жесткий лимит metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 60
При аномальном всплеске HPA не создаст больше семи подов: приложение будет деградировать (очереди, таймауты), но ресурсы не разрастутся бесконтрольно и сберегут бюджет.
А так выглядит ограничение узлов:
{ "clusterId": "<your-cluster-id>", "name": "my-node-pool", "scalePolicy": { "autoScale": { "minCount": 2, "maxCount": 5, // ← жесткий лимит узлов "initialCount": 3 } }, "machineConfiguration": { "disk": {"typeName": "SSD", "size": 30}, "flavorId": "<flavor-id>" }, "networkConfiguration": { "nodesSubnetId": "<subnet-id>" } }
VPA в режиме рекомендаций
Следующий слой защиты — VPA. Он показывает завышенные requests и помогает устранить ложную нехватку ресурсов, из-за которой кластер добирает лишние узлы.
Но здесь важен нюанс модели оплаты. Например, в Managed Kubernetes вы платите не за requests подов как таковые, а за конфигурацию и количество узлов. Поэтому VPA уменьшает счет через цепочку: корректировка requests → более плотная упаковка нагрузки → возможность сократить node-пул или выбрать более дешевый флейвор.
Это принципиально: без понимания модели оплаты рекомендации VPA останутся просто цифрами в консоли.
VPA может поддерживать несколько режимов через updatePolicy.updateMode:
apiVersion: autoscaling.k8s.io/v1 kind: VerticalPodAutoscaler metadata: name: my-app-vpa spec: targetRef: apiVersion: apps/v1 kind: Deployment name: my-app updatePolicy: updateMode: "Off" # ← только рекомендации, не меняет поды
Режимы:
Auto — VPA сам пересоздает поды с новыми requests/limits.
Initial — устанавливает рекомендации только при первом запуске.
Off — только рекомендации, не трогает поды (то, что вам нужно).
Гарантированная доля vCPU
Еще один зрелый инструмент для dev- и тестовых сред — гарантированная доля vCPU. Есть несколько вариантов.
1. 100% (гарантированная доля vCPU — 1 : 1)
Полное физическое ядро, никакой конкуренции.
Аналогия: отдельная комната в отеле.
Когда использовать: для продакшена и баз данных (highload-приложений).
Цена: базовая.
2. 30% — стандартная переподписка (1 : 3)
Вы платите за один vCPU, но гарантированно получаете 30% процессорного времени физического ядра. Оставшееся время доступно, если хост не загружен, — но без гарантии.
Аналогия: комната, которую делят примерно три гостя (каждый гарантированно получает свою треть).
Когда использовать: CI/CD, командные стенды, тестовые нагрузки.
Цена: дешевле 100%-й цены.
3. 10% — максимальная переподписка (1 : 10)
Вы платите за один vCPU, но гарантированно получаете 10% процессорного времени. Физическое ядро может быть разделено примерно между 10 машинами.
Аналогия: хостел — много гостей, ресурс делится.
Когда использовать: dev, песочницы, легкие сервисы, боты.
Цена: самый дешевый вариант.
Перевод тестовых сред на 10–30% vCPU при сохранении 100% для продакшена — это классический ход, который мгновенно улучшает общую юнит-экономику проекта.
GPU и ML: где деньги сгорают быстрее, чем на обычных vCPU на простое

В GPU-сценариях классический уровень 0 выглядит так: дата-сайентист берет дорогую GPU-машину, запускает обучение, задача завершается ночью или в выходные, а инстанс продолжает работать еще несколько часов, просто потому что.
На уровне 2 уже не работает надежда на дисциплину команды, здесь подключается политика автоотключения. Инфраструктура умеет отслеживать утилизацию GPU и автоматически выключать ВМ при простое, поэтому перерасход перестает зависеть от человеческой памяти.
Например, это могут быть скрипты на уровне инфраструктуры (или оркестратора вроде Run:ai или Kubeflow), которые останавливают инстанс, если утилизация GPU — меньше 5% более 15 минут.
Раз уж разбираем на близких мне технологиях, объясню: в Cloud.ru встроенного idle shutdown по утилизации GPU нет, но для обычных GPU-ВМ это можно реализовать кастомно. Есть два сценария.
Сценарий 1. Обычные ВМ с GPU. Проще
Вы можете написать скрипт, который мониторит утилизацию GPU через метрики или nvidia-smi, а при простое более N минут вызывает API выключения ВМ.
Как это работает
API поддерживает включение и выключение ВМ:
# Выключение ВМ curl -X POST https://compute.api.cloud.ru/api/v1/vms/{vm_id}/set-power \ -H "Authorization: Bearer <token>" \ -H "Content-Type: application/json" \ -d '{"power_off": "power_off"}' # Включение ВМ curl -X POST https://compute.api.cloud.ru/api/v1/vms/{vm_id}/set-power \ -H "Authorization: Bearer <token>" \ -H "Content-Type: application/json" \ -d '{"power_on": "power_on"}'
Схематично выглядит так:
[ВМ с GPU] ← nvidia-smi/DCGM→ [Мониторинг-сервис] ↓ GPU < 5% > 15 мин? ↓ Да API Cloud.ru: power_off ↓ ВМ остановлена → не тарифицируется
Варианты реализации:
Python-скрипт на отдельном ВМ-контроллере (есть готовый пример в документации).
Cron + скрипт на самой GPU-ВМ, который проверяет утилизацию и вызывает API.
Grafana Alert → webhook → скрипт выключения (если мониторинг уже настроен).
Kubeflow или Run:ai — если используете эти платформы, у них есть встроенные idle-shutdown-политики.
Важные нюансы в качестве вспомогательного чек-листа:
Аспект | Детали |
Тарификация | Когда ВМ выключена, за вычислительные ресурсы вы не платите. Диск продолжает тарифицироваться |
Данные | Данные на диске сохраняются, при включении ВМ возвращается в прежнее состояние |
Запуск обратно | Либо по расписанию, либо вручную, либо по триггеру (например, новый job в очереди) |
GPU-ВМ | Работает так же, как и обычные ВМ: API не различает типы |
Сценарий 2. Kubernetes с GPU-нодами. Сложнее
В Managed Kubernetes нельзя остановить отдельный узел в node-пуле. Есть три доступных действия над группой узлов:
Изменить количество узлов (scale up/down).
Обновить группу узлов (rolling update).
Удалить группу целиком.
Но вот что можно сделать:
Подход | Как работает |
Scale down node pool | При простое уменьшить count до минимума через API. Но это влияет на все поды в пуле, а не только на GPU-нагрузки |
Отдельный node pool для GPU | Вынести GPU-ноды в отдельную группу. При простое масштабировать ее до 0, а при необходимости — обратно |
Taints + tolerations | Пометить GPU-ноды тейнтами, чтобы на них планировались только GPU-поды. Тогда scale down не затронет другие нагрузки |
Пример — GPU node pool с масштабированием:
// GPU-пул для ML-задач { "name": "gpu-ml-pool", "scalePolicy": { "autoScale": { "minCount": 0, "maxCount": 3, "initialCount": 0 } }, "machineConfiguration": { "flavorId": "<gpu-flavor-id>", "disk": {"typeName": "SSD", "size": 100} }, "taints": [ { "key": "nvidia.com/gpu", "value": "true", "effect": "NO_SCHEDULE" } ] }
При отсутствии GPU-задач пул сжимается до 0 узлов, здесь и появляется экономия.
Итог:
Обычные ВМ с GPU | Kubernetes с GPU | |
Idle shutdown | ✅ Скрипт + API power_off | ⚠️ Scale down node pool до 0 |
Встроенное решение | ❌ Нет | ❌ Нет |
Готовый пример | ✅ Python-скрипт в документации | Частично (Cluster Autoscaler) |
Возврат к работе | API power_on / расписание | Scale up при появлении задач |
MIG (Multi-Instance GPU)
Следующий шаг зрелости — не просто выключать простаивающие машины, а повышать утилизацию самого GPU. Для этого используется MIG: одна физическая A100 или H100 делится на несколько изолированных инстансов.
Есть две стратегии:
1. Single — одинаковые экземпляры.
Разделяет GPU на равные части. Например, all-1g.10gb — каждый инстанс получает одну вычислительную группу и 10 ГБ памяти:
kubectl label nodes <node-name> nvidia.com/mig.config=all-1g.10gb --overwrite
Для A100 40 ГБ создадим четыре экземпляра по 10 ГБ каждый.
2. Mixed — разные экземпляры.
Система сама определяет оптимальное распределение. Например, all-balanced:
kubectl label nodes <node-name> nvidia.com/mig.config=all-balanced --overwrite
Для A100 40 ГБ получится три экземпляра: один 2g.20gb и два 1g.10gb.
Важно честно проговорить, что MIG не делает карту дешевле по тарифу. Он делает ее эффективнее в использовании: на том же GPU можно запустить больше задач.
Поверх MIG можно включить Time-Slicing — виртуальное разделение каждого MIG-инстанса между несколькими подами. Это даст двухуровневую мультиплексацию:
Физическая A100 ├─ MIG 1g.10gb (инстанс 1) → 5 подов через Time-Slicing ├─ MIG 1g.10gb (инстанс 2) → 5 подов через Time-Slicing ├─ MIG 1g.10gb (инстанс 3) → 5 подов через Time-Slicing └─ MIG 1g.10gb (инстанс 4) → 5 подов через Time-Slicing
Итого: 1 A100 → 4 MIG → 20 подов
Для длинных обучений нужен еще один обязательный паттерн — чекпойнты. Если веса и состояние обучения регулярно сохраняются в устойчивое хранилище, то при остановке или пересоздании инстанса обучение можно продолжить с последней контрольной точки, а не начинать все заново.
Как это работает:
Обучение модели на GPU-ВМ ↓ Каждые N шагов/эпох → сохранение чекпойнта ↓ Чекпойнт пишется в Object Storage (S3) или на диск ↓ Если ВМ остановили/удалили → запускаете новую → восстанавливаете из последнего чекпойнта
Есть три варианта хранения чекпойнтов.
Вариант 1. Evolution Object Storage (S3) — рекомендуемый
import boto3 s3 = boto3.client('s3', endpoint_url='https://s3.cloud.ru', aws_access_key_id='<key_id>', aws_secret_access_key='<secret_id>') # Сохранение чекпойнта s3.upload_file('checkpoint_epoch_5.pt', 'my-model-bucket', 'checkpoints/epoch_5.pt') # Восстановление s3.download_file('my-model-bucket', 'checkpoints/epoch_5.pt', 'checkpoint_epoch_5.pt')
Плюсы:
Чекпойнты живы независимо от ВМ (даже если ВМ удалили).
Доступны из любой ВМ/кластера.
Автоматическая репликация, надежность.
Минусы:
Сеть: сохранение/загрузка по сети (медленнее, чем локальный диск).
Вариант 2. Локальный диск ВМ
# PyTorch-пример torch.save(model.state_dict(), '/data/checkpoints/epoch_5.pt') # Восстановление model.load_state_dict(torch.load('/data/checkpoints/epoch_5.pt'))
Плюсы:
Быстрая запись (локальный SSD).
Минусы:
Если ВМ удалили, то чекпойнты пропали (диск удаляется вместе с ВМ).
Нужно делать бэкап на S3 для безопасности.
Вариант 3. Kubernetes + S3 CSI Driver
Для Managed Kubernetes можно примонтировать бакет S3 прямо в под:
apiVersion: v1 kind: Pod metadata: name: training-pod spec: containers: - name: trainer volumeMounts: - name: s3-checkpoints mountPath: /checkpoints volumes: - name: s3-checkpoints persistentVolumeClaim: claimName: s3-pvc
Чекпойнты пишутся прямо в S3 как в обычную файловую систему.
Хранилище: почему дорогой SSD — это не универсальный ответ
На уровне 0 ошибка хранения звучит так: «Закинем все на быстрый SSD, так надежнее». В результате и базы, и логи, и бэкапы, и архивы, и статика оказываются в одном дорогом классе хранения, хотя требования к ним совершенно разные.
На уровне 2 появляется тирирование.
Базы данных и действительно горячие stateful-данные остаются на SSD.
Активная статика и резервные копии уезжают в Object Storage Standard.
Более редкие данные — в Cold. Долгосрочные архивы и compliance-нагрузка — в Ice.
┌─────────────────────────────────────────────────────┐ │ Приложение (ВМ/K8s) │ │ │ │ БД: Managed PostgreSQL на SSD │ │ Кеш: Managed Redis (in-memory) │ │ Горячие данные: PV cloudru-ssd (100Gi) │ └──────────────┬──────────────────────────────────────┘ │ ┌──────────┴──────────┐ │ │ ┌───▼──────┐ ┌───────▼──────────┐ │ S3 │ │ S3 Cold/Ice │ │ Standard │ │ (архивы) │ │ │ │ │ │ Статика │ │ Логи > 30 дней │ │ Бэкапы │ │ Архивы данных │ │ Медиа │ │ Compliance
Guardrails: квоты, бюджеты и защита от аномалий

Даже при хорошей архитектуре остаются ошибки в коде и сбойные автоскейлеры.
Поэтому FinOps уровня 2 — это еще и набор тормозов, которые не дают инфраструктуре расти бесконечно или сжигать бюджет без сигнала человеку.
Главные механики здесь — квоты и бюджеты.
Квоты
Квоты ограничивают создание новых ресурсов. Если лимит выбран, API возвращает Quota Exceeded, и новая ВМ, диск или кластер просто не создается. Так мы защищаемся от случайного запуска сотен ресурсов, но не останавливаем уже работающие инстансы.
Пример: если у вас квота на 10 ВМ, они будут крутиться и сжигать деньги 24/7, даже если вы исчерпали денежный бюджет. Квота ограничивает площадь инфраструктуры, но не время ее жизни или стоимость.
Бюджеты
Бюджеты решают другую задачу. Это финансовый стоп-кран: сначала вы получаете уведомления на порогах, например 50, 80 и 100%, а при исчерпании бюджета система может заблокировать дальнейшее потребление (если в настройках лимита выбрана данная опция — «Приостановка») и остановить рост счета.
Важно: хранилище (диски, Object Storage) и сетевые ресурсы (публичные IP) — это «тихие потребители», которые не могут остановится. Их можно только удалить. У некоторых провайдеров это происходит автоматически, например, через 30 дней.
Пример сценария: у вас квота — 10 ВМ и бюджет — 100 000 рублей. Вы запустили пять ВМ на мощных флейворах. Квота не нарушена — 5 из 10 использовано. Но если эти ВМ работают 24/7, бюджет сработает сам: сначала придет уведомление, затем ресурсы остановятся. Квота защищает от расползания инфраструктуры, а бюджет — от превышения стоимости.
Квоты и бюджеты часто путают, поэтому сделал небольшую табличку:
Категория | Квоты | Бюджеты |
Что ограничивают | Количество ресурсов | Стоимость потребления |
Как работают | Блокируют создание новых | Приостанавливают работающие |
Кто настраивает | Поддержка (для увеличения) | Самостоятельно |
Слабые места | Не останавливают уже запущенное | Не ограничивают количество |
Как связать инфраструктурные траты с метриками бизнеса
Третий вопрос — самый важный. Именно он наконец приводит нас на уровень 3.
До этого момента мы говорили о том, как тратить меньше и прозрачнее. А теперь зададим новый вопрос.
Стало ли лучше бизнесу оттого, что мы сократили облачный счет?
Простой пример — e-commerce. Команда жестко ограничила maxReplicas, увидела минус 20% к счету за облако и одновременно получила просадку конверсии в часы пик, потому что сервис перестал выдерживать нагрузку. Формально расходы сократились. Фактически юнит-экономика ухудшилась.
Именно поэтому зрелый финальный шаг к 3-му уровню — связать инфраструктурные расходы с бизнес-метриками: стоимостью заказа, транзакции, активного пользователя, inference-запроса или ML-эксперимента.
Пример. Инженеру мало увидеть, что потратили 600 рублей в час. Важно, что эти 600 рублей в час ушли на 10 000 транзакций. И тогда разговор об эффективности становится предметным.
На практике переход к уровню 3 выглядит так:
Собираем бизнес-метрики в той же системе мониторинга, где уже есть инфраструктурные (Prometheus, Grafana). Заказы, транзакции, активные пользователи — в виде временных рядов.
Строим дашборды юнит-экономики. Поверх стандартных графиков утилизации vCPU добавляем стоимость инфраструктуры за период, деленную на количество бизнес-событий за тот же период.
Внедряем алерты на юнит-метрики. Если стоимость транзакции выросла на 20% при стабильной нагрузке, это триггер для инженерного разбора, даже если общий счет не изменился.
Привязываем FinOps к планированию. Перед архитектурными изменениями оцениваем влияние не только на надежность, но и на юнит-экономику. Переход на более дешевый класс хранения не должен замедлить сервис настолько, чтобы увеличить стоимость транзакции.
Рекомендации, которые я описал выше, как правило, сокращают общий счет на величину до 30% — внутренние подсчеты. И важно, что основная экономия затрагивает не продакшен-нагрузку, а тестовые среды и неиспользуемые мощности — то, что не влияет негативно на юнит-экономику.
Вывод
Переход от поиска забытых дисков и виртуалок к управлению архитектурой, моделями потребления и юнит-экономикой — это и есть взросление FinOps. На раннем этапе почти всегда можно быстро срезать лишнее. Но это не настоящая зрелость.
Три шага, с которых можно начать уже сейчас:
Определите свой уровень зрелости. Если вы на уровне 0, внедрите тегирование владельцев и сред. Если на уровне 1, начните с тирирования хранения и лимитов на node-пулы. Инструменты уровней 2 и 3 не работают без фундамента.
Разделите нагрузку по моделям потребления. Продакшен — в долгосрочные модели со скидками, а dev и тестовые среды — на гарантированную долю vCPU 10–30%. Это дает быструю экономию без риска для продакшен-стабильности.
Начните считать юнит-экономику/метрики. Выберите одну ключевую метрику бизнеса (стоимость заказа, транзакции, пользователя) и начните отслеживать ее в связке с инфраструктурными расходами. Первый же квартал такой аналитики даст вам понимание, где экономия реальна, а где — иллюзия.
