Pull to refresh

Развертывание сайта на Джанго, используя FastCGI

Reading time9 min
Views16K

От переводчика


Данную статью я прочитал на Django Advent приуроченному к уже скорому выходу Django 1.2 и она показалось мне настолько интересной, что я решил ее перевести. Далее текст статьи.

Когда разрабатываешь сайт на Джанго, так легко просто открыть консоль и напечатать:

python manage.py runserver

С этой простой командой управления ваши медиа файлы админки сайта поддерживаются правильным образом, PYTHONPATH правильно настроен и включает корневую папку нашего проекта, а так же запущен автоматически перегружающийся веб-сервер на указанном нами порту (от переводчика: по умолчанию порт 8000). Так просто!

Не удивительно, что люди так разочаровываются, когда приходит время положить их сайт на боевой сервер: существует так много шагов в этом процессе и поэтому сложно все их выучить и сделать все правильно. Неудивительно, что вся эта сложность приводит к тому, что написано много статей о развертывании веб-сайта на Джанго. Но почти все из этих статей фокусируются на развертывании сайта используя Apache и mod_wsgi или mod_python.

Однако иногда Apache не идеальное решение. Может быть ваш VPS имеет только 256 МБ памяти, а может быть вы хотите избежать сложности настройки Apache при установке. Или может быть вам просто не нравиться Apache. По любой из этих причин мы можем обратить свое внимание на FastCGI.


Прежде всего


До того, как мы начнем наше развертывание, нам необходимо удостовериться, что основа нашей системы установлена. Во-первых, нам нужен сервер на котором мы развертываем наше приложение. Это может быть любой сервер и операционная система, но ради простоты мы предполагаем, что это Ubuntu Linux.
Давайте установим основу системы, включая некоторые компиляторы, заголовочные файлы для питона и setuptools

sudo apt-get install build-essential python-dev python-setuptools

Поставим так же Nginx в качестве нашего веб-сервера. Это можно сделать так:

sudo apt-get install nginx

Так же следует поставить daemontools – это коллекция инструментов для управления сервисами. Мы будем использовать их, чтобы быть уверенными, что наши сервисы будут оставаться запущенными (или по крайней мере будут возвращаться к жизни) даже в случае ошибки или перезапуска сервера. Чтобы поставить daemontools напечатайте:

sudo apt-get install daemontools

К сожалению пакет daemontools требует нашей небольшой дополнительной работы, чтобы автоматически перезапускаться при перезагрузке. Сначала, создайте файл /etc/event.d/svscanboot со следующим содержимым:

start on runlevel 2
start on runlevel 3
start on runlevel 4
start on runlevel 5
stop on runlevel 0
stop on runlevel 1
stop on runlevel 6
respawn
exec /usr/bin/svscanboot


Затем создайте папку /etc/service , выполнив следующую команду:

sudo mkdir /etc/service

В конце, запустим daemontools, выполнив эту команду:

sudo initctl start svscanboot

Создадим нового пользователя для нашего сайта:

adduser mysite

Если мы хотим использовать команду sudo с нашим пользователем, то нам так же нужно отредактировать /etc/sudoers. Найди в этом файле строку root ALL=(ALL) ALL и под ней добавь:

mysite ALL=(ALL) ALL

Сейчас мы можем переключиться на нашего пользователя:

su - mysite

Итак, основа нашей системы готова. Мы умышленно не рассказываем про базу данных, почтовые сервера, системы контроля версий, memcached и другие различные сервисы, потому что они могут сильно варьироваться в зависимости от персональных предпочтений.

Настройка нашей виртуальной среды для Python


Сейчас, когда основа нашей системы установлена, мы можем сфокусироваться на интересных вещах. Во-первых, мы собираемся поставить virtualenv – это инструмент для создания изолированной среды для Python. Мы будем использовать virtualenv для создания изолированной среды для нашего приложения.

sudo easy_install virtualenv

С нашей свежей копией virtualdev мы можем пойти дальше и настроить новую виртуальную среду:

mkdir ~/virtualenvs
virtualenv ~/virtualenvs/mysite


Мы создали директорию virtualenvs в нашей домашней папке и внутри нее мы создали виртуальную среду под названием mysite. Сейчас давайте начнем использовать ее и происталлируем pip, чтобы легко инсталлировать питоновские пакеты:

source ~/virtualenvs/mysite/bin/activate
easy_install pip


Сейчас нам необходимо убедиться, что у нас установлен пакет Flup. Это набор полезных инструментов для работы с WCGI приложениями, включая адаптер для превращения WSGI приложения в FastCGI (и SCGI, и AJP… но это за пределами данной статьи). Джанго требует, чтобы Flup был проинсталлирован до того, как вы сможете использовать команду управления runfcg. Используя pip мы можем легко проинсталлировать его:

pip install flup

Если мы хотим использовать адаптеры базы данных, графические библиотеки или xml парсеры, инсталлированные по системному пути Питона, мы должны убедиться, что они доступны из нашей виртуальной среды. Для этого мы добавляем .pth файл в директорию виртуальной среды site-packages:

echo "/usr/lib/python2.6/dist-packages/" > ~/virtualenvs/mysite/lib/python2.6/site-packages/fix.pth

Следующий шаг – клонировать код Джанго на наш сервер (очевидно git может быть заменен на mercurial, svn или даже rsync):

git clone github.com/myusername/mysite.git

Если у вашего проекта есть pip requirements файл, сейчас вы можете его использовать:

pip install -U -r mysite/requirements.txt

Или, если у вас нет requirements file, вы можете проинсталлировать зависимости вручную. Например:

pip install -U Django simplejson python-memcached

Выбор опций для нашего FastCGI сервера


Вау! Мы прошли длинный путь настройки нашей системы и даже еще не рассказали о FastCGI части. Не бойся, мы готовы сделать это сейчас. Давай определимся, какие опции мы хотим иметь, когда мы запустим наш FastCGI сервер.

Первый выбор, который нужно сделать – какой метод распараллеливания мы хотим использовать:
  • threaded:
    Выполнение потокового сервера в одном процессе для всех HTTP запросов. Это сохраняет много памяти, но все потоки упираются в проблему одного Global Interpreter Lock (GIL). Это означает, что производительность может быть ограничена интенсивной процессорной загрузкой. Заметьте, что операции ввода-вывода происходят за пределами GIL, поэтому интенсивная загрузка операциями ввода-вывода не упирается в проблему GIL. Так же некоторые расширения питона не предполагались быть потоко-безопасными, что означает, что они не могут быть использованы с этим методом конкуренции.
  • prefork:
    Выполнение разветвляющего сервера порождающего пул процессов, каждый из которых со своей собственной копией джанги и питона загруженного в память. Это означает, что будет использоваться больше памяти, но нет вышеупомянутых проблем с GIL или потоко-безопасностью.

Давайте предположим, что нам интересен FastCGI, потому что у нас сервер с маленьким размером памяти. Так как разветвляющий (prefork) метод будет использовать больше памяти, поэтому мы выберем потоковый (threaded) метод.

Сейчас мы выберем несколько опций, которые говорят серверу, как ему действовать под нагрузкой:
  • minspare:
    Какое минимальное кол-во процессов/потоков сервер будет поддерживать готовыми и ожидающими будущих запросов?
  • maxspare:
    Какое максимальное кол-во процессов/потоков сервер будет поддерживать готовыми и ожидающими будущих запросов?
  • maxrequests (только для prefork метода):
    Как много запросов каждый процесс будет обслуживать до того, как он будет убит и запущен заново. Чтобы предотвратить утечки памяти, до того момента как это станет проблемой. Это хорошая идея установить эту опцию.
  • maxchildren (только для prefork метода):
    Как много дочерних процессов могут поддерживать запросы в любое заданное время?

Наш сервер работает на небольшом VPS с 265 МБ памяти, поэтому мы выберем очень скромные настройки: 2 для
minspare, 4 для maxspare, 6 для maxchildren и 500 для maxrequests.

В конце мы выбираем наши последние несколько настроек:
  • host
    Имя хоста (hostname), который будет слушать входящие соединения?
  • port
    На каком порту слушать входящие соединения?
  • pidfile
    Когда FastCGI сервер стартует, он создает файл со своим идентификатором процесса (process ID). Этот process ID – pid главного потока/процесса. Это процесс который будет поддерживать сигналы OS, например SIGHUP. Эта опция определяет расположение этого файла.

После того, как мы сделали наши выборы, мы можем запустить сервер выполнив runfcgi команду:

python manage.py runfcgi method=threaded host=127.0.0.1 port=8080 pidfile=mysite.pid minspare=4 maxspare=30 daemonize=false

Заметьте, что мы добавили флаг daemonize=false. Он всегда должен быть установлен (по мнению автора пропуск этой опции является просчетом в команде runfcgi). Так же заметьте, что результатом выполнения этой команды будет создан mysite.pid файл в нашей директории проекта, так что это хорошая идея удостовериться, что ваша система контроля версий игнорирует этот файл.

Сейчас, когда мы проверили, что наш FastCGI сервер запущен правильно, давайте остановим его и перейдем к следующему шагу: использование daemontools для запуска этой команды и поддержку работы сервера все время в фоновом режиме.

Daemontools выполняет наш FastCGI сервер


Daemontools будет смотреть во все поддиректории в /etc/service директории и в каждой из них он ищет исполняемый файл, называемый run. Если он находит такой файл, то он запускает его и перезапускает его, если он умирает. Итак, давайте создадим mysite директорию:

sudo mkdir /etc/service/mysite

Сейчас, давайте сделаем небольшой скрипт, который запускает наш fastcgi сервер. Используйте ваш любимый текстовый редактор, чтобы записать этот текст в /etc/service/mysite/run:

#!/usr/bin/env bash

source /home/mysite/virtualenvs/mysite/bin/activate
cd /home/mysite/mysite
exec envuidgid mysite python manage.py runfcgi method=threaded host=127.0.0.1 port=8080 pidfile=mysite.pid minspare=4 maxspare=30 daemonize=false


Тут нет ничего хитрого. Сначала мы проверяем, что мы в правильной виртуальной среде (virtualenv), затем мы изменяем текущую директорию на mysite и затем выполняем runfcgi команду, которую мы обсуждали раньше. envuidgid mysite просто убеждается, что следующая команда выполняется под пользователем mysite, вместо root.

Скрипт должен быть исполняемым, чтобы daemontools распознал его, поэтому давайте выполним следующую команду:

sudo chmod +x /etc/service/mysite/run

Сейчас мы можем проверить, что он выполняется, используя команду svstat:

sudo svstat /etc/service/mysite/

Результат должен выглядеть примерно так:

/etc/service/mysite/: up (pid 3610) 33 seconds

Это означает, что процесс поднят, ему присвоен process id = 3610 и он проработал 33 секунды. Вы можете использовать команду svc, чтобы остановить процесс:

sudo svc -d /etc/service/mysite/

Затем, если вы выполните снова svstat, то получите примерно такой вывод:

/etc/service/mysite/: down 4 seconds, normally up

Чтобы вернуть процесс обратно, просто выполните:

sudo svc -u /etc/service/mysite/

Полный список  svc команд можно найти online — это очень хороший источник, если вы собираетесь погрузиться глубже в daemontools.

Конфигурируем Nginx для работы с нашим сервером


Мы уже близко к финишной черте. Все что нам осталось сделать – сконфигурировать nginx для беседы с нашим FastCGI сервером, получения от него ответов и передачи их пользователю.

Ubuntu поставляется с полезным /etc/nginx/fastcgi_params файлом. К сожалению, он не совсем правильный. Он кодирует (encode) SCRIPT_NAME параметр, но то, что наш сервер реально хочет – PATH_INFO. Вы можете выполнить поиск и замену или скопировать содержимое ниже в /etc/nginx/fastcgi_params файл:

fastcgi_param QUERY_STRING $query_string;
fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param CONTENT_TYPE $content_type;
fastcgi_param CONTENT_LENGTH $content_length;

fastcgi_param PATH_INFO $fastcgi_script_name;
fastcgi_param REQUEST_URI $request_uri;
fastcgi_param DOCUMENT_URI $document_uri;
fastcgi_param DOCUMENT_ROOT $document_root;
fastcgi_param SERVER_PROTOCOL $server_protocol;

fastcgi_param GATEWAY_INTERFACE CGI/1.1;
fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;

fastcgi_param REMOTE_ADDR $remote_addr;
fastcgi_param REMOTE_PORT $remote_port;
fastcgi_param SERVER_ADDR $server_addr;
fastcgi_param SERVER_PORT $server_port;
fastcgi_param SERVER_NAME $server_name;


Сейчас мы создадим определение для нашего сайта. Используя текстовый редактор, давайте создадим файл /etc/nginx/sites-available/mysite со следующим содержанием:

server {
listen 80;
server_name mysite.com www.mysite.com;
access_log /var/log/nginx/mysite.access.log;

location /media {
autoindex on;
index index.html;
root /home/mysite/mysite;
break;
}
location / {
include /etc/nginx/fastcgi_params;
fastcgi_pass 127.0.0.1:8080;
break;
}
}


Он говорит слушать порт 80 (стандарт для HTTP) для mysite.com и http://www.mysite.com/. Запросы для /media следует обрабатывать сразу с диска из /home/mysite/mysite/media директории. И самое важное: все остальные запросы будут передаваться через FastCGI нашему серверу.

Сейчас давайте подключим его через symlink:

sudo ln -s /etc/nginx/sites-available/mysite /etc/nginx/sites-enabled/mysite

Наконец перезапускаем nginx, чтобы новые настройки вступили в силу:

sudo /etc/init.d/nginx restart

Заключение


Мы настроили минимальный сервер, используя nginx, чтобы обслуживать media файлы на умопомрачительной скорости и чистый питоновский FastCGI сервер, чтобы обслуживать динамические запросы без каких-либо промежуточных слоев между ними. Используя daemontools мы имеем полный контроль над FastCGI процессом и можем останавливать его, перезапускать или изменять его настройки в любой момент.

По настоящему интересная вещь – достаточно только нескольких маленьких подстроек и этот же самый стэк мог бы использоваться для решений на базе gunicornspawning, или paste. Вместо использования fastcgi_pass мы могли бы использовать proxy_pass. Мы могли бы по прежнему использовать daemontools, чтобы поддерживать наш процесс в рабочем состоянии и контролировать его. Почти каждый шаг этой статьи останется тот же самый.
Это очень жизнеспособная альтернатива часто навязываемому стэку из Apache/mod_wsgi и надеюсь после прочтения этой статьи больше людей будут рассматривать его, как метод развертывания своего сайта на джанго.

Об авторе статьи


Eric Florenzano является разработчиком программного обеспечения. В настоящий момент он живет в San Francisco и работает в Mochi Media. На данный момент он участвует в Django и Pinax коммъюнити несколько лет. В настоящий момент он является со-основателем Django Dose, подкасты о всем, что связано с Django. Время от времени он ведет свой блог на www.eflorenzano.com о питоне, нереляционных базах данных и других интересных темах.
Tags:
Hubs:
+39
Comments35

Articles