Pull to refresh

Боевой сервер для Django-приложения: Ubuntu Server 10.04 LTS + django 1.4 + nginx + gunicorn

Reading time 11 min
Views 121K
Многие учебные пособия по разработке на Django раскрывают как быстро получить работающий отладочный сервер (python manage.py runserver), а вопрос развертывания в боевом режиме часто остается нераскрытым или освещаются далеко не самые простые и эффективные методы.
Ниже я расскажу о об одном из способов развернуть сайт на Django в боевом режиме, начиная от выбора хостинга, заканчивая развертыванием веб-сервера. Таким образом статья может быть полезна тем, кто освоил разработку на базе Django, но не имеет опыта развертывания серверов. Мой способ один из многих, но он достаточно прост, эффективен в работе и легок в поддержке. Используем VPS-хостинг, Ubuntu 10.04, nginx, gunicorn.



Оглавление




Структура проекта


Для дальнейшего повестования необходимо рассказать про структуру развертываемого проекта:
- myproject/ - корень проекта, обратите внимание, что это не корень исходников проекта;
    - env/ - вируальное окружение, создаваемое при помощи virtualenv;
    - src/ - папка с иходниками, ее удобно добавить в пути IDE, для того, чтобы обращаться к пакету myproject по имени;
        - myproject/ - корень исходников проекта, должна быть знакома Django-программистам;
            - __init__.py
            - settings.py
            - manage.py
            - urls.py
            - myapp1/
            - myapp2/
             ...
    - static_content/ - папка для файлов, которые раздает веб-сервер первого эшелона (у нас - nginx), будучи параноиком, я боюсь передавать веб-серверу папку, содержащую исходники проекта;
        - static/ - статические файлы проекта (то что храним в репозитории);
        - media/ - media-файлы, то что создается сайтом в ходе его функционирования;
    - docs/ - тут лежат полезные документы, комментарии, примеры конфигов;
    - logs/ - папка, куда складываю логи, касающиеся работы сайта;
    - pids/ - тут pid-файлы.

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

О хранении исходников
В рамках статьи не раскрывается вопрос хранения исходников проекта, поскольку выбор, организация, настройка доступа репозитория — отдельная тема для статьи.


Хостинг


Для себя я давно выяснил, что наиболее удобный хостинг для django — это VPS хостинг. Перепробовав несколько VPS-хостингов, я остановился на наиболее приемлемом в соотношении цена/качество — FirstVDS. Я пользуюсь тарифом за 400 рублей в месяц — 1500 MB RAM, 24000 MB HDD. Но сначала обходился тарифом за 150 рублей (в этом случае нужно будет научить некоторые приложения потреблять меньше ресурсов).
Итак, для того, чтобы начать нужно заказать и оплатить VPS, выбрав при этом тип виртуализации OpenVZ — неполная имитация физической машины, но для наших задач вполне достаточная, стоит заметить, что при такой виртуализации за те же деньги памяти дают больше, пригодится для memcached. В качестве операционной системы рекомендую выбирать Ubuntu Server 10.04 LTS#. Доступна также и Ubuntu Server 12.04 LTS, мне без особого труда удалось ввести ее в строй, однако шаблон на хостинге появился недавно, да и специалисты поддержки хостинг-провайдера не рекомендуют пока ее ставить на боевые сервера. Сейчас использую ее только на тестовом сервере.

Альтернативные Ubuntu Server операционные системы для хостинга
Наверняка найдутся адвокаты более стабильных систем для хостинга: FreeBSD, Debian, RedHat, CentOS. Не буду ввязываться в холивар. Замечу лишь, что мой опыт показывает, что для разработки на Django, часто требуются довольно новые версии пакетов и приложений. На перечисленные системы эти пакеты встают после подпирания множеством костылей, что, в итоге, сводит на нет ее надежность. В Ubutnu же новые версии пакетов принимаются в репозитории гораздо быстрее.


После заказа и оплаты на почту приходит SSH-доступ, теперь можно приступать к настройке операционной системы.

Ubuntu server


Получив доступ, я, первым делом, обновляю систему, затем отключаю root-доступ по SSH.
Подключиться по SSH в Windows можно широкоизвестной программой Putty, но я использую ее более “накрученную” модификацию — kitty. В качестве IP-адреса сервера вводим высланный хостером адрес, подключаемся, вводим root в качестве user и пароль при запросе.
Для подключения в Линукс-подобных системах используем родной терминал (Terminal, Konsole, etc.), набрав в нем нехитрую команду:
ssh root@1.2.3.4

где 1.2.3.4 — IP-адрес, высланный хостером, при запросе вводим пароль, высланный хостинг-провайдером.
Первым делом сменим пароль рута, паранойи ради:
passwd

по запросу вводим новый пароль дважды, генерировать и хранить советую в KeePassX, не стесняйтесь сделать его посложнее (>20 символов), использовать его почти не придется.
Обновляем пакеты в системе:

apt-get update
apt-get upgrade

Добавляем пользователя, от лица которого будем развертывать сервер:
adduser myuser

можно ничего не вводить кроме пароля, но лучше его придумать посложнее
Теперь понадобится редактор для правки конфигов, я предпочитаю nano:
apt-get install nano

Добавляем пользователю права исполнять команды от имени root (sudo):

nano /etc/sudoers
# находим строку
root    ALL=(ALL:ALL) ALL
# сразу после нее вставляем
myuser    ALL=(ALL:ALL) ALL

Теперь отключаем возможность пользователю root логиниться по SSH (делать необязательно, мера безопасности):

nano /etc/ssh/sshd_config
# ищем
PermitRootLogin yes
# меняем на
PermitRootLogin no

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

Спустя минуту можно логиниться как myuser (или можете предварительно проверить, что логин рутом больше не работает):
ssh myuser@1.2.3.4


Теперь я ставлю ряд системных пакетов, необходимых для работы Django-приложения и инфраструктуры:
sudo apt-get install gcc mysql-server python-mysqldb memcached mercurial python-profiler w3m python-setuptools libmysqlclient-dev git-core python-dev rabbitmq-server supervisor nginx

Давайте разберем, что есть что:
  • gcc — компилятор C, C++, чего-то еще до кучи, определенно понадобится для сборки некоторых пакетов;
  • python-setuptools — установка команды easy_install, понадобится только для установки virtualenv, дело в том, что из системных пакетов virtualenv устанавливается не последней версии, с чем возникает ряд раздражающих проблем;
  • mysql-server — из названия ясно, что это MySQL-сервер, понадобится нам, раз мы собираемся поднимать сервер баз данных на хосте веб-приложения;
  • libmysqlclient-dev — этот пакет понадобиться для того, чтобы собрать интерфейс доступа к MySQL для python;
  • python-dev — API python’а, определенно понадобится для сборки некоторых пакетов;
  • supervisor — это программа — контроллер демонов, понадобится для приручения веб-сервера и, возможно, ряда других программ;
  • nginx — веб-сервер nginx, выражаю свою признательность Игорю Сысоеву;
  • rabbitmq-server — необязательно, AMQP-сервер, пригодится, если потребуется использовать celery;
  • memcached — необязательно, известная реализация кеша в оперативной памяти;
  • git-core — необязательно, клиент для системы контроля версий git, потребуется, если нужные пакеты хранятся в git-репозитории;
  • mercurial — необязательно, клиент для системы контроля версий mercurial (hg), потребуется, если нужные пакеты хранятся в mercurial-репозитории.


Развертывание проекта


Для развертывания воспользуемся виртуальным окружением, при помощи инструмента virtualenv.
В предыдущей главе мы установили полезную утилиту easy_install, с помощью которой установим теперь virtualenv последней версии:
sudo easy_install virtualenv

Примечание. Я намеренно не ставил pip глобально, чтобы не было случайных конфликтов с ним у pip’ов в виртуальных окружениях.
Теперь можно приступить к развертыванию проекта. Для начала нужно разместить его где-то в домашней директории, пусть это будет:
/home/myuser/web/myproject

Я пользуюсь системой контроля версий git. Но вы можете добиться этого простым копированием по SCP. В Windows можно использовать WinSCP, в линуксах файловые менеджеры поддерживают формат адреса sftp://myuser@1.2.3.4, как вы наверное догадались, в качестве аутентификационных используются данные для доступа по SSH.
В моем проекте в корне лежит файл для сборки виртуального окружения — build_env.sh:
#!/bin/bash
echo $0: Creating virtual environment
virtualenv --prompt="<myenv>" ./env

mkdir ./logs
mkdir ./pids
mkdir ./db
mkdir ./static_content
mkdir ./static_content/media

echo $0: Installing dependencies
source ./env/bin/activate
export PIP_REQUIRE_VIRTUALENV=true
./env/bin/pip install --requirement=./requirements.conf --log=./logs/build_pip_packages.log

echo $0: Making virtual environment relocatable
virtualenv --relocatable ./env

echo $0: Creating virtual environment finished.

и файл requirements.conf, который содержит пакеты, необходимые для работы приложения:
django
git+git://github.com/sehmaschine/django-grappelli.git#egg=django-grappelli
git+git://github.com/django-mptt/django-mptt.git#egg=django-mptt
git+git://github.com/krvss/django-social-auth.git#django-social-auth
git+git://github.com/gabrielhurley/django-wymeditor.git#django-wymeditor
git+git://github.com/jtauber/django-mailer.git#django-mailer
git+git://github.com/tweepy/tweepy.git#tweepy
django-celery
django-debug-toolbar
django-pdb
python-memcached
MySQL-python 
xlrd
unidecode
anyjson
gunicorn
pillow
south
fabric
requests
xlwt

Это возможный набор пакетов, как-нибудь расскажу подробнее про некоторые из них, но мы не углубляемся в аспекты разработки в этой теме.
Итак, после запуска в папке проекта /home/myuser/web/myproject:
./build_env.sh

устанавливается виртуальное окружение для работы нашего сервера.
Теперь нужно позаботиться о создании базы данных. В наших проектах мы используем MySQL. Запускаем консоль MySQL:
mysql -uroot -pROOTPASSWORD

где создаем базу и пользователя:
CREATE DATABASE myproject CHARACTER SET utf8 COLLATE utf8_general_ci;
CREATE USER 'myproject'@'localhost' IDENTIFIED BY 'USERPASSWORD';
GRANT ALL PRIVILEGES ON myproject.* TO ‘myproject'@'localhost';

теперь настройки доступа можно внести в файл настроек проекта (settings.py или local_settings.py).
Если у вас есть дамп базы — вы знаете, что делать. Если нет, то создаем в ней схему и необходимые данные:
cd ~/web/myproject              # возвращаемся в папку проекта
source env/bin/activate         # активируем виртуальное окружение
python manage.py syncdb    # синхронизируем модели с базой данных

теперь соберем статические файлы:
python manage.py collectstatic

На этом приложение готово к запуску.

Настройка веб-сервера Nginx


Для настройки веб-сервера Nginx нужно разместить конфиг myproject.conf (суффикс должен быть .conf) в папке /etc/nginx/sites-available/.
Создать и отредактировать файл можно в консоле SSH через nano:
sudo nano /etc/nginx/sites-available/myproject.conf

затем вставляем содержимое примера ниже (Shift+Insert), исправляем под свои нужды, сохраняем (Ctrl+O), выходим (Ctrl+X).
Пример содержания файла myproject.conf:
upstream myproject.ru {
    server localhost:12345 fail_timeout=0;
}

server {
    listen 80;
    server_name  www.myproject.ru;
    rewrite ^/(.*) http://myproject.ru/$1 permanent;
}

server {
    listen 80;
    client_max_body_size 4G;
    server_name myproject.ru;
    access_log  /home/myuser/web/myproject/logs/myproject.access.log;
    keepalive_timeout 5;

    root /home/myuser/web/myproject/static_content;
    
    location / {
        proxy_pass http://myproject.ru;
    }

    error_page 500 502 503 504 /500.html;
    location = /500.html {
        root /home/myuser/web/myproject/static_content/static/html;
    }

    location ~ ^/(static|media)/ {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_redirect off;
        if (!-f $request_filename) {
            proxy_pass http://myproject.ru;
            break;
        }
     }
}
Попробуем разобраться в общих словах, что здесь к чему.
В данном примере upstream — сокет localhost:12345, в который будем пересылать все запросы, которые не удовлетворяют следующим url:
/static/* — статические файлы сайта;
/media/* — медиа-файлы, файлы, которые создаются в процессе функционирования сайта;
На этом сокете мы повесим на прослушивание наш gunicorn в следующей части статьи. Имя апстрима, теоретически, выбирается произвольно, однако я сталкивался с проблемами, в случае, если имя апстрима отличалось от доменного имени сайта (не помню текста ошибок Django, чтобы описать подробнее).

Еще интересные части конфига:
редирект с поддомена www.myproject.ru на myproject.ru, можете сделать наоборот, если вам больше нравится;
обработка ошибок 5XX орагнизована выдачей статического файла 500.html. Получить такой файл довольно просто: заставить сайт показать страницу 404, сохранить в браузере как 500.html и отредактировать текст ошибки;
в случае, если статический файл не найден, запрос направляется gunicorn’у, чтобы тот мог выдать стандартную ошибку 404.

После того, как файл конфига размещен где следует (можно проверить командой cat /etc/nginx/sites-available/myproject.conf), нужно проставить на него симлинк:
sudo ln -s /etc/nginx/sites-available/myproject.conf /etc/nginx/sites-enabled/

и перезапустить nginx:
sudo service nginx restart


Настройка gunicorn и supervisor


При развертывании проекта мы установили пакет gunicorn, теперь мы используем его для формирования динамичеких HTTP-ответов (HTTP-response) Django-приложением. Сам gunicorn умеет принимать запросы и формировать ответы на них, однако нужно поддерживать сам gunicorn запущенным, например стартовать при перезапуске системы, это мы поручим специализирующейся в этом программе supervisor.
Заметьте, что разработчики gunicorn крайне не рекомендуют ставить его на линию фронта, а поручить прием запросов от пользователей специализированным серверам, например nginx, как это делаем мы.
Итак, создаем в папке /etc/supervisor/conf.d/ файл myproject.conf следующего содержания:
[program:myproject]
command=/home/myuser/web/myproject/env/bin/python /home/myuser/web/myproject/src/myproject/manage.py run_gunicorn --bind=localhost:12345 --workers=3 --pid=/home/myuser/web/myproject/pids/gunicorn.pid --log-file /home/myuser/web/myproject/logs/gunicorn.log
directory=/home/myuser/web/myproject/src/myproject
umask=022
autostart=true
autorestart=true
startsecs=10
startretries=3
exitcodes=0,2
stopsignal=TERM
stopwaitsecs=10
user=myuser

Заметьте, что в параметрах gunicorn’у мы передаем для прослушки тот же порт (в нашем примере 12345), что указывали в конфиге nginx’а. Количество рабочих процессов установлено в значение 3, подбирал этот параметр субъективно для одного из сайтов, исходя из своих нагрузок, и пока не могу порекомендовать какое-либо значение.
После того как конфиг размещен в нужной папке (можно проверить запуском cat /etc/supervisor/conf.d/myproject.conf) нужно перечитать файл либо просто перезапустить supervisor, я использую второй вариант для надежности:
sudo supervisorctl reload

проверить успешность запуска можно командой:
sudo supervisorctl status

в случае успеха статус процесса myproject через несколько секунд станет RUNNING.

Устранение проблем


perl: warning: Setting locale failed.
С самого начала работы, при вводе команд, система начинает выдавать warning:
perl: warning: Setting locale failed. 
perl: warning: Please check that your locale settings:
решается добавлением правильной локали:
locale-gen ru_RU.UTF8

по мотивам: http://askubuntu.com/questions/76013/how-do-i-add-locale-to-ubuntu-server


sudo: must be setuid root
При первом применении sudo из-под нового пользователя возможна ошибка:
sudo: must be setuid root

решается например так:
chmod 4755 /usr/bin/sudo



E: Unable to correct problems, you have held broken packages.
при установке некоторых пакетов в Ubuntu 12.04 (например python-dev или libmysql-dev) возможна ошибка:
libc6-dev : Depends: libc6 (= 2.15-0ubuntu10.2) but 2.15-0ubuntu10+openvz0 is to be installed
E: Unable to correct problems, you have held broken packages.
Для ее решения достаточно модифицировать файл /etc/apt/preferences.d/99ovz-libc-pin:
заменив
libc-bin libc6

на
libc-bin libc6 libc6-dev libc-dev-bin



Couldn't open /dev/null: Permission denied
При создании пользователя может возникать ошибка:
Couldn't open /dev/null: Permission denied

решал так:
sudo chmod 666  /dev/null

не знаю, насколько это корректно, но я опасностей не увидел.


Заключение


Отметим положительные моменты такого развертывания:
  • Развертывание решения на VPS, виртуальная машина дает практически неограниченные возможности в администрировании.
  • Независимость nginx от gunicorn, как это часто предлагается при использовании Apache и mod_python, например. В частности довольно легко подменить, например для отладки, gunicorn разработческим сервером:
    sudo supervisorctl stop myproject
    python manage.py runserver localhost:12345 # из папки с исходниками
    

    теперь nginx будет передавать запросы на сервер разработчика, даже не узнав про изменения.
  • Nginx в качестве раздавальщика статических файлов признан одним из лучших, может даже лучшим.
  • virtualenv позволяет легко и непринужденно изолировать виртуальные окружения разных проектов друг от друга. В наше время издержки дискового пространства несравнимо дешевле возни с конфликтами версий пакетов.
  • gunicorn под контролем supervisor значительно удобнее и надежнее способов запуска gunicorn под контролем скриптов в /etc/init.d/ и некоторых других способов запуска, так как дает способ мониторинга запущенных программ, заботливо перезапускает программы в случае сбоев.

В сатье расмотрен лишь один из множества вариантов развертывания Django-приложения, я пришел к нему путем многих проб и ошибок. Мои рекомендации не претендуют на истину в последней инстанции. Более того, буду рад любой конструктивной критике.

Полезные ссылки


  1. FirstVDS — дешевый VPS-хостинг, по ссылке заказ на четверть дешевле.
  2. Putty и Kitty — SSH-клиенты под Windows.
  3. WinSCP — SCP-клиент под Windows, не нужно поднимать FTP-сервер, просто используйте имеющийся SSH-аккаунт.
  4. KeePassX — утилита хранения паролей, кроссплатформенная.
  5. Ubuntu LTS — политика выпусков релизов Ubuntu Long Term Support.
  6. Ubuntu Security — настройка для параноиков для десктопа, но что-то можно почерпнуть для сервера.
  7. Django Deployement — другие способы развертывания сайтов на Django.
  8. virtualenv — использование virtualenv.
  9. Nginx — много интересного о настройке nginx.
  10. Supervisor+gunicorn — об использовании gunicorn через nginx.
Tags:
Hubs:
+24
Comments 41
Comments Comments 41

Articles