Django. Запуск проекта в связке uWSGI и Nginx

Очень часто меня просят рассказать как запустить django-проект в продакшен. Я обычно кидаю несколько конфигов со своих проектов и пишу как все установить и запустить. В 2012-ом начал писать эту статью, но как и многие другие мои статьи она попала в бездну прокрастинации.
Пришло время дописать эту статью, чтобы больше не объяснять всем и каждому почему и как запустить проект на Django под uWSGI.
Почему uWSGI?
Вкратце, он стабильный, гибкий в настройке и работает довольно быстро. По крайне мере, в большинстве бенчмарков, что я смотрел в своё время, он был быстрее его конкурентов. Например, в сравнении с Gunicorn:
Гуглятся результаты тестов производительности довольно быстро, так что оставлю за вами выбор источников информации. Либо можете самостоятельно написать тесты и мы вместе позапускаем их :-)
Настройка uWSGI
Сначала установим uWSGI в ваше виртуальное окружение:
cd ~/<project_name>
source ~/<venv_path>/bin/activate
pip install uWSGI
зафиксируем версию в файл зависимостей
pip freeze|grep -i uwsgi >> requirements.txt
Добавим в проект файл uwsgi/production.ini (каталог “uwsgi” нужен, потому что у меня обычно несколько конфигов, например “test.conf” для тестового стенда):
[uwsgi]
chdir=/home/<username>/<project_name>
pidfile=/home/<username>/<project_name>_uwsgi.pid
socket=/home/<username>/<project_name>_uwsgi.sock
chmod-socket=750
virtualenv=/home/<username>/<venv_path>
pythonpath=.
pythonpath=<project_name>
module=<project_name>.wsgi:application
callable=app
master=true
processes=2
harakiri=30
buffer-size=32768
Разберём опции:
chdir- путь до каталога с проектом;pidfile- путь до pid-файла;socket- файл UNIX или TCP сокета, рекомендую UNIX (работает немногим быстрее);chmod-socket- права на сокет-файл, позже добавим nginx в группу “” пользователя “”;virtualenv- путь до каталога окружения;pythonpath=.- добавим текущий от “chdir” каталог в “sys.path”;pythonpath=<project_name>- и каталог с кодом Django-проекта тоже;module- наш wsgi-модуль (идет в поставке с Django-проектом)callable- WSGI имя;master- запуск в режиме master: отдельный процесс будет контроллировать воркер-процессы;processes- количество воркер-процессов, которые будут обрабатывать поступающие запросы;harakiri- количество секунд после которых подвисший процес будет перезапущен;buffer-size- максимальный размер буфера для запроса (заголовки, например cookie и query string), не включает в себя размер тела запроса.
Ещё больше опций можете найти в оф. документации
Настройка Nginx
Если вы еще не установили Nginx, то сделайте это:
sudo apt-get install nginx
Создадим в корне проекта файл nginx/production.conf, со следующим содержимым:
server {
listen 80;
server_name example.org;
location /media/ {
root /home/<username>/<project_name>_storage;
}
location /static/ {
root /home/<username>/<project_name>_storage;
}
# Иногда бывает нужно отдавать статику не из хранилища,
# а прямо из каталога проекта. Но тогда престанет работать админ-панель Django,
# поэтому надо будет добавить вот такой локейшен:
# location /static/admin/ {
# root /home/<username>/<project_name>_storage;
# }
location / {
uwsgi_pass unix:///home/<username>/<project_name>_uwsgi.sock;
include uwsgi_params;
}
}
для того, чтобы Nginx смог работать с файлом uWSGI-сокета добавим его в группу вашего проекта, в Ubuntu это можно сделать так:
sudo usermod -a -G <usergroup> www-data
Настройка Supervisor
Если он у вас ещё не установлен, то это можно сделать так:
sudo apt-get install supervisor
sudo service supervisor start
Более подробно про работу с Supervisor я писал ранее.
На самом деле uWSGI может перезапускаться самостоятельно и его не надо контроллировать. Но я предпочитаю чтобы процессы контроллировались внешними утилитами, дабы избежать проблем автономности и всё унифицировать.
Создадим такой конфиг supervisor/production.conf:
[program:<project_name>_uwsgi]
environment=PATH="/home/<username>/<venv_path>/bin"
numprocs=1
directory=/home/<username>/<project_name>
command=/home/<username>/<venv_path>/bin/uwsgi uwsgi/production.ini
user=<username>
autostart=true
autorestart=true
redirect_stderr=true
stopwaitsecs=60
stopsignal=INT
stderr_logfile=/home/<username>/logs/%(program_name)s_err.log
stdout_logfile=/home/<username>/logs/%(program_name)s_out.log
stdout_logfile_maxbytes=100MB
stdout_logfile_backups=30
stdout_capture_maxbytes=1MB
Запускаем проект
Создадим для логов каталог в директории пользователя:
mkdir -p /home/<username>/logs
Сделаем симлинк на конфиг супервайзора нашего проекта, после чего дадим ему знать чтобы он обновился, он запустит по конфигу наш uWSGI-сервис:
sudo ln -s /home/<username>/<project_name>/supervisor/production.conf /etc/supervisor/conf.d/<project_name>.conf
sudo supervisorctl update
sudo supervisorctl status
После чего сделаем симлинк на Nginx-конфиг и перезапустим его тоже:
sudo ln -s /home/<username>/<project_name>/nginx/production.conf /etc/nginx/sites-enable/<project_name>.conf
sudo nginx -t
sudo nginx -s reload
Теперь должно работать, српашивайте что непонятно
Мониторинг и статистика работы uWSGI воркеров
Чуть позже расскажу про “uwsgi-top”. Статья в доработке.

Комментарии 11
Архив обсуждения. Новые комментарии закрыты.
Наконец-то! На той же Хабре все запущено и много воды. Спасибо за статью )
Я использую 1 uwsgi.ini файл для разных окружений - production и development
Все это легко реализуется благодаря тому, что можно использовать несколько секций в uwsgi.ini, например:
[uwsgi]
some default options for both environments
[cache]
cache = 1000
cache-blocksize = 65536
[staticfiles]
static-map2 = /assets=%dpublic
static-map2 = /uploads=%dpublic
[producion]
env = DEBUG=False
env = DJANGO_CONFIGURATION=Production
ini = :uwsgi
ini = :cache
venv = /server/.py/%c
print = Loaded production settings!
[development]
env = DEBUG=True
env = DJANGO_CONFIGURATION=Development
ini = :uwsgi
ini = :staticfiles
venv = /home/mechanism/.virtualenvs/%c
py-autoreload = 2
show-config
print = Loaded development settings!
это я просто для примера указал конфиг.
ну и например в Procfile команда для запуска:
web: newrelic-admin run-program uwsgi –ini uwsgi.ini:production
если нужно запустить секцию development просто подставить соответственно после uwsgi.ini:
web: newrelic-admin run-program uwsgi –ini uwsgi.ini:development
Можно в конфиге увидеть как делается импорт одной секции в другую:
ini = :cache
Не очень понятно зачем столько раз в конфиге писать “/home/” когда можно использовать значения из волшебной таблицы или задать переменные в конфиге и использовать их.
Я обычно кладу uwsgi.ini в корень прокта и использую значения из волшебной таблицы:
%c - имя папки в которой лежит файл uwsgi.ini
%d - полный путь до папки в которой лежит uwsgi,ini
можно прописать pp = %d
где pp - pythonpath а %d пусть до папки с проектом.
или touch-reload = %p
где %p это сам файл конфига uwsgi.ini
ну и в конфиге приведенном мной выше видно как используется %c - вирт окружение называется так же как и папка в которой лежит проект, поэтому venv = /server/.py/%c
Доброго времени суток, спасибо за статью. В образовательных целях я развернул джанго проект по вашей статье, но заметил странное поведение, суть проблемы я описал здесь http://stackoverflow.com/questions/36166430/uwsgi-worker-free-but-request-handling-has-significant-delay
Ну вот, этого мне и не хватало в 2012, чтобы пересесть на джангу. а теперь поздно
Странно, я “пересел” на django в 2013, поначалу деплоив ее под apache2/mod_wsgi и все было ок :)
supervisor здесь лишняя сущность имхо ;)
@slav0nic ну я так и написал
я уже ушёл с супервизора. Или голый uwsgi/emperor или systemd.
Есть ещё circus, который сейчас более привлекательно смотрится
Делаю все по Вашему мануалу. Ловлю на сервере ошибку 404 при обращении к своей API. В логах пишет следующее:
WSGI app 0 (mountpoint=’’) ready in 1 seconds on interpreter 0x19daf60 pid: 18511 (default app).
Настройки uWSGI:
Настройки nginx:
Настройки supervisor:
Сделал все по инструкции, вместо django морды получаю стандартную заглушку nginx…
@Александр, значит где-то ошиблись.
Чтобы проверить работает ли location, укажите
И если при заходе на ваш хост будет выдана 402 ошибка, то тогда дело уже в uwsgi, иначе проблема где-то до этого места