FastAPIPythonДеплой

Где разместить FastAPI-приложение: варианты хостинга и деплой

6 мин чтения

FastAPI-приложение работает локально через uvicorn main:app --reload. Этот режим — для разработки: --reload следит за изменениями файлов и не рассчитан на нагрузку. Чтобы приложение отвечало в продакшене круглосуточно, его нужно правильно запустить и где-то разместить. Разберём, как запускать FastAPI в проде, какие есть варианты хостинга и как задеплоить.

ASGI-сервер для продакшена

FastAPI — это ASGI-приложение, и в проде его запускает ASGI-сервер. Вариантов два.

Один процесс — uvicorn:

uvicorn main:app --host 0.0.0.0 --port 8000

Без --reload. Один процесс держит один поток событий — этого достаточно для умеренной нагрузки.

Несколько процессов — gunicorn с uvicorn-воркерами:

gunicorn main:app -k uvicorn.workers.UvicornWorker -w 4 --bind 0.0.0.0:8000

-w 4 поднимает четыре рабочих процесса и использует все ядра. Ориентир по числу воркеров — 2 × ядра + 1. Это рекомендуемый способ для прода.

Сам ASGI-сервер не стоит выставлять напрямую в интернет — перед ним ставят reverse proxy (nginx) для TLS и домена. Как это настроить — в статье про nginx и SSL.

Куда размещать: три варианта

VPS (Beget, Timeweb, Hetzner). Полный контроль. Минус — Python, gunicorn под supervisor, база, бэкапы, nginx и SSL настраиваются и обслуживаются вручную.

Managed-платформа (PaaS). Деплой по git push: платформа собирает проект, держит процесс живым, рядом managed-база, домен и SSL — автоматически.

Serverless (облачные функции). FastAPI можно запустить в Lambda через адаптер Mangum, но cold start и лимиты времени выполнения делают это неудобным для постоянной нагрузки и фоновых задач. Для долгоживущего API-сервиса подходит плохо.

Подготовка к деплою

Зафиксировать зависимости в requirements.txt (pip freeze > requirements.txt).

Вынести конфигурацию в переменные окружения. Секреты и строку подключения к базе нельзя держать в коде. Удобно через pydantic-settings:

from pydantic_settings import BaseSettings

class Settings(BaseSettings):
    database_url: str
    secret_key: str

settings = Settings()  # читает DATABASE_URL и SECRET_KEY из окружения

Подробнее о хранении ключей — в статье про секреты и .env.

Не забыть про миграции базы. Схему накатывают миграциями (обычно Alembic), а не вручную. Команда alembic upgrade head должна выполняться при каждом деплое — до старта приложения.

Деплой на VPS

git clone https://github.com/user/myapi.git /opt/myapi
cd /opt/myapi
python -m venv .venv
.venv/bin/pip install -r requirements.txt
.venv/bin/alembic upgrade head

Запускать gunicorn руками нельзя — процесс умрёт с закрытием сессии. Нужен супервизор с автозапуском; минимальный systemd-сервис:

[Service]
WorkingDirectory=/opt/myapi
ExecStart=/opt/myapi/.venv/bin/gunicorn main:app -k uvicorn.workers.UvicornWorker -w 4 --bind 127.0.0.1:8000
EnvironmentFile=/opt/myapi/.env
Restart=always

--bind 127.0.0.1 — приложение слушает только localhost, наружу его отдаёт nginx. Полная настройка supervisor — в статье как держать процесс 24/7.

Деплой на managed-платформе

Короче: подключить репозиторий, задать переменные окружения (DATABASE_URL, SECRET_KEY) в панели, указать команду запуска (gunicorn с uvicorn-воркерами) и сделать git push. Платформа определит Python-проект, установит зависимости, запустит процесс и будет держать его живым. Managed Postgres подключается через DATABASE_URL, домен и SSL поднимаются автоматически. Миграции удобно вешать на стадию деплоя.

Фоновые задачи и статика

Не всякую работу стоит делать прямо в обработчике запроса. Короткую задачу после ответа (отправить письмо, записать в лог) удобно вынести в BackgroundTasks FastAPI. Но тяжёлые и длительные задачи (обработка видео, рассылка) так делать нельзя: они держат воркер и блокируют запросы. Для них нужен отдельный фоновый воркер с очередью — Celery или RQ поверх Redis.

Статику (загруженные файлы, медиа) на нескольких воркерах и тем более нескольких инстансах нельзя хранить на локальном диске процесса — она будет видна не всем. Файлы кладут в объектное хранилище (S3-совместимое), а раздачу статики фронтенда поручают reverse proxy или CDN.

Проверка после деплоя

Запущенное приложение стоит проверить явно, а не считать рабочим по факту деплоя:

  • открыть автодокументацию по /docs — если она отвечает, ASGI-сервер поднялся;
  • дёрнуть простой эндпоинт и убедиться, что приходит 200, а не 502 от reverse proxy;
  • посмотреть логи старта: подключение к базе, выполненные миграции, отсутствие исключений.

Полезно завести лёгкий /health-эндпоинт, который проверяет доступность базы. Его используют reverse proxy и мониторинг, чтобы понимать, что приложение живо не только как процесс, но и функционально.

Частые грабли

  • --reload в проде. Режим разработки оставляет лишний процесс-наблюдатель и течёт по памяти под нагрузкой.
  • Один воркер под нагрузкой. Один процесс обрабатывает запросы по очереди; для параллелизма нужны воркеры gunicorn.
  • Блокирующий вызов в async-эндпоинте. Синхронный запрос к базе или time.sleep внутри async def блокирует весь event loop. Тяжёлую синхронную работу выносят в run_in_executor или делают эндпоинт обычным def.
  • Забытые миграции. Код задеплоен, а схема старая — приложение падает на старте. alembic upgrade head — часть деплоя.
  • CORS. Фронтенд на другом домене не достучится до API без настроенного CORSMiddleware.

Чеклист

  • Прод запускается gunicorn с uvicorn-воркерами, без --reload.
  • Число воркеров подобрано под ядра (2 × ядра + 1).
  • ASGI-сервер слушает localhost, наружу отдаёт reverse proxy с SSL.
  • DATABASE_URL и секреты — в переменных окружения, не в коде.
  • Миграции (alembic upgrade head) выполняются при деплое.
  • Процесс под супервизором с автоперезапуском.
  • Настроены бэкапы базы.

Managed-платформа закрывает большую часть этого списка по умолчанию: процесс держится сам, reverse proxy с SSL и managed Postgres — из коробки, миграции вешаются на деплой. На Hostim FastAPI-приложение разворачивается по git push, стек определяется автоматически, а база подключается через DATABASE_URL без ручной настройки сервера.

Читать дальше