Деплой: Gunicorn, Nginx и продакшен-стек

Сервер разработки runserver не для боя. В продакшене Django запускают через WSGI/ASGI-сервер за обратным прокси Nginx — это связка для нагрузки и безопасности.
Суть: Gunicorn (WSGI) или Uvicorn (ASGI) запускают Django в несколько рабочих процессов, Nginx стоит впереди как обратный прокси: терминирует HTTPS, раздаёт статику, защищает. systemd держит процесс живым.

Почему не runserver

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

Архитектура продакшен-стека

Боевое развёртывание — это слоёный пирог, где каждый компонент делает своё дело:

Интернет (HTTPS)
      │
      ▼
┌──────────────┐
│    Nginx     │ ── обратный прокси:
│              │    HTTPS/TLS, раздача статики,
│              │    защита, балансировка
└──────┬───────┘
       │ проксирует динамику (Unix-сокет)
       ▼
┌──────────────┐
│   Gunicorn   │ ── WSGI-сервер:
│  worker × N  │    несколько процессов Django
└──────┬───────┘
       │
       ▼
┌──────────────┐
│    Django    │ ── ваше приложение
└──────┬───────┘
       │
       ▼
┌──────────────┐
│  PostgreSQL  │ ── база данных
└──────────────┘

Gunicorn и воркеры

Gunicorn — популярный WSGI-сервер. Он запускает несколько рабочих процессов (воркеров), каждый из которых обрабатывает запросы параллельно. Запуск выглядит так:

pip install gunicorn
gunicorn mysite.wsgi:application --workers 3 --bind unix:/run/app.sock

Базовая рекомендация по числу воркеров — 2 × число_ядер + 1. Для async-приложений на ASGI используют Uvicorn вместо Gunicorn.

Nginx как обратный прокси

Nginx стоит перед Gunicorn и берёт на себя то, что Django делать не должен: терминирует HTTPS, эффективно раздаёт собранную статику, буферизует соединения, отсекает мусорные запросы. Динамические запросы он проксирует в Gunicorn через Unix-сокет (быстрее и безопаснее TCP-порта).

systemd: держим процесс живым

Чтобы Gunicorn запускался при загрузке сервера и перезапускался при падении, его оформляют как systemd-сервис. Тогда ОС следит за процессом и поднимает его автоматически.

Как это работает под капотом

Балансировка запросов между воркерами — это распределение задач по свободным обработчикам. Языко-независимая логика «очередь к пулу воркеров»:

# Попробуй сам ▶ — как воркеры разбирают запросы
from collections import deque

requests = ["GET /", "POST /login", "GET /blog/1", "GET /api", "POST /buy"]
workers = [0, 0, 0]            # счётчик нагрузки 3 воркеров
log = []

for i, req in enumerate(requests):
    # round-robin: запрос идёт наименее загруженному воркеру
    w = workers.index(min(workers))
    workers[w] += 1
    log.append(f"{req:14} → worker #{w}")

for line in log:
    print(line)
print("Итоговая нагрузка воркеров:", workers)

Gunicorn делает это надёжнее (учитывает занятость, перезапускает зависшие воркеры), но идея — распределить входящие запросы между несколькими процессами для параллелизма.

Чек-лист перед деплоем

Перед выкаткой обязательно: collectstatic для сборки статики, migrate для применения миграций, и главное — команда проверки безопасности:

python manage.py check --deploy
python manage.py collectstatic --no-input
python manage.py migrate

check --deploy прогоняет настройки против чек-листа Django и предупреждает о небезопасных значениях (DEBUG, незащищённые куки, отсутствие HSTS).

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

  • Запускать runserver в продакшене. Не для нагрузки и без HTTPS.
  • Забыть collectstatic. На проде пропадут все стили и картинки.
  • Не применить миграции на сервере. Ошибки «no such column».
  • Раздавать статику через Django вместо Nginx/WhiteNoise. Медленно и не нужно.
  • Пропустить check --deploy. Легко выкатить небезопасные настройки.

Best practices

  • Связка Nginx + Gunicorn (или Uvicorn для async) — проверенный стандарт.
  • Держите Gunicorn под systemd для автозапуска и перезапуска.
  • В пайплайн деплоя включите migrate и collectstatic.
  • Всегда прогоняйте check --deploy и включайте HTTPS с HSTS.

Итоги

Продакшен — это Nginx впереди, Gunicorn/Uvicorn с несколькими воркерами, Django и PostgreSQL. Nginx раздаёт статику и HTTPS, Gunicorn даёт параллелизм, systemd держит процесс. Перед деплоем — migrate, collectstatic, check --deploy. В завершение — как строить API на Django.

Проверьте себя
1. Почему нельзя использовать runserver в продакшене?
AОн платный
BЭто однопоточный сервер для разработки без HTTPS, не для нагрузки
CОн не поддерживает шаблоны
DОн требует Nginx
2. Какую роль в продакшен-стеке играет Nginx?
AХранит данные
BОбратный прокси: HTTPS, раздача статики, защита перед Gunicorn
CПрименяет миграции
DЗаменяет Django