Структура проекта и подготовка к продакшену
Промышленный FastAPI-проект разбит на модули по доменам, настраивается через переменные окружения и запускается в продакшене несколькими воркерами под управлением процесс-менеджера, обычно в Docker-контейнере.
Зрелый проект отличают три вещи: понятная модульная структура, конфигурация из окружения (а не из кода) и продакшен-запуск с несколькими воркерами вместо одиночного
--reload.
Учебный пример живёт в одном файле, но реальное приложение требует структуры. Типичная раскладка делит код по слоям и доменам: роутеры (эндпоинты), модели (Pydantic-схемы), сервисы (бизнес-логика), работа с БД, конфигурация. Это упрощает навигацию, тестирование и совместную работу.
app/
main.py # создание FastAPI, lifespan, подключение роутеров
config.py # настройки из переменных окружения
routers/
users.py # APIRouter для пользователей
items.py
models/
user.py # Pydantic-схемы
services/
user_service.py # бизнес-логика
db.py # engine, сессия-зависимость
Конфигурацию берут из окружения, а не зашивают в код: секреты, строки подключения, флаги. Pydantic предлагает для этого BaseSettings, который читает переменные окружения и валидирует их как обычную модель. Так один и тот же образ работает в разных средах, меняя поведение через переменные.
Как работает под капотом
Чтение настроек из окружения с дефолтами и проверкой типов — это та же валидация, что у Pydantic-модели. Смоделируем загрузку конфигурации на stdlib:
import os
# имитируем переменные окружения
fake_env = {"APP_PORT": "8080", "DEBUG": "false", "DB_URL": "postgresql://prod"}
def load_settings(env):
def get(key, default, cast=str):
raw = env.get(key, default)
if cast is bool:
return str(raw).lower() in ("1", "true", "yes")
return cast(raw)
return {
"port": get("APP_PORT", "8000", int),
"debug": get("DEBUG", "true", bool),
"db_url": get("DB_URL", "sqlite:///./local.db"),
}
settings = load_settings(fake_env)
print("настройки из окружения:", settings)
# тот же код, но окружение пустое -> сработают дефолты
print("настройки по умолчанию:", load_settings({}))
Попробуй сам ▶ Один код даёт разные настройки в зависимости от окружения — это и есть «12-факторный» принцип конфигурации, который реализует BaseSettings.
В продакшене приложение запускают не через --reload, а несколькими процессами-воркерами, чтобы использовать все ядра. Команды:
fastapi run app/main.py --workers 4
gunicorn app.main:app -k uvicorn.workers.UvicornWorker -w 4
Чаще всего это упаковывают в Docker, чтобы окружение было воспроизводимым:
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
CMD ["fastapi", "run", "app/main.py", "--workers", "4"]
Частые ошибки
Первая — тащить весь код в один файл по мере роста проекта. Вторая — хранить секреты в коде вместо переменных окружения. Третья — запускать прод с --reload и одним воркером, теряя производительность и устойчивость. Четвёртая — не фиксировать версии зависимостей, получая «работает у меня». Пятая — открывать наружу --reload//docs и отладочные настройки в продакшене.
Best practices
- Делите проект по доменам и слоям; держите
main.pyтонким. - Конфигурацию читайте из окружения через
BaseSettings; секреты — только в окружении. - В проде — несколько воркеров под процесс-менеджером, без
--reload. - Фиксируйте версии зависимостей и упаковывайте в Docker для воспроизводимости.
Наблюдаемость в продакшене
Развернуть сервис — половина дела; его нужно ещё видеть в работе. Зрелый продакшен включает три кита наблюдаемости. Логи — структурированные (в JSON), с корреляционным идентификатором запроса, чтобы по одному id проследить весь путь обращения через сервисы. Метрики — счётчики запросов, латентность по перцентилям, доля ошибок, использование пула соединений; их собирают и строят дашборды, чтобы замечать деградацию до жалоб пользователей. Трассировка — сквозное прослеживание запроса через несколько сервисов, незаменимое в микросервисной архитектуре. Плюс health-check эндпоинт, по которому оркестратор понимает, жив ли сервис. Без наблюдаемости продакшен превращается в чёрный ящик, где о проблемах узнают последними. Поэтому подготовка к проду — это не только воркеры и Docker, но и встроенные с самого начала логи, метрики и проверки здоровья, превращающие сервис из «работает на моей машине» в управляемую и диагностируемую систему.
Итог: зрелый проект — это модульная структура, конфигурация из окружения и продакшен-запуск несколькими воркерами, обычно в Docker. Эти практики превращают учебный main.py в надёжный сервис.