Делюсь опытом в описанных технологиях. Блог в первую очередь выполняет роль памяток для меня самого.

Apache2, nginx, virtualenv и Python 2.6 в Debian

Комментариев нет

Как я уже писал, в моей компании многие продукты писаны на Python, при том некоторые на Python 2.6, что не может не удручать, т.к. в большинстве популярных дистрибутивов давно используется Python 2.7 и выше. В этой статье рассказывается, как настроить Debian 7.1 Wheezy. Как обычно, всё делается под root или через sudo. Явно я об этом больше нигде в статье писать не буду.

Собственно, почему Debian?

В отличие от той же CentOS 6.4 Debian удобнее в администрирование. Говоря это, я имею ввиду, не количество пакетов, а то, что он "из коробки" имеет кучу оптимальных настроек, в то время как в CentOS приходится всё допиливать под себя.

  1. Огромное количество пакетов. Я не знаю, есть ли где-то ещё больше. Если есть - напишите мне.
  2. Python 2.6 в официальном репозитории. Да, Debian 7.1 не требует возни с дохлыми змеями.
  3. Под него есть официальные сборки PostgreSQL и Nginx. Я раньше тоже думал, что MySQL наше всё, а сейчас вообще не понимаю, как можно пользоваться этой поделкой, не соблюдающей 90% мировых SQL-стандартов. Да, если вы пишете скрипты, руководствуясь SQL-92 или SQL-99, можете быть уверены, что в PostgreSQL они будут работать без каких-либо изменений, в то время как в MySQL их придётся переписывать, и весьма основательно (к слову, в разработке PostgreSQL принимают участие несколько профессоров математики и информатики, что как бы говорит о качестве получающегося продукта).
  4. Стабильность. Пакеты старые, но если они есть в репозитории, то можно не сомневаться, что они будут работать так, как то ожидается.

Немного про виртуальные окружения

Пакет python-virtualenv позволяет на одной системе иметь несколько версий не только интерпретатора, но и одних и тех же пакетов. Допустим, нужно, чтобы проект работал с soaplib версии 0.8.0, а при очередном плановом обновлении системы версия пакета повысится до 1.0.0, некоторые функции будут удалены как устаревшие, в итоге приложение перестанет работать вовсе. Чтобы такого не случалось, лучше всего вовремя создать виртуальное окружение. Пакеты в нём будут именно той версии, которая нужна, независимо от того, какая версия пакета установлена в системе.

Также следует пару слов сказать про пакет virtualenvwrapper. Это небольшой скрипт для ленивых питонистов, который позволяет удобно и быстро управлять созданными виртуальными окружениями.

Бывает два вида виртуальных окружений - изолированные и открытые. Отличия таковы: изолированное окружение не видит пакетов, установленных в системе. При создании открытого виртуального окружения можно быть уверенным, что приложение будет видеть те пакеты, что установлены в системе. Если, например, все приложения используют psycopg2, lxml и pytz одной и той же версии, почему бы не установить его на уровне системы, а затем не заставить окружения "видеть" его?

Nginx как фронтэнд Apache2

В этой статье не будет рассказываться, как заставить Nginx работать в связке с Gunicorn и Supervisor. Я расскажу, как научить Nginx раздавать статику, а все остальные запросы переадресовывать Apache. Сразу скажу о недостатках используемого подхода.

  • Первое - модуль mod_wsgi, необходимый для увязки Apache с Python. Ограничение тут такое, что этот модуль может работать только с одной версией Python, т.е. заставить его обрабатывать проекты, работающие на Python2.6 и также проекты на Python2.7 не получится, нужно выбрать что-то одно. При неправильном выборе версии будете получать ошибку No module named django.core.handlers.wsgi. Об этом чуть позже.
  • Второе - прожорливость Apache. Nginx частично решит проблему, тем не менее, без тонкой настройки Apache ест ОЧЕНЬ много памяти и создаёт отдельный процесс на КАЖДОЕ подключение, что не есть хорошо.

Установка пакетов

Первым делом нужно добавить репозиторий nginx и ключ для него в список пакетов apt. Многие рекомендуют отредактировать файл /etc/apt/sources.list. Мне это решение не по душе, т.к. я считаю, что там должны быть только те репозитории, которые относятся непосредственно к системе, поэтому делаем так:

nano /etc/apt/sources.list.d/nginx.list

В этот файл нужно написать:

deb http://nginx.org/packages/debian/ wheezy nginx
deb-src http://nginx.org/packages/debian/ wheezy nginx

Соответственно, если используется другая версия, нужно написать её имя. Для Ubuntu нужно также заменить /debian/ на /ubuntu/.

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

wget http://nginx.org/keys/nginx_signing.key
apt-key add nginx_signing.key

Если wget откажется скачивать ключ (во всяком случае, такое было у меня при добавлении репозитория PostgreSQL), необходимо также добавить параметр --no-check-certificate.

Делаем обновление списка доступных для установки пакетов, обновление устаревших и установку nginx

apt-get update && apt-get dist-upgrade && apt-get install nginx -y

Когда поставится, можно будет увидеть сообщение об успешной установке.

Теперь установка остальных пакетов и Apache2:

apt-get install apache2 libapache2-mod-wsgi libapache2-mod-rpaf python-dev libxml2-dev libxslt1-dev libpq-dev python-pip python-setuptools  python-virtualenv virtualenvwrapper -y

По шагам:

  • apache2 - сам сервер, обязательно
  • libapache2-mod-wsgi - обязательно, без него не получится увязать Apache с Python'ом
  • libapache2-mod-rpaf - обязательно, иначе при работе в связке с nginx Apache не будет отличать одно подключение от другого
  • python2.6-dev - обязательно, нужен, если расширения для Python будут собираться в виртуальном окружении (а они будут!)
  • libxml2-dev, libxslt1-dev - нужны для сборки и корректной работы библиотек lxml, xmlsec и других в виртуальном окружении, ставить по желанию (если не поставить, собрать, например, пакет lxml в виртуальном окружении не получится)
  • libpq-dev - опционально, если используется модуль psycopg2
  • python-pip - опционально, для установки пакетов Python, распространяемых в исходных кодах. Рекомендую в систему ставить пакет из репозиториев Debian, а в виртуальные окружения через easy_install pip
  • python-setuptools - обязательно, для установки пакетов в формате egg (вообще setuptools может ставить и другие форматы пакетов, но он всё равно нужен для последующей установки pip)
  • python-virtualenv, virtualenvwrapper - рекомендуется, первый для создания виртуальных окружений, второй для удобного управления ими

Помимо этих пакетов apt сам подтянет кучу зависимостей, нужно их все поставить.

Если Вы не стали ставить системный pip, его можно поставить следующей командой:

easy_install pip

По команде pip freeze можно увидеть список установленных пакетов и их версий, либо сообщение об ошибке. Ничего страшного, работе это не помешает. Если установка прошла успешно, можно приступать ко второму этапу.

Настройка виртуальных окружений

Перед тем, как приступать к настройке виртуальных окружений, желательно добавить в начало своего .bashrc следующее:

export WORKON_HOME=/var/www/.virtualenvs
export VIRTUALENVWRAPPER_LOG_DIR=/var/www/.virtualenvs
export VIRTUALENVWRAPPER_HOOK_DIR=/var/www/.virtualenvs
Естественно, папка /var/www/.virtualenvs должна существовать и быть доступной для записи для текущего пользователя. Если определения этих переменных не будет, virtualenv создаст папку .virtualenvs в домашнем каталоге пользователя. Итак, переопределили папку для хранения виртуальных окружений и где их должен искать workon (команда будет доступна после установки virtualenvwrapper, без параметров выдаёт список существующих окружений). Нужно перелогиниться либо перечитать файл настроек .bashrc:
source .bashrc

Примечание: не надо редактировать /etc/bash.bashrc, начнутся конфликты из-за того, что после root никто не сможет писать в указанную папку виртуальных окружений, эти настройки делаются в файле пользователя, который будет с этими окружениями работать.

Создадим виртуальное окружение командой (под обычным пользователем):

mkvirtualenv new_env --python=/usr/bin/python2.6

Этой командой мы создали в каталоге /var/www/.virtualenvs каталог new_env, куда поместили одноимённое окружение, которое будет работать с Python2.6. Если не указывать ключ --python, окружение будет работать с тем интерпретатором, на который указывает мягкая ссылка в каталоге /usr/bin (в Debian 7 по умолчанию это Python2.7). Некоторые пишут, что обязательно нужно также указать --no-system-site-packages. У меня встречный вопрос: зачем, если по умолчанию все окружения и так создаются с этим ключом? Переопределить ссылку на интерпретатор Python можно так (под root):

cd /usr/bin
rm -f python
ln -s python2.6 python

Теперь команда workon выдаст нам список наших окружений, а команда workon %имя_окружения% его активирует.

workon new_env

Слева от интерпретатора появится подсказка, например, такая:

(new_env)xphoenix@debian:/etc/apt/sources.list.d$

Тут команда pip freeze выведет сразу:

argparse==1.2.1
distribute==0.6.24
wsgiref==0.1.2

Не надо удалять эти пакеты! Без них окружение работать НЕ БУДЕТ!

Можно немного допилить окружение, для этого в каталоге bin нужно отредактировать файл activate
nano /var/www/.virtualenv/new_end/bin/activate

Добавим в самое начало строку:

export DJANGO_SETTINGS_MODULE='project.settings'

Те, кто пользуется Django, сразу поняли, что произошло. Если Вы не поняли, объясняю: эта переменная указывает, где искать файл настроек для Django-проекта. После этого нужно выйти из окружения командой

deactivate

и активировать снова.

Настройка Apache 2

Апач поставлен, но т.к. он ставился после Nginx, он не сможет запуститься, т.к. порт 80 уже занят для прослушивания. Делаем так:

nano /etc/apache2/ports

Тут должны присутствовать вот такие строки:

ServerName debian.lo
NameVirtualHost 127.0.0.1:8080
Listen 127.0.0.1:8080

Вместо debian.lo можно написать хоть ololoyakrutoj, ни на что не влияет, но избавляет от сообщения об отсутствии имени при перезапуске Apache. Следующие две строки говорят, что будут использоваться виртуальные хосты, находящиеся на локалхосте на порту 8080, и слушать Apache будет только его. Тем самым убираем конфликт с Nginx.

Включаем модуль rpaf (wsgi включает себя сам):

a2enmod rpaf

Также следует немного подредактировать его настройки:

nano /etc/apache2/mods-available/rpaf.conf
<IfModule rpaf_module>
    RPAFenable On
    RPAFsethostname Off
    RPAFproxy_ips 127.0.0.1 ::1
    RPAFheader X-Real-IP
</IfModule>
Можно включить модуль сжатия данных deflate (если выключен) и настоятельно рекомендуется включить модуль rewrite (по умолчанию выключен):
a2enmod deflate
a2enmod rewrite

Следует подкрутить также загрузчик модуля wsgi:

nano /etc/apache2/mods-avaliable/wsgi.load

Там будет написано нечто подобное:

LoadModule wsgi_module /usr/lib/apache2/modules/mod_wsgi_2.7.so

Если используется Python2.6, следует изменить 7 на 6, иначе опять будем получать ошибку no module named django.core.handlers.wsgi. Добавим виртуальный хост Apache:

nano /etc/apache2/sites-available/new_site.ru

В этот файл записываем примерно следующее:

<VirtualHost 127.0.0.1:8080>
  ServerName new_site.ru
  ServerAlias www.new_site.ru
  ServerAdmin mymail@domen.ru

  WSGIScriptAlias / /var/www/my_project/my_project.wsgi
  WSGIProcessGroup my_project
  WSGIDaemonProcess my_project user=xphoenix group=xphoenix threads=2 maximum-requests=1000

  DocumentRoot "/var/www/my_project"
  <Directory "/var/www/my_project">
    Order allow,deny
    Allow from all
  </Directory>

  FileETag none

  <Location "/">
    Order deny,allow
    Allow from all
  </Location>

  # Сжатие трафика
  <Location />
    SetOutputFilter DEFLATE
    BrowserMatch ^Mozilla/4 gzip-only-text/html
    BrowserMatch ^Mozilla/4\.0[678] no-gzip
    BrowserMatch \bMSI[E] !no-gzip !gzip-only-text/html
    SetEnvIfNoCase Request_URI \.(?:gif|jpe?g|png)$ no-gzip dont-vary
    SetEnvIfNoCase Request_URI \.(?:exe|t?gz|zip|bz2|sit|rar)$ no-gzip dont-vary
    SetEnvIfNoCase Request_URI \.pdf$ no-gzip dont-vary
  </Location>
</VirtualHost>

Этот виртуальный хост будет обрабатывать запросы к сайтам new_site.ru и www.new_site.ru (что одно и то же), для запуска проекта будет использоваться файл /var/www/my_project/my_project.wsgi

Для обработки запросов будет использоваться два потока (рекомендуется ставить по числу ядер), процесс будет работать в группе my_project, запускаться от имени пользователя xphoenix. Дальнейшие директивы, в принципе, понятны.

Включаем сайт в apache:

a2ensite new_site.ru

Создадим вышеуказанный файл запуска приложения:

nano /var/www/my_project/my_project.wsgi

со следующим содержимым

#!encoding=utf-8
import os

activate_this = '/var/www/.virtualenvs/new_env/bin/activate_this.py'
execfile(activate_this, dict(__file__=activate_this))

os.environ['DJANGO_SETTINGS_MODULE'] = 'project.settings'

import django.core.handlers.wsgi
application = django.core.handlers.wsgi.WSGIHandler()

Теперь можно перезапустить apache:

service apache2 restart

Настройка Nginx

Nginx будет проксировать запросы, приходящие на 80-й порт с любого интерфейса. Если это будет запрос статики, он сам его обработает, если нет, переадресует Apache. Создадим настройки для нашего сайта:

nano /etc/nginx/conf.d/my_site.conf

со следующим содержимым:

server {
        listen *:80; #слушать порт 80 на всех интерфейсах
        server_name new_site.ru www.new_site.lo; #этот сервер будет обрабатывать эти сайты
        access_log off; #не хочу собирать общую статистику доступа
        error_log /var/www/my_project/log/nginx_error.log; #А вот ошибки - да

        location / {
                proxy_pass http://127.0.0.1:8080/; #Переадресация запросов к Apache
                proxy_set_header Host $http_host;  #Подкорректируем заголовки, чтобы отличать одного клиента от другого
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $remote_addr;
                proxy_connect_timeout 120;
                proxy_send_timeout 120;
                proxy_read_timeout 180;
        }

        location ~* \.(jpg|jpeg|gif|png|ico|css|bmp|swf|js|html|txt|exe|pdf|djvu|doc|docx|xls|xlsx)$ {
                root /var/www/my_project;
                access_log /var/www/my_project/log/nginx_static.log;#Статистика по статике здесь
                expires max; #Срок жизни кеша - максимальный
        }
}

Вот теперь можно перезапустить и nginx

service nginx restart

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

Комментариев нет :

Отправить комментарий