2 февраля 2016 г. Django uWSGI Python

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, чтобы пересесть на джангу. а теперь поздно

Ну вот, этого мне и не хватало в 2012, чтобы пересесть на джангу. а теперь поздно

Странно, я "пересел" на django в 2013, поначалу деплоив ее под apache2/mod_wsgi и все было ок :)

supervisor здесь лишняя сущность имхо ;)

@slav0nic ну я так и написал

На самом деле uWSGI может перезапускаться самостоятельно и его не надо контроллировать. Но я предпочитаю чтобы процессы контроллировались внешними утилитами, дабы избежать проблем автономности и всё унифицировать.

я уже ушёл с супервизора. Или голый uwsgi/emperor или systemd.
Есть ещё circus, который сейчас более привлекательно смотрится

Делаю все по Вашему мануалу. Ловлю на сервере ошибку 404 при обращении к своей API. В логах пишет следующее:
WSGI app 0 (mountpoint='') ready in 1 seconds on interpreter 0x19daf60 pid: 18511 (default app).

Настройки uWSGI:

[uwsgi]
chdir=/home/myuser/vwrapperhome/myproj
master=true
pidfile=/home/myuser/vwrapperhome/myproj/uwsgi/myproj_uwsgi.pid
socket=/home/myuser/vwrapperhome/myproj/uwsgi/myproj_uwsgi.sock
chmod-socket=750
virtualenv=/home/myuser/.virtualenvs/python3-venv
pythonpath=.
pythonpath=/home/myuser/vwrapperhome/myproj
env=DJANGO_SETTINGS_MODULE=sources.myproj.settings
module=sources.myproj.wsgi:application
processes=1
threads=2
vacuum = true
harakiri=30
buffer-size=32768

Настройки nginx:

server {
    listen 80;
    server_name my_ip_address:80;

    charset utf-8;
    client_max_body_size 75M;

    location /media {
        alias /home/myuser/vwrapperhome/myproj/sources/files/public/media;
    }
    location /static {
        alias /home/myuser/vwrapperhome/myproj/sources/files/public/static;
    }
    location / {
        uwsgi_pass unix:///home/myuser/vwrapperhome/myproj/uwsgi/myproj_uwsgi.sock;
        include uwsgi_params;
    }
}

Настройки supervisor:

[program:myproj_uwsgi]
environment=PATH="/home/myuser/.virtualenvs/python3-venv/bin"
numprocs=1
directory=/home/myuser/vwrapperhome/myproj
command=/home/myuser/.virtualenvs/python3-venv/bin/uwsgi uwsgi/config.ini
user=myuser
autostart=true
autorestart=true
redirect_stderr=true
stopwaitsecs=60
stopsignal=INT
stderr_logfile=/home/myuser/vwrapperhome/myproj/logs/%(program_name)s_err.log
stdout_logfile=/home/myuser/vwrapperhome/myproj/logs/%(program_name)s_out.log
stdout_logfile_maxbytes=100MB
stdout_logfile_backups=30
stdout_capture_maxbytes=1MB
Александр 10 ноября 2018 г. 14:32

Сделал все по инструкции, вместо django морды получаю стандартную заглушку nginx...

@Александр, значит где-то ошиблись.
1) Не перезапустили nginx
2) server_name и хост который вы ввели в браузер не совпадают

Чтобы проверить работает ли location, укажите

location / {
    return 402;
}

И если при заходе на ваш хост будет выдана 402 ошибка, то тогда дело уже в uwsgi, иначе проблема где-то до этого места

Оставьте свой комментарий

Markdown