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 в группу "<usergroup>" пользователя "<username>";
- 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". Статья в доработке.
Комментарии
Наконец-то! На той же Хабре все запущено и много воды. Спасибо за статью )
Я использую 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...
@Александр, значит где-то ошиблись.
1) Не перезапустили nginx
2) server_name и хост который вы ввели в браузер не совпадают
Чтобы проверить работает ли location, укажите
И если при заходе на ваш хост будет выдана 402 ошибка, то тогда дело уже в uwsgi, иначе проблема где-то до этого места
Оставьте свой комментарий