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

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


    Данную статью я прочитал на 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 о питоне, нереляционных базах данных и других интересных темах.
    Ads
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More

    Comments 35

      0
      Хорошая статья. Хоть и не использую Джанго увидел FastCGI и нет смотрю волшебного слова nginx в преамбуле и в ключевых словах — полез читать как реализовано. Ан нет — без nginx и тут не обошлось )
        +2
        lighttpd/cherokee рекомендуется разработчиками, а про nginx на сайте Django ни слова, если не ошибаюсь
        почему был выбран именно nginx не очень понятно, не думаю что тот же lighttpd чем-то хуже был бы…
          0
          Дело вкуса, видимо. Я взял cherokee + uwsgi, в первую очередь и из-за хорошей документации и большого сообщества. Ну и потому, что на 160 мб vps (вместе с mysqld) крутится это оч шустро.
            0
            Я думаю Nginx или lighthttpd — дело вкуса. Nginx мне лично более знаком и нравиться, да и с Игорем Сысоевым можно даже лично поговорить, если уж очень надо :) Он кстати сам говорит, что легковестные веб-сервера нет смысла пытаться ускорить, так как они в основном большей частью вызывают функции нижележащей операционной системы. Так что более важно настраивать ее :) И еще один момент — доля серверов на nginx в мире постоянно растет и сейчас уже достигает 8%, если верить этой информации: www.linux.org.ru/news/russia/4373637, а lighthttpd заметно отстает.
            0
            Спасибо! Был без интернета сутки, не мог сразу ответить :)
            0
            Если мы хотим использовать команду sudo с нашим пользователем, то нам так же нужно отредактировать /etc/service

            Подозреваю, речь шла об /etc/sudoers
              +2
              Оставляя в стороне всякие незначительные стилистические моменты типа вопросительных знаков в описаниях опций и странности в обращении к читателю (то на «ты», то на «вы», причем именно с маленькой буквы), хочется заметить, что перевод пары терминов на русский (на мой вкус) выглядит спорным:
              — «метод конкуренции» — неудачная калька с английского. Метод распараллеливания, способ одновременного исполнения,…
              — «и это очень хорошее чтение» — под «чтением» обычно подразумевают процесс. «Чтиво» (если просторечно) или, например, источник.

              Сам откладывал чтение Advent'а до выхода последней статьи, поэтому Ваш перевод прочитал раньше оригинала. Большое спасибо за труд; сборка советов очень грамотная, поэтому буду использовать сам и рекомендовать в качестве памятки.
                0
                Спасибо за ценные замечания! Спорные моменты поправил. Заменил везде «ты» на «вы». Но с большой буквы вы писать не обязательно, насколько мне подсказывают филологи :)
                0
                Автор, поправь на /etc/sudoers.
                Сбивает с толку.
                  0
                  Поправил! Спасибо!
                –1
                Спасибо! Очень актуально в ходе развертывания бета-версии проекта на django.
                  0
                  Спасибо! Рад, что оказалось полезно!
                  +1
                  FastCGI в режиме threaded не очень стабилен. GIL опять зря приплели.
                    0
                    Можешь ли указать источник информации, где можно об этом почитать. Интересно
                    0
                    FastCGI очень медленный, как бы его не называли :)
                    А nginx поддерживает быстрый WSGI. Например, так: www.rkblog.rk.edu.pl/w/p/hosting-django-under-nginx-scgi-and-wsgi/
                      –2
                      Нельзя говорить, что FastCGI очень медленный. Он быстрый, просто WSGI еще быстрее.
                        +1
                        blog.locum.ru/lab/fastcgi_protiv_wsgi
                        конечно на специфичном оборудовании, но все же в 20 раз быстрее
                          +1
                          Александр Кошелев там в первом же комментарии весьма аргументировано, как мне кажется, высказался, почему тот тест лучше вообще не читать
                            0
                            По прошествии некоторого времени я бы наверно там в своем комментарии немного снизил градус «наезда», но по сути да — тест очень странный.
                        0
                        Тут вы совсем не правы. Нельзя сравнивать WSGI и FastCGI — они на разных уровнях ответственности оперируют. Первый это по сути дырка, а второй это провод с переходником который в неё втыкается.

                        Потом, mod_wsgi в nginx не работает. Вот просто не работает по одной просто причине — любая обработка запрос, которая уходит в mod_wsgi лочит весь воркер nginx. Если для раздачи файлов (а nginx до недавнего времени лочил воркеры и на этой операции из-за синхронного обращения к диску) это не критично, так как файлы обычно маленькие и отдаются зачастую напрямую из файлового кеша OS, то при мало-мальски серьезном приложении внутри mod_wsgi это становится большой проблемой.

                        По этой же причине проект по сути и умер.
                          0
                          а в других серверах, lighttpd или apache — работает?
                            0
                            Сам не пробовал, но точно знаю, что Джанго приложения вполне хорошо живут под lighttpd. Тот же блог Ивана Сагалаева (http://softwaremaniacs.org/about/) работает под lighttpd.
                              +1
                              В lighttpd нет mod_wsgi (а если бы и был по той же архитектуре построен, то обладал бы теме же проблемами что и в nginx только ещё хуже — у лайти один воркер вообще), а в apache да есть, и да работает.
                          +3
                          По технической части-то все здорово в этой статье.

                          Только непонятно все-таки, чем апач-то не угодил. Поднадоели уже все эти мифы: апач медленный, апач жрет кучу памяти, апач сложно настраивать и т.д. Оверхед апача на запуск джанги под mod_wsgi = метров 5 сразу + потом по паре сотен килобайт на процесс джанги (на процесс, не на тред). Это безо всяких перекомпиляций и тд, стандартный апач из дебиана, без каких-либо сакральных знаний о секретных натсройках. Средний процесс джанги метров 20 у меня занимает, тут +- пара сотен килобайт — совсем не существенно. Причем я совсем не уверен, что у fastcgi+flup оверхед меньше.

                          По скорости уже не знаю что нужно делать, чтобы упереться не в обработку запросов, а в скорость апача (и получить сколь-либо заметный выигрыш, перейдя на что-то еще),

                          По настройке тоже куча статей написано, ничего там сложного.

                          Страшные цифры для апача получают по нескольким причинам:

                          а) люди не отключат mod_php => в каждый процесс еще грузится здоровенный mod_php
                          б) неправильное использование mpm_worker без понимания того, как это работает. Можно использовать daemon mode и не париться ни о чем.
                          в) контейнер OpenVZ (см. habrahabr.ru/blogs/hosting/53236/)

                          Зато с апачем имеем самый стабильный и отточенный способ запуска джанги, и это, как мне кажется, важно. Хотя, возможно, это тоже миф, что flup глючит, дела с ним иметь не приходилось. Но даже если и не глючит, преимущества все равно неочевидны.
                          • UFO just landed and posted this here
                              0
                              +1
                              я свои скрипты написал на шелле
                              0
                              Да Коллега я согласен, но у меня часть сайтов на пхп а чать на джанге, поэтой причине я не могу поставить два апача. поэтому нгикс. (
                                0
                                Эмм… а кто мешает поставить _один_ апач запустив и php и django через fastcgi?
                              0
                              Спасибо огромное! Статья очень помогла!
                                0
                                пожайлуста, очень рад!
                                0
                                А чем не устраивает вариант запуска через /etc/inittab?
                                И геморроя меньше, и init следит за процессом, чтобы не упал, а в случае падения, сам его вежливо поднимет?
                                ap:2345:respawn:/bin/su apteka -c "/www/apt2/run/server.sh"
                                  0
                                  Ценю Ваш труд. Тем не менее, лучше грамотный английский текст, чем безграмотный перевод. В данном случае удручающее качество не оправдывается количеством. Пожалуйста, прогоните статью через ворд какой-нибудь или через знакомых. Лексика, стиль, орфография, пунктуация таковы, что… читать больно, чесслово.

                                  (Надеюсь, Вы не обижаетесь на интепретаторы, компиляторы и читателей за внимание к синтаксису и пунктуации.)
                                    0
                                    Нет, не обижаюсь. Конкретные замечания были бы ценнее, если вы можете выделить особенно задевшее.

                                    Я сам получил большой отклик и много информации по теме, да и многим статья оказалась полезной.
                                    0
                                    Я использовал вместо /etc/event.d/svscanboot /etc/init/svscanboot.conf в Ubuntu 10.4. Это нормально? Иначе не работал initctl start svscanboot.
                                      0
                                      Извини, но не профи в этом вопросе. Не смогу подсказать точно, но насколько я знаю убунту, можно и так.

                                    Only users with full accounts can post comments. Log in, please.