Некоторое время назад озадачился поиском способа развертывания проектов Django, к которому предъявлялись два требования:
По второму пункту мой выбор склонился в пользу Nginx + uWSGI. По первому же, из рассмотренных мною вариантов больше всего понравились обвязки для uWSGI в Debian.
Поддержка uWSGI появилась в Nginx с версии 0.8.40, но ни первого, ни второго в стабильной ветке Debian нет. Поэтому Nginx будем брать из бэкпортов, а uWSGI – из нестабильной ветки. Для этого добавим следующие строчки в /etc/apt/sources.list:
И создадим примерно такой /etc/apt/preferences:
Синхронизируемся с помощью aptitude update и устанавливаем необходимые пакеты:
Рассмотрим развертывание для проекта example. Для определенности, предположим, что все проекты размещаются в подкаталоге proj домашнего каталога пользователя web. Так же в proj имеется подкаталог static, где будут размещаться статические файлы проектов, за раздачу которых будет отвечать Nginx. Для проекта example получим следующие пути:
В settings.py проекта example имеем настройки:
Таким образом менеджмент-команда collectstatic будет собирать всю статику проекта в /home/web/proj/static/example.
Виртуальное окружение для проекта example будет находиться в /home/web/envs/example.
Конфигурационный файл uWSGI для проекта example будет называться /etc/uwsgi/apps-available/example.ini и примет следующий вид:
Также создаем символическую ссылку на него в /etc/uwsgi/apps-enabled:
После чего запускаем демон uWSGI: /etc/init.d/uwsgi start example
Конфигурационный файл Nginx назовем /etc/nginx/sites-available/example. Его минимальный вариант может быть таким:
Точно так же создаем символическую ссылку в /etc/nginx/sites-enabled:
И перезапускаем Nginx: /etc/init.d/nginx restart
Сначала вкратце остановлюсь на пераметрах в конфигурационном файле uWSGI. Параметр plugins явно задает версию Python. Параметры chdir и pythonpath добавляют каталог проекта и его родительский каталог в пути поиска Python. Параметры env и module позволяют обойтись без создания специального скрипта для запуска проекта демоном uWSGI. Параметр touch-reload позволяет делать перезагрузку проекта с помощью команды touch /home/web/proj/example/touchme.
Такой лаконичный файл конфигурации для uWSGI получился потому, что стартовый скрипт /etc/init.d/uwsgi использует еще один конфигурационный файл /usr/share/uwsgi/conf/default.ini, в котором для всех остальных необходимых параметров заданы достаточно разумные значения по умолчанию. При необходимости их можно переопределить в /etc/uwsgi/apps-available/example.ini. За удалением некоторых комментариев, содержимое /usr/share/uwsgi/conf/default.ini будет следующим:
В обвязках к uWSGI в Debian введено понятие конфигурационного пространства имен. В default.ini оно представлено с помощью переменной %(deb-confnamespace) со значением по умолчанию app. Имя конфигурационного пространства имен определяется следующим образом: стартовый скрипт /etc/init.d/uwsgi ищет в каталоге /etc/uwsgi все подкаталоги, заканчивающиеся на s-enabled, и часть имени подкаталога, предшествующая s-enabled становится именем конфигурационного пространства для файлов в этом подкаталоге. В нашем случае можно было бы создать в /etc/uwsgi более привычные подкаталоги sites-available и sites-enabled, в которые поместить файл конфигурации example.ini и символическую ссылку на него соответственно; pidfile и socket для этой конфигурации находились бы в /run/uwsgi/site/example/pid и /run/uwsgi/site/example/socket, а управление демоном uWSGI для этой конфигурации осуществлялось бы с помощью команд /etc/init.d/uwsgi start|stop|restart site/example. При отсутствии префикса подразумевается пространство имен app.
- Удобное управление запуском/остановкой/перезапуском нескольких проектов на одном хосте
- Поддержка разных виртуальных сред для разных проектов
По второму пункту мой выбор склонился в пользу Nginx + uWSGI. По первому же, из рассмотренных мною вариантов больше всего понравились обвязки для uWSGI в Debian.
Установка ПО
Поддержка uWSGI появилась в Nginx с версии 0.8.40, но ни первого, ни второго в стабильной ветке Debian нет. Поэтому Nginx будем брать из бэкпортов, а uWSGI – из нестабильной ветки. Для этого добавим следующие строчки в /etc/apt/sources.list:
deb http://backports.debian.org/debian-backports squeeze-backports main contrib non-free
deb http://ftp.ru.debian.org/debian testing main non-free contrib
deb http://ftp.ru.debian.org/debian unstable main non-free contrib
И создадим примерно такой /etc/apt/preferences:
Package: *
Pin: release a=stable
Pin-Priority: 700
Package: *
Pin: release a=squeeze-backports
Pin-Priority: 675
Package: *
Pin: release a=testing
Pin-Priority: 650
Package: *
Pin: release a=unstable
Pin-Priority: 600
Синхронизируемся с помощью aptitude update и устанавливаем необходимые пакеты:
aptitude -t squeeze-backports install nginx
aptitude -t unstable install uwsgi
aptitude -t unstable install uwsgi-plugin-python
aptitude -t unstable install python-virtualenv
Структура каталогов для проектов
Рассмотрим развертывание для проекта example. Для определенности, предположим, что все проекты размещаются в подкаталоге proj домашнего каталога пользователя web. Так же в proj имеется подкаталог static, где будут размещаться статические файлы проектов, за раздачу которых будет отвечать Nginx. Для проекта example получим следующие пути:
/home/web/proj/example
/home/web/proj/static/example
В settings.py проекта example имеем настройки:
STATIC_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), '../static/example'))
STATIC_URL = '/static/'
Таким образом менеджмент-команда collectstatic будет собирать всю статику проекта в /home/web/proj/static/example.
Виртуальное окружение для проекта example будет находиться в /home/web/envs/example.
Настройка uWSGI и Nginx
Конфигурационный файл uWSGI для проекта example будет называться /etc/uwsgi/apps-available/example.ini и примет следующий вид:
[uwsgi]
plugins = python27
virtualenv = /home/web/envs/example/
chdir = /home/web/proj/example/
pythonpath = ..
env = DJANGO_SETTINGS_MODULE=example.settings
module = django.core.handlers.wsgi:WSGIHandler()
touch-reload = /home/web/proj/example/touchme
Также создаем символическую ссылку на него в /etc/uwsgi/apps-enabled:
cd /etc/uwsgi/apps-enabled
ln -s ../apps-available/example.ini .
После чего запускаем демон uWSGI: /etc/init.d/uwsgi start example
Конфигурационный файл Nginx назовем /etc/nginx/sites-available/example. Его минимальный вариант может быть таким:
server {
listen 80;
server_name example;
access_log /var/log/nginx/example.access.log;
error_log /var/log/nginx/example.error.log;
location /static/ {
alias /home/web/proj/static/example/;
}
location / {
include uwsgi_params;
uwsgi_pass unix:///var/run/uwsgi/app/example/socket;
}
}
Точно так же создаем символическую ссылку в /etc/nginx/sites-enabled:
cd /etc/nginx/sites-enabled/
ln -s ../sites-available/example .
И перезапускаем Nginx: /etc/init.d/nginx restart
Под капотом
Сначала вкратце остановлюсь на пераметрах в конфигурационном файле uWSGI. Параметр plugins явно задает версию Python. Параметры chdir и pythonpath добавляют каталог проекта и его родительский каталог в пути поиска Python. Параметры env и module позволяют обойтись без создания специального скрипта для запуска проекта демоном uWSGI. Параметр touch-reload позволяет делать перезагрузку проекта с помощью команды touch /home/web/proj/example/touchme.
Такой лаконичный файл конфигурации для uWSGI получился потому, что стартовый скрипт /etc/init.d/uwsgi использует еще один конфигурационный файл /usr/share/uwsgi/conf/default.ini, в котором для всех остальных необходимых параметров заданы достаточно разумные значения по умолчанию. При необходимости их можно переопределить в /etc/uwsgi/apps-available/example.ini. За удалением некоторых комментариев, содержимое /usr/share/uwsgi/conf/default.ini будет следующим:
[uwsgi]
# try to autoload appropriate plugin if "unknown" option has been specified
autoload = true
# enable master process manager
master = true
# spawn 2 uWSGI worker processes
workers = 2
# automatically kill workers on master's death
no-orphans = true
# write master's pid in file /run/uwsgi/<confnamespace>/<confname>/pid
pidfile = /run/uwsgi/%(deb-confnamespace)/%n/pid
# bind to UNIX socket at /run/uwsgi/<confnamespace>/<confname>/socket
socket = /run/uwsgi/%(deb-confnamespace)/%n/socket
# set mode of created UNIX socket
chmod-socket = 660
# place timestamps into log
log-date = true
# user identifier of uWSGI processes
uid = www-data
# group identifier of uWSGI processes
gid = www-data
В обвязках к uWSGI в Debian введено понятие конфигурационного пространства имен. В default.ini оно представлено с помощью переменной %(deb-confnamespace) со значением по умолчанию app. Имя конфигурационного пространства имен определяется следующим образом: стартовый скрипт /etc/init.d/uwsgi ищет в каталоге /etc/uwsgi все подкаталоги, заканчивающиеся на s-enabled, и часть имени подкаталога, предшествующая s-enabled становится именем конфигурационного пространства для файлов в этом подкаталоге. В нашем случае можно было бы создать в /etc/uwsgi более привычные подкаталоги sites-available и sites-enabled, в которые поместить файл конфигурации example.ini и символическую ссылку на него соответственно; pidfile и socket для этой конфигурации находились бы в /run/uwsgi/site/example/pid и /run/uwsgi/site/example/socket, а управление демоном uWSGI для этой конфигурации осуществлялось бы с помощью команд /etc/init.d/uwsgi start|stop|restart site/example. При отсутствии префикса подразумевается пространство имен app.