Понадобилось развернуть приложение Django, работающее на Python 3.x, на VPS-сервере, на котором уже есть запущенные приложения на Python 2.6. Но проблема заключалась в том, что используемый apache-модуль mod_wsgi может работать только с той версией интерпретатора, для которой был скомпилирован.
После поиска альтернатив было принято решение для формирования динамических HTTP-ответов использовать сервер Gunicorn, так как по отзывам кроме хорошей производительности и возможности запускать приложения на различных версиях CPython, он также поддерживает такую реализацию интерпретатора, как PyPy, что может пригодиться в будущем. В качестве обратного прокси я решил поставить Nginx, а для запуска и перезапуска сервера Gunicorn выбрал систему Supervisor.
Начальные условия
- VPS с Debian "squeeze";
- Проект на Django 1.5.1
Дополнительные цели, которых я хотел достичь:
- Возможность установки нескольких версий интерпретатора на сервер и запуск приложений на них;
- Интерпретаторы должны работать независимо друг от друга и не должны влиять на работу системного интерпретатора;
- Возможность иметь изолированные окружения для каждого приложения со своими версиями пакетов
Начнем.
Установка Python 3.x
Зайдем на сервер под своим именем пользователя (предполагается, что на сервере уже создан хотя бы один пользователь кроме root'a).
Для начала поставим сам интерпретатор. Есть несколько путей установки Python 3.x на Debian 6, среди которых компиляция вручную из исходников, создание или поиск .deb файла или бэкпортирование из experimental ветки. Но я выбрал другой вариант, с использованием менеджера инсталляций pythonz, т.к. он предоставляет удобные инструменты для управления установкой различных версий Python.
Для установки pythonz понадобится curl:
$ sudo apt-get update
$ sudo apt-get install curl
Ставим pythonz:
$ curl -kL https://raw.github.com/saghul/pythonz/master/pythonz-install | bash
После установки для удобства обращения к pythonz добавим в файл .bashrc в папке нашего пользователя:
# ~/.bashrc
[[ -s $HOME/.pythonz/etc/bashrc ]] && source $HOME/.pythonz/etc/bashrc
Сделаем релогин для того, чтобы прочитались изменения в .bashrc.
Прежде чем начать сборку Python'a, установим компилятор gcc, а также все зависимости, необходимые для компиляции Python'a:
$ sudo apt-get install gcc build-essential libbz2-dev libsqlite3-dev zlib1g-dev libxml2-dev libxslt1-dev libgdbm-dev libgdb-dev libxml2 libssl-dev tk-dev libexpat1-dev libncursesw5-dev
Используя pythonz, собираем Python (в момент написания заметки последняя версия 3.3.1):
$ pythonz install -t cpython 3.3.1
Используя собранный Python, устанавливаем пакет distribute, для того чтобы иметь возможность удобного управления пакетами:
$ curl -O http://pypi.python.org/packages/source/d/distribute/distribute-0.6.36.tar.gz
$ tar -xzvf distribute-0.6.36.tar.gz
$ cd distribute-0.6.36
$ ~/.pythonz/pythons/CPython-3.3.1/bin/python3.3 setup.py install
И устанавливаем пакет virtualenv для создания виртуального окружения проекта.
$ cd ~/.pythonz/pythons/CPython-3.3.1/bin
$ ./easy_install virtualenv
Настройка связки Django + Gunicorn + Nginx
Создаем виртуальное окружение для проекта:
$ ./virtualenv --python=./python3.3 --no-site-packages ~/projects/myproject/venv
$ source ~/projects/myproject/venv/bin/activate
Ставим Gunicorn и Django:
$ easy_install gunicorn django
После переноса файлов проекта на сервер, в папку ~/projects/myproject (при помощи Ftp или Git), устанавливаем зависимости, синхронизируем модели с базой данных и собираем статические файлы. Предварительно, для корректной установки в будущем python-коннектора mysql, установим также библиотеку libmysqlclient-dev.
$ sudo apt-get install libmysqlclient-dev
$ cd ~/projects/myproject
$ pip install --requirement=./requirements.conf
$ python manage.py syncdb
$ python manage.py collectstatic
Устанавливаем Supervisor для управления запуском Gunicorn:
$ sudo apt-get install supervisor
Создаем в папке /etc/supervisor/conf.d/ конфигурационный файл myproject.conf для запуска приложения:
[program:myproject]
command=/home/username/projects/myproject/venv/bin/gunicorn myproject.wsgi:application --bind=127.0.0.1:8080 --workers=3 --pid=/home/username/projects/myproject/pids/gunicorn.pid --log-file=/home/username/projects/myproject/logs/gunicorn.log
directory=/home/username/projects/myproject
umask=022
autostart=true
autorestart=true
startsecs=10
startretries=3
exitcodes=0,2
stopsignal=TERM
stopwaitsecs=10
user=username
environment=PYTHON_EGG_CACHE="/home/username/projects/myproject/.python-eggs"
Где:
command - команда запуска приложения через wsgi-интерфейс (как рекомендуется в документации для Django >= 1.4 вместо использования команды "manage.py run_gunicorn") с количеством процессов, обслуживающих запросы, равным трем (вычисляется как 1 + 2 * количество_CPU на сервере), с указанием локального адреса и порта для прослушивания 8080;
directory - директория, в которую переключится Supervisor при запуске процесса;
user - пользователь, от имени которого запускается Gunicorn;
environment - переменные среды в виде KEY="val",KEY2="val2", которые будут установлены при запуске проекта.
После создания конфигурационного файла, можно просто перезагрузить Supervisor для того, чтобы он загрузил конфигурацию нового процесса:
$ sudo supervisorctl reload
И проверяем, успешно ли запустился процесс:
$ sudo supervisorctl status
Если всё в порядке, статус процесса будет "RUNNING".
Теперь настроим Nginx для получения внешнего доступа к приложению. Создаем в папке /etc/nginx/conf.d файл myproject.conf со следующим содержимым:
server {
listen 80;
server_name myproject.su www.myproject.su;
# редирект на адрес без "www"
if ( $host = 'www.myproject.su' ) {
rewrite ^/(.*)$ http://myproject.su/$1 permanent;
}
root /home/username/projects/myproject/www;
access_log /home/username/projects/myproject/logs/nginx_access.log;
# статические файлы обрабатываются напрямую
location ~* ^.+\.(bmp|jpg|jpeg|pjpeg|gif|ico|png|css|doc|txt|js|docx|rtf|ppt|pdf|swf|zip|rar|gz)$ {
expires 10d;
log_not_found on;
try_files $uri
/home/username/projects/myproject/www/$uri;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /var/www/nginx-default;
}
location / {
# направляем запрос Gunicorn'у (у каждого приложения свой порт)
proxy_pass http://127.0.0.1:8080/;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
client_max_body_size 10m;
client_body_buffer_size 128k;
proxy_connect_timeout 90;
proxy_send_timeout 90;
proxy_read_timeout 90;
proxy_buffer_size 4k;
proxy_buffers 4 32k;
proxy_busy_buffers_size 64k;
proxy_temp_file_write_size 64k;
}
}
И перезапускаем Nginx:
$ sudo /etc/init.d/nginx restart
Вот и всё. Теперь Django-приложение работает на сервере, используя интерпретатор Python 3.3. Сервер Gunicorn установлен изолированно, что позволяет запускать множество приложений в разных окружениях, не зависящих друг от друга. Основные и дополнительные цели достигнуты.
Полезные ссылки:
http://habrahabr.ru/post/159575/
http://blog.djangofan.ru/2012/04/gunicorn-nginx-django-ubuntu.html
http://senko.net/en/django-nginx-gunicorn/
https://docs.djangoproject.com/en/dev/howto/deployment/wsgi/gunicorn/