App Service: хостинг приложений

App Service — это «сервер, о котором можно не думать»: вы отдаёте код, Azure держит машину, ставит обновления, выдаёт HTTPS и масштабирует нагрузку.

Azure App Service — PaaS-платформа (Platform as a Service) для веб-приложений и API: вы деплоите код или контейнер, а операционную систему, веб-сервер, TLS-сертификаты и автомасштабирование обслуживает Azure.

Разница с виртуальной машиной — в границе ответственности. На VM вы сами ставите nginx, сами обновляете OpenSSL, сами настраиваете systemd и Let's Encrypt. В App Service всё это уже есть: у приложения из коробки адрес вида https://myapp.azurewebsites.net, действующий сертификат, логи, метрики и кнопка «добавить ещё два инстанса».

Зачем это на практике

Представьте типичную задачу: выкатить Django- или Express-приложение и не проснуться ночью от того, что закончилось место на диске из-за логов. На VM вам понадобится: настроить сервер, поставить runtime, написать unit для автозапуска, настроить reverse proxy, продлевать сертификат, следить за обновлениями безопасности. На App Service — одна команда деплоя.

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

Как устроено: план и приложение

Здесь у новичков возникает главная путаница. В App Service две сущности:

  • App Service Plan — это и есть арендованные вычислительные мощности (по сути, пул скрытых от вас виртуальных машин). Деньги вы платите за план.
  • Web App (App Service) — само приложение, которое работает на плане. Приложение само по себе не стоит ничего.

Из этого следует важное: на одном плане можно разместить несколько приложений — они будут делить его CPU и память. Это отличный способ сэкономить на dev-стендах и очень плохой способ «сэкономить» на проде: соседнее приложение с утечкой памяти уронит все остальные.

az appservice plan create \
  --name plan-demo \
  --resource-group rg-demo \
  --sku B1 \
  --is-linux

az webapp create \
  --resource-group rg-demo \
  --plan plan-demo \
  --name cc-demo-app-42 \
  --runtime "PYTHON:3.12"

Имя приложения глобально уникально: оно превращается в cc-demo-app-42.azurewebsites.net, поэтому «myapp» давно занят.

Тарифные планы

ТарифЧто даётПорядок цены
F1 (Free)1 ГБ, общие ресурсы, лимит 60 CPU-минут в сутки, нет своего домена с TLS, приложение засыпает0 $
B1–B3 (Basic)Выделенные инстансы, свой домен и сертификат, Always Onот ~13 $ в месяц
S1–S3 (Standard)+ слоты развёртывания (до 5), автомасштабирование, бэкапыот ~70 $ в месяц
P1v3+ (Premium)Больше CPU/RAM, до 20 слотов, интеграция с VNet, быстрое масштабированиеот ~110 $ в месяц

Бесплатный F1 отлично подходит, чтобы потрогать сервис руками. Но помните про его характер: 60 CPU-минут в день — это не «час работы», это суммарное процессорное время; исчерпали — приложение отдаёт ошибку до конца суток. И на F1 нельзя включить Always On, поэтому после 20 минут простоя приложение выгружается и первый запрос ждёт запуска.

Деплой кода

Самый простой путь — az webapp up: из папки с проектом он сам создаст план и приложение, запакует код и зальёт его.

cd ~/projects/my-flask-app

az webapp up \
  --name cc-demo-app-42 \
  --resource-group rg-demo \
  --plan plan-demo \
  --runtime "PYTHON:3.12"

Более контролируемый путь — zip-деплой готового архива:

zip -r app.zip . -x "*.git*" "*.venv*"

az webapp deploy \
  --resource-group rg-demo \
  --name cc-demo-app-42 \
  --src-path app.zip \
  --type zip

# логи в реальном времени — бесценно при первом деплое
az webapp log tail --resource-group rg-demo --name cc-demo-app-42

Само приложение — обычный веб-фреймворк, ничего специфичного для Azure в коде нет. Платформа сама запустит его и передаст порт через переменную окружения:

import os
from flask import Flask

app = Flask(__name__)

@app.route("/")
def index():
    # SLOT_NAME мы зададим сами в настройках приложения
    slot = os.environ.get("SLOT_NAME", "unknown")
    return f"Привет из слота: {slot}"

if __name__ == "__main__":
    # локально — 5000, в App Service порт придёт из окружения
    app.run(host="0.0.0.0", port=int(os.environ.get("PORT", 5000)))

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

az webapp config appsettings set \
  --resource-group rg-demo \
  --name cc-demo-app-42 \
  --settings SLOT_NAME=production DB_HOST=db.internal

Слоты развёртывания

Слот — это полноценная копия приложения на том же плане, со своим адресом вида cc-demo-app-42-staging.azurewebsites.net. Вы деплоите новую версию в staging, проверяете её на живой инфраструктуре, а потом делаете swap — и слоты меняются ролями.

az webapp deployment slot create \
  --resource-group rg-demo \
  --name cc-demo-app-42 \
  --slot staging

az webapp deploy \
  --resource-group rg-demo \
  --name cc-demo-app-42 \
  --slot staging \
  --src-path app.zip --type zip

# проверили https://cc-demo-app-42-staging.azurewebsites.net — и меняем местами
az webapp deployment slot swap \
  --resource-group rg-demo \
  --name cc-demo-app-42 \
  --slot staging \
  --target-slot production

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

Слоты доступны начиная с тарифа Standard. На Basic их нет — это, пожалуй, главная причина не экономить на проде.

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

Ваше приложение живёт в контейнере на скрытой от вас VM (worker). Перед ней стоит фронтенд-слой Azure, который принимает HTTPS-трафик, терминирует TLS и раскидывает запросы по инстансам. Рядом с приложением всегда работает служебный сайт Kudu/SCM по адресу <имя>.scm.azurewebsites.net — через него идёт деплой, там же есть консоль и файловый браузер: полезно, когда «локально работает, а там нет».

При zip-деплое Linux-приложение обычно собирается билд-системой Oryx: она видит requirements.txt или package.json и ставит зависимости прямо на сервере, если включена настройка SCM_DO_BUILD_DURING_DEPLOYMENT=true. Если её не включить, а зависимости не приложить — приложение упадёт с ModuleNotFoundError.

Настройки приложения можно пометить как slot setting («прилипшие к слоту») — тогда при swap они не переезжают. Так делают со строкой подключения к базе: production смотрит в боевую БД, staging — в тестовую, и после swap каждый остаётся при своей.

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

  • «Я остановил приложение, почему счёт растёт?» Оплачивается план, а не приложение. Остановленный Web App на плане S1 стоит ровно столько же. Чтобы перестать платить — удалите план или переведите его на F1: az appservice plan update --sku F1.
  • Первый запрос идёт 30 секунд. На Basic и выше включите Always On (az webapp config set --always-on true), иначе после простоя приложение выгружается и «просыпается» на первом запросе. На F1 эта опция недоступна в принципе.
  • Слоты «не находятся». Их просто нет на Free/Shared/Basic. Нужен Standard или выше.
  • Забыли про sticky settings. Сделали swap — и staging начал писать в боевую базу. Помечайте окружение-зависимые настройки как slot setting (--slot-settings).
  • Секреты в репозитории. Строки подключения — в App Settings, а лучше в Key Vault со ссылкой из настроек. В git им не место.
  • Свой Docker-контейнер не отвечает. App Service ждёт приложение на порту 8080 (для Linux-контейнеров) — либо слушайте его, либо задайте WEBSITES_PORT с нужным номером.
  • Автомасштабирование «на всякий случай». Правило «до 10 инстансов» на плане P1v3 при DDoS-волне или зацикленном запросе выставит счёт на сотни долларов. Ставьте верхний предел осознанно и заводите бюджетные алерты.

Итоги

  • App Service — PaaS: вы отвечаете за код, Azure — за ОС, веб-сервер, TLS и масштабирование.
  • Платите за App Service Plan; приложения на плане бесплатны, но делят его ресурсы.
  • F1 — бесплатно и с ограничениями (60 CPU-минут в сутки, засыпание), Basic — минимальный «взрослый» тариф, Standard — первый со слотами.
  • Деплой: az webapp up для старта, az webapp deploy --type zip для CI/CD.
  • Слоты дают деплой без простоя и мгновенный откат; настройки, зависящие от окружения, помечайте как slot settings.
  • Конфигурация — в App Settings и переменных окружения, не в коде.
Проверьте себя
1. За что именно списываются деньги в Azure App Service?
AЗа каждое приложение (Web App) отдельно, по числу запросов
BЗа App Service Plan — даже если приложения на нём остановлены
CТолько за исходящий трафик приложения
DЗа время, которое приложение реально обрабатывало запросы
2. С какого тарифа App Service становятся доступны слоты развёртывания (staging/production)?
AС бесплатного F1
BС Basic (B1)
CСо Standard (S1) и выше
DТолько в Isolated