Деплой Django+uWSGI+Nginx

May 29, 2014 10:35 · 1059 words · 5 minute read django админство

Несколькими статьями ранее я рассказывал как на одной странице вывести список объектов и форму для добавления нового. Пришло время запульнуть это приложение на сервер :) Для этого нам понадобится nginx (1.4.6), uwsgi (1.0.3), virtualenv (1.7.1.2), supervisor. Определимся, что файлы проекта будут лежать в /var/www/tyvik/markettyvik.

Установка виртуального окружения

Любое приложение на Django по-хорошему надо поместить в песочницу, чтобы пакеты, которые оно требует, не конфликтовали с системными. Для этих целей есть специальные пакеты virtualenv и virtualenvwrapper. Мне пока достаточно функционала первого, приступим:

$ sudo apt-get install python-virtualenv
$ cd /var/www/tyvik/markettyvik
$ mkdir venv
$ virtualenv --no-site-packages venv/

После этих манипуляций в каталоге /var/www/tyvik/markettyvik/venv/ появятся файлы, необходимые для работы python. Ключ –no-site-packages нужен для того, чтобы уже установленные системные пакеты не переносились в виртуальное окружение. Активируем его и установим зависимости:

$ source venv/bin/activate
$ pip install -r requirements.txt

Я не зря упомянул virtualenvwrapper, он позволяет держать несколько разных виртуальных окружений и переключаться между ними не через source, а командой workon. Это пригодится разработчикам, особенно когда они сопровождают множество разных python-проектов.

Установка зависимостей

Зачастую в проекте есть зависимости от модулей, которые компилируются при установке, например, MySQL-python. Для них нужны dev-пакеты с исходниками. Понять, что какого-то пакета нет в системе можно по сообщению об ошибке “Не найден файл *.h”. В таком случае рекомендую просто гуглить по строке с ошибкой. В частности, для MySQL-python нужны python-dev и libmysqlclient-dev. Установим их:

$ sudo apt-get install libmysqlclient-dev
$ sudo apt-get install python-dev

Установка приложения

С зависимостями разобрались, теперь нужно установить приложение. Сводится это к двум моментам: настройка базы и сбор статики. Моё приложение использует MySQL, следовательно нужно создать соответствующего пользователя, базу данных и распределить права. Также надо задать кодировку по умолчанию как utf8-general-ci, потому как в командах CREATE TABLE Django автоматически не добавляет эту настройку. После всего этого можно запустить миграцию:

$ ./manage.py syncdb

В результате будут созданы нужные таблицы и добавлены связи. Теперь перейдём к сбору статических ресурсов (CSS и JS). Для этого служит команда collectstatic:

$ ./manage.py collectstatic

Она просто копирует всю статику из каждого модуля в каталог со статикой главного приложения. Встроенный web-сервер Django умеет находить эти ресурсы самостоятельно, но для работы uwsgi+nginx необходимо, чтобы вся она лежала в одном каталоге. Настройки для этой операции находятся в settings.py головного модуля.

Настройка uWSGI

В комплекте с Django идёт простенький web-сервер, который предназначен для разработки. Его крайне не рекомендуется использовать в боевом окружении. Стандартом для этого стал uWSGI - шлюз между web-сервером (nginx) и приложением (Django). Эта штука запускается демоном и умеет сама запускать виртуальное окрудение и взаимодействовать со скриптами. В общем, ставим в систему:

$ sudo apt-get install uwsgi uwsgi-plugin-python

Теперь зададим конфигурацию приложения в файле deploy/uwsgi.conf.xml:

<uwsgi>
  <socket>/var/www/tyvik/markettyvik/deploy/data/markettyvik.sock</socket>
  <plugins>python27</plugins>
  <pythonpath>/var/www/tyvik/markettyvik</pythonpath>
  <virtualenv>/var/www/tyvik/markettyvik/venv</virtualenv>
  <module>markettyvik.wsgi</module>
  <master />
  <workers>4</workers>
  <processes>2</processes>
  <uid>www-data</uid>
</uwsgi>

Разберём параметры:

  • socket - сокет, через который будет идти взаимодействие nginx с django.
  • plugins - тип приложения
  • pythonpath - путь до приложения
  • virtualenv - какое виртуальное окружение использовать
  • module - точка входа
  • workers - количество воркеров
  • processes - количество процессов
  • uid - от имени какого пользователя выполнять скрипты

Конфигурационные файлы можно писать не только в формате xml, но также ini, yaml и пр. Проверить работу можно командой

$ sudo uwsgi -x deploy/uwsgi.conf.xml

Не будем пока прерывать работу этой команды, она понадобится для настройки Nginx.

Настройка Nginx

Осталось последнее звено - настройка web-сервера. Его конфигурационные файлы находятся в каталоге /etc/nginx, а в подкаталоге sites-available - настройки для конкретных сайтов. Можно создать новый файл непосредственно там, но я предпочитаю всё, что касается проекта, держать в одном месте. Посему конфиг будет в каталоге приложения deploy/nginx.conf, а в sites-available всего лишь ссылка на него.

upstream markettyvik {
    ip_hash;
    server unix:///var/www/tyvik/markettyvik/deploy/data/markettyvik.sock;
}

server {
    listen 80;
    server_name django.tyvik.ru;
    location / {
        uwsgi_pass  markettyvik;
        include     uwsgi_params;
    }
    location /static {
        alias /var/www/tyvik/markettyvik/static;
    }
}

Разберём подробнее… Т.к. взаимодействие с uwsgi будет идти через UNIX-сокет, то нужно создать upstream со ссылкой на него. Далее в блоке location мы даём на него ссылку (параметр uwsgi_pass) и указываем, что используется uwsgi (подключаем uwsgi_params). Отдельно указываем где находится статика, потому что к ней будет обращаться nginx, а не django. Именно из-за этого ранее мы и собрали всю её в одном месте командой collectstatic. Если у вас используются файлы, загруженные пользователем, то по аналогии со /static надо будет дописать ещё один раздел location со ссылкой на него. Также указан порт и имя сервера, которое нужно обслуживать. Этими действиями мы только уведомили nginx, что есть конфигурация для нашего сервере, осталось её активировать, то есть создать ссылку в sites-enabled; также обновим конфигурацию Nginx:

$ ln -s /etc/nginx/sites-available/django.conf /etc/nginx/sites-enabled/django.conf
$ sudo service nginx reload

После этого по адресу django.tyvik.ru можно увидеть работающее приложение.

Итак, при запросе ресурса с django.tyvik.ru происходит следующее:

  1. Запрос перехватывается nginx. Если он начинается со “/static”, то берётся непосредственно файл из каталога static.
  2. В противном случае запрос передаётся через UNIX-сокет markettyvik.sock в uWSGI.
  3. uWSGI, используя виртуальное окружение, передаёт в markettyvik/wsgi.py.
  4. Там создаётся wsgi_application, которое уже обрабатывает запрос и ответ передаёт в uWSGI, которое отдаёт nginx, который уже отдаёт пользователю :)

В принципе, всё работает и на этом можно было бы и остановиться, но рассказ не будет полным без описания supervisor'а.

Настройка supervisor

А что будет, если наше приложение упадёт? Всё остановится пока не придёт админ и не поднимет его. Это жутко неудобно, а значит есть средства для выполнения этой рутины. Назвается оно supervisor, ставится как-то так:

$ sudo apt-get install supervisor

Если вкратце, то оно регистрирует демона, который будет следить за другими процессами. В частности, определять жив ли как-нибудь процесс и в противном случае перезапускать его. Настроим, чтобы он следил за нашим приложением. Работа с ним должна вестись от имени root. Конфигурационный файл выглядит следующим образом:

[program:markettyvik]
user = www-data
directory = /var/www/tyvik/markettyvik
command = uwsgi -x /var/www/tyvik/markettyvik/deploy/uwsgi.conf.xml
autostart = true
autorestart = true
stderr_logfile = /var/www/tyvik/markettyvik/deploy/data/uwsgi-err.log
stdout_logfile = /var/www/tyvik/markettyvik/deploy/data/uwsgi-out.log
stopsignal = QUIT

Здесь говорится, что для программы markettyvik [program] надо перейти в каталог /var/www/tyvik/markettyvik [directory] и выполнить uwsgi -x /var/… [command] от имени www-data [user], причём вывод писать в /var/www/…/uwsgi-out.log [stdout_logfile], а ошибки в /var/www/…/uwsgi-err.log [stderr_logfile]. Все конфигурации для supervisor хранятся в /etc/supervisor/conf.d/. Сделаем туда ссылку и немного поиграемся :)

> sudo ln -s /var/www/tyvik/markettyvik/deploy/supervisor.conf /etc/supervisor/conf.d/markettyvik.conf
> sudo supervisorctl update
markettyvik: added process group
> sudo supervisorctl start markettyvik
markettyvik: started
> sudo supervisorctl status
markettyvik                      RUNNING    pid 8988, uptime 0:00:32

Приложение работает и будет перезапущено в случае падения.

Вот теперь, пожалуй, всё готово :) Если что-то не работает, или я забыл указать какой-либо нюанс, просьба оставить комментарий. Готовый пример можно посмотреть у меня на github.