Деплой: 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.