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 productionSwap — это не копирование файлов, а переключение маршрутизации: платформа сначала «прогревает» инстансы 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 и переменных окружения, не в коде.