Один и тот же backend в Azure можно запустить несколькими способами — от «дайте голую виртуальную машину, дальше я сам» до «вот функция, вызывайте её на событие». Выбор влияет на то, сколько эксплуатации ляжет на вас и сколько вы заплатите. Разберём варианты и как выбирать. Сеть и доступ, на которых всё стоит, — в основах Azure.

Functions код на событие App Service код или контейнер, веб-сервер у Azure Container Apps контейнер масштабируется до нуля AKS узлы ваши, управление у Azure Virtual Machines ОС, софт и обновления ваши

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

Обязательно

Virtual Machines

Нужно перенести сервис «как есть», с его службой Windows или systemd-юнитом, или поставить то, что в контейнер не заворачивается. Virtual Machines — виртуальные машины: вы получаете сервер с операционной системой и делаете с ним что хотите. Аналог EC2 в AWS.

Максимум контроля и максимум ответственности: вы сами ставите софт, настраиваете автозапуск, следите за диском и за обновлениями. Уточнение про обновления важное: дело не в том, что облако «умеет только железо». У Azure есть и автоматическая установка обновлений в гостевую систему, и отдельный сервис управления обновлениями по расписанию — просто ни то ни другое не включается само. Пока вы этого не настроили, машина стоит с тем, что на ней было, и ответственность остаётся на команде.

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

Azure Kubernetes Service (AKS)

Сервисов стало два десятка, у каждого свой выкат и свои реплики, и руками это уже не удержать. AKS — это Kubernetes, у которого управляющую часть (control plane) обслуживает Azure, а вы работаете с рабочими узлами и деплоите контейнеры. Аналог EKS в AWS.

Kubernetes берут, когда сервисов много и нужна оркестрация: автоперезапуск упавших контейнеров, раскатка без простоя, обнаружение сервисов, масштабирование по нагрузке. Для одного маленького сервиса это из пушки по воробьям. Общая механика кластера — в разделе Kubernetes.

Когда брать: несколько сервисов, команда знает Kubernetes, нужны единый выкат и переносимость.

Про деньги здесь есть нюанс, который легко пропустить при сравнении вариантов. Управляющая часть на бесплатном уровне действительно бесплатна, но идёт без обещаний по доступности: сломается — починят, а никаких гарантий и компенсаций за простой вам никто не давал. Гарантированная доступность API-сервера — это платный уровень (Standard): почасовая плата за сам кластер, ещё до всяких рабочих узлов. Для боевого кластера это не опция, а обязательная строка в смете.

Azure Container Apps

Сервис один, нагрузка приходит волнами, а платить за кластер ради него жалко. Container Apps — золотая середина: вы упаковываете сервис в контейнер, а облако само запускает его и масштабирует, вплоть до нуля при отсутствии обращений. Кластер держать не нужно. Из AWS ближе всего по нише App Runner: тоже «отдал контейнер — получил работающий сервис». ECS с Fargate решает похожую задачу, но это всё-таки оркестратор задач, и сам до нуля он не сжимается. Под капотом у Container Apps управляемый Kubernetes, но вы его не видите.

Когда брать: есть контейнер, поднимать ради него полноценный AKS не хочется, нагрузка неравномерная. Платите за фактическое потребление.

Чего не снимает: сервис должен быстро стартовать и переживать пересоздание (stateless).

Azure Functions

Раз в час обработать файл в хранилище или ответить на веб-хук — десять строк кода, ради которых заводить сервис смешно. Azure Functions — код без сервера: вы загружаете функцию (на C#, Java, Python, Node.js и др.), а облако вызывает её на событие — HTTP-запрос, сообщение в очереди, файл в хранилище, таймер. Аналог AWS Lambda. Подробно — в статье про serverless.

Когда брать: небольшие событийные обработчики, нерегулярная нагрузка, желание платить по факту. Два ограничения, из-за которых функции чаще всего и переезжают в контейнер. Первое — время выполнения: на тарифе потребления одна функция работает по умолчанию 5 минут, а поднять предел можно максимум до 10; долгую обработку туда не положишь. Второе — холодный старт: если функцию давно не звали, первый вызов ждёт, пока поднимется среда. Когда логика разрастается в полноценный сервис или упирается в эти два предела — сигнал переезжать в контейнер.

App Service

Отдельно стоит App Service — платформа для веб-приложений и API (PaaS): вы отдаёте код или контейнер, а Azure держит веб-сервер и обновляет его под вами. Одна деталь, которую пропускают: приложение живёт не в воздухе, а в плане обслуживания (App Service Plan) — это, по сути, выделенные под него машины, и от выбранного уровня плана зависит и цена, и что вообще доступно. Масштабирование само собой не включается: правила масштабирования настраивают в плане, и на младших уровнях их просто нет. Удобно для классических веб-приложений, где не нужен ни полный контроль машины, ни событийная модель функций.

Как выбрать

Простое правило по возрастанию контроля и эксплуатации:

  • Azure Functions — событийная склейка, редкие или нерегулярные вызовы, минимум забот.
  • App Service — обычное веб-приложение или API без возни с инфраструктурой.
  • Container Apps — есть контейнер, нагрузка неравномерная, кластер держать не хочется.
  • AKS — много сервисов, нужна оркестрация и единый выкат, команда знает k8s.
  • Virtual Machines — нужен полный контроль или перенос «как есть».

Частая ошибка новичка — начинать с AKS «потому что так делают все». Для одного-двух сервисов Container Apps или App Service проще, дешевле и надёжнее.

Холодный старт: чем платят за уход в ноль

«Масштабирует вплоть до нуля» — приятное свойство, у которого есть цена: первый запрос после простоя ждёт запуска.

Что происходит: когда экземпляров ноль, платформа должна выделить ресурсы, скачать образ, запустить процесс и дождаться готовности приложения. Для лёгкой функции это сотни миллисекунд; для образа контейнера с приложением на виртуальной машине языка — от единиц до десятков секунд, и основную часть занимает не платформа, а старт самого приложения.

Где это чувствуется и что делают:

  • Функции в плане потребления. Холодный старт есть, и лечится он либо планом с заранее прогретыми экземплярами (Premium с минимальным числом), либо переходом на другой вариант размещения. В плане потребления сокращают то, что можно: меньше зависимостей, меньше инициализации при старте.
  • Container Apps с минимумом ноль. То же самое: первый запрос платит за старт. Лечится минимальным числом экземпляров, равным единице — и тогда сервис перестаёт быть бесплатным в простое, зато отвечает сразу.
  • App Service на выделенном плане холодного старта не имеет (процесс работает постоянно), но имеет настройку, которая его создаёт: выключенная опция «всегда включено» у некоторых планов усыпляет приложение после простоя.

Практическая раскладка. Фоновая обработка — холодный старт неважен, уходите в ноль и экономьте. API, которым пользуются люди — либо держите минимум один экземпляр, либо измерьте, сколько занимает первый запрос, и решите, терпимо ли это. API с требованием к задержке — холодный старт неприемлем, берите выделенные ресурсы или прогретые экземпляры.

И способ, который недооценивают: сократить сам старт приложения. Секунды старта — это ваш код и ваши зависимости; собранный заранее образ, ленивая инициализация того, что не нужно сразу, и заранее прогретый кэш классов сокращают холодный старт вдвое без всяких изменений в тарифе.

Требование не хранить состояние: ко всем, а не к одному

Оговорка «приложение должно быть без состояния» стоит у Container Apps, а относится она ко всем вариантам, которые масштабируются или пересоздаются: к функциям, к App Service, к контейнерам в кластере. Формулировка одна: экземпляр может исчезнуть в любой момент, и всё, что лежало у него в памяти и на локальном диске, исчезнет вместе с ним.

Что именно надо вынести и куда — по видам:

  • Сессии пользователей — во внешний кэш или, лучше, в подписанный токен у клиента. Липкие сессии у балансировщика (ARR affinity у App Service) — временный обход, который ломается при каждом масштабировании.
  • Загруженные файлы — в хранилище объектов, а не на локальный диск. У App Service есть общая файловая система между экземплярами, и она медленная: как хранилище для пользовательских файлов её берут только в маленьких проектах.
  • Кэш вычислений — локальный можно, если он не обязателен; общий нужен всему, что должно быть одинаковым у всех экземпляров (счётчики, блокировки, ограничение частоты).
  • Фоновые задачи по расписанию — либо с блокировкой в общем хранилище, либо вынести из приложения (планировщик платформы, очередь с одним читателем). Иначе задача выполнится столько раз, сколько экземпляров.
  • Долгая работа — в очередь или таблицу заданий, чтобы потеря экземпляра означала задержку, а не потерю.

Проверка, которая ставит всё на место: погасите один экземпляр под нагрузкой и посмотрите, заметил ли это пользователь.

Где это применяется

Выбор способа запуска — одно из первых архитектурных решений, и оно тянет за собой остальное. Куда бы ни поехал сервис, он опирается на сеть и доступ (managed identity вместо ключей).

Дополнительно: при первом чтении можно пропустить

Глубже: ещё два варианта и один, который не стоит начинатьрасширенное

К перечисленным выше стоит добавить две вещи и одно предупреждение.

Azure Container Instances — самый короткий путь запустить одиночный контейнер: ни кластера, ни описания сервиса, ни балансировщика. Одна команда, контейнер работает, платите за секунды его работы.

az container create -g rg-tools -n importer --image acme.azurecr.io/importer:1.2 \
  --cpu 1 --memory 2 --restart-policy Never

Для чего годится: разовая задача, обработка по расписанию, инструмент, который надо запустить и забыть. Для чего нет: постоянный сервис (нет масштабирования и балансировки в привычном виде), сложные сети, всё, что требует нескольких экземпляров.

Azure Spring Apps — не начинайте. Это был отдельный сервис под приложения на Spring Boot, и его первым найдёт любой, кто ищет «как запустить Spring в Azure». Microsoft объявила о его сворачивании: новые возможности туда не приезжают, а поддержка заканчивается в 2028 году. Для нового приложения правильный ответ — Container Apps (или кластер Kubernetes, если он уже есть): те же задачи, живой сервис, обычный образ контейнера вместо специальной обвязки. Если сервис уже работает в Spring Apps, переезд — отдельная задача, и планировать её лучше заранее, а не в последний год поддержки.

Глубже: цена как ось выборарасширенное

Сравнение по контролю и эксплуатации не отвечает на вопрос, который задают в конце: а во что это обходится. Модели оплаты у четырёх вариантов принципиально разные, и от профиля нагрузки зависит, какой дешевле.

ВариантЗа что платятДешевле всего, когда
Виртуальные машиныза время работы машины, независимо от нагрузкинагрузка ровная и предсказуемая, можно взять обязательство на год
Кластер Kubernetesза узлы (то есть за машины) плюс, в части вариантов, за управлениесервисов много и они делят узлы
Container Appsза использованные ресурсы по секундам, с возможностью уйти в нольнагрузка неровная, есть простои
Functions в плане потребленияза гигабайт-секунды плюс число вызововвызовы редкие или пиковые
App Serviceза план (то есть за выделенные ресурсы), независимо от нагрузкиодин-два приложения с постоянной нагрузкой

Два практических вывода из таблицы. Постоянная ровная нагрузка почти всегда дешевле на выделенных ресурсах — машине, узле кластера или плане приложений, особенно с обязательством на год. И наоборот: сервис, который девять часов из десяти простаивает, дешевле на том, что умеет уходить в ноль — Container Apps или функции.

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

Отдельная статья расходов, о которой забывают: сеть и обвязка. Шлюз приложений, преобразование адресов для выхода в интернет, приватные точки входа, журналы — всё это в сумме сопоставимо со стоимостью самих вычислений. Разбор — в статье про затраты.

Глубже: как ходить в приватную базурасширенное

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

Виртуальная машина и кластер Kubernetes — просто: они сами стоят в сети, и вопрос закрыт группами безопасности.

Container Apps — среда создаётся в вашей сети (это выбирается при создании и потом не меняется): вы отдаёте ей отдельную подсеть, и дальше приложение видит приватные ресурсы как свои. Подсеть нужна с запасом по размеру, требования зависят от вида среды.

Functions и App Service — здесь и прячется неожиданность. По умолчанию они работают вне вашей сети, и до приватной базы не дотянутся. Лечится это интеграцией с виртуальной сетью: приложению выделяется отдельная делегированная подсеть, через которую идёт его исходящий трафик. Две вещи, которые надо знать: интеграция доступна не во всех тарифных планах (в самых дешёвых её нет, и это повод пересмотреть план), и подсеть под неё нужна отдельная, только для этого приложения.

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

Обратный путь — дать базе публичную точку входа и закрыть её правилами по адресам — работает и считается плохой практикой: адреса исходящего трафика у платформенных сервисов меняются, и правила приходится поддерживать списком целых диапазонов.

Коротко

  • Virtual Machines дают полный контроль и полную ответственность: софт, автозапуск, диск и обновления ваши; берут для переноса «как есть» и специфического окружения.
  • AKS обслуживает управляющую часть, узлы и обновления ваши; бесплатный уровень без гарантий доступности, а для одного сервиса это из пушки по воробьям.
  • Container Apps масштабируют контейнер вплоть до нуля и берут деньги по факту; сервис обязан быстро стартовать и переживать пересоздание.
  • Functions это код на событие с ограничением по времени выполнения и холодным стартом; App Service держит веб-сервер под вашим кодом за план; Container Instances для разовой задачи; Azure Spring Apps не начинают.
  • Порядок выбора по возрастанию контроля: Functions, App Service, Container Apps, AKS, машины; для одного-двух сервисов AKS «как у всех» это лишняя эксплуатация.
  • Постоянная ровная нагрузка дешевле на выделенных ресурсах с обязательством, редкие или пиковые вызовы дешевле по факту; переломная точка около половины времени под нагрузкой, а сеть и обвязка стоят сопоставимо с вычислениями.
  • Уход в ноль оплачивается первым запросом после простоя: фоновой обработке всё равно, API держат минимум один экземпляр или сокращают старт приложения.
  • Без состояния обязаны быть все варианты, которые масштабируются или пересоздаются: сессии в токен или кэш, файлы в хранилище объектов, расписание под блокировку, долгая работа в очередь.
  • До приватной базы машина и кластер дотягиваются сами, Container Apps через свою подсеть, а Functions и App Service только с интеграцией в сеть на подходящем тарифе.

Что почитать дальше