Май 01, 2013
Деплой Django-приложения на сервере с использованием Nginx, Gunicorn и Python 3.x

Понадобилось развернуть приложение 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/

Теги: , ,
Добавить комментарий:
Комментариев: (4)
Рейтинг: 1 Опубликовал Максим 4082 дн. назад
Не нравится Нравится
Вместо >>Сделаем релогин для того, чтобы прочитались изменения в .bashrc. можно просто source .bashrc
Рейтинг: 0 Опубликовал admin 4080 дн. назад
Не нравится Нравится
Да, спасибо за замечание.
Рейтинг: 1 Опубликовал zavx0z 3972 дн. назад
Не нравится Нравится
Во время команды установки виртуального окружения происходит - OSError: Command /home/zavx0z/project...t/venv/bin/python3.3 -c "import sys, pip; pip...ll\"] + sys.argv[1:])" setuptools pip failed with error code 1
Рейтинг: -2 Опубликовал mesaa 3811 дн. назад
Не нравится Нравится
Для прокси идеально подойдет vds сервер от cloudmouse.com Можно хоть на час взять, а настраивать то 5 минут туннель, в итоге выйдет меньше 1$ и за приватный персональный vds от http://cloudmouse.com
Опубликовать