Debian, Dojo, Django, Python

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

Показаны сообщения с ярлыком nginx. Показать все сообщения
Показаны сообщения с ярлыком nginx. Показать все сообщения

Django 1.8, Python 3, WSGI и Gunicorn

Введение

В данной статье рассказывается, как можно запустить приложение на Django и Python 3 под WSGI. Способ не претендует на звание самого лучшего, замечания по настройке и прочим нюансам приветствуются.

Структура каталогов

Следует создать в каталоге /var/www подкаталог для нашего проекта. Пусть сегодня он будет называться talos. В нём нужно будет создать каталоги для размещения статичных файлов, файлов, загружаемых пользователями, и логов.

От имени www-data
cd /var/www
mkdir talos
cd talos/
mkdir static media log

Сам проект будет расположен в виртуальном окружении, которое будет создано чуть позже.

Создание файла запуска

Здесь же, в каталоге /var/www/talos, создадим файл run.bash, который будет запускать приложение, передавая ему нужные параметры.

/var/www/talos/run.bash
   #!/bin/bash

NAME="talos"                                # Название приложения
DJANGODIR=/var/www/.virtualenvs/talos/talos # Директорая проекта - путь к виртуальному окружению
                                            # плюс папка с проектом
SOCKFILE=/var/www/sockets/talos.sock        # Тут будет лежать сокет
USER=www-data                               # От чьего имени запускается
GROUP=www-data                              # Группа для запуска
NUM_WORKERS=3                               # Кол-во воркеров, обычно число ядер * 2 + 1
DJANGO_SETTINGS_MODULE=talos.settings       # Откуда брать настройки
DJANGO_WSGI_MODULE=talos.wsgi               # Имя wsgi-файла для запуска

echo "Starting $NAME as `whoami`"

# Активация окружения
cd $DJANGODIR
source ../bin/activate
export DJANGO_SETTINGS_MODULE=$DJANGO_SETTINGS_MODULE
export PYTHONPATH=$DJANGODIR:$PYTHONPATH

# Если папки для сокета нет, её надо создать
RUNDIR=$(dirname $SOCKFILE)
test -d $RUNDIR || mkdir -p $RUNDIR

# Запуск через gunicorn с передачей параметров
exec ../bin/gunicorn ${DJANGO_WSGI_MODULE}:application \
  --name $NAME \
  --workers $NUM_WORKERS \
  --user=$USER --group=$GROUP \
  --bind=unix:$SOCKFILE \
  --log-level=debug \
  --log-file=/var/www/talos/log/talos.log
  

Создание виртуального окружения

Последние версии Django написаны на Python 3, да и вообще использовать Python 2 в 2015 году - дурной тон. Ставим нужные пакеты, если их ещё нет:

Установка пакетов
apt-get install python3-dev virtualenvwrapper -y

Первый пакет нужен для сборки пакетов, поставляемых в исходных кодах (lxml, psycopg2, pillow), второй - для удобного управления виртуальными окружениями. Пакеты установлены, пользователь www-data в системе. Пришло время создать окружение и поставить нужные пакеты:

Создание окружения, установка пакетов
mkvirtualenv talos --python=/usr/bin/python3
workon talos

В результате в каталоге для виртуальных окружений (у каждого пользователя свой, по умолчанию называется .virtualenvs) будет создан подкаталог talos. В нём будут размещены необходимые для работы с окружением скрипты и несколько других каталогов. Разместим наш проект внутри каталога /var/www/.virtualenvs/talos.

Далее следует поставить в окружение все необходимые пакеты, требуемые для запуска проекта, а так же пакет gunicorn. При необходимости стоит так же обновить pip, его последние версии умеют кэшировать скачанные пакеты.

Не забудьте создать статику и при необходимости подкорректировать файл настроек приложения.

Запуск через supervisor

Если supervisor ещё не установлен, пришло время это сделать:

Установка supervisor
apt-get install supervisor -y

Настройки хранятся в каталоге /etc/supervisor. Главный файл называется supervisor.conf, файлы для запуска приложений следует расположить в каталоге /etc/supervisor/conf.d/, указав расширение .conf. В нашем случае файл будет лежать по пути /etc/supervisor/conf.d/talos.conf

/etc/supervisor/conf.d/talos.conf
[program:talos]
command=/var/www/talos/run.bash
user=www-data
group=www-data

autostart=true
autorestart=true

redirect_stderr=true
stdout_logfile=/var/www/talos/log/supervisor.log

Когда файл будет создан, следует обновить данные Supervisor'а:

supervisorctl update

При необходимости перезапустить то или иное приложение следует вызывать не перезапуск системной службы supervisor, а давать команды supervisorctl:

Управление Supervisor'ом
supervisorctl update        # Перечитать файлы конфигации приложений
supervisorctl start talos   # Запустить приложение talos
supervosorctl stop talos    # Остановить приложение talos
supervisorctl restart talos # Перезапуск приложения talos
supervisorctl status        # Посмотреть статус всех приложений
supervisorctl ДЕЙСТВИЕ all  # Выполнить ДЕЙСТВИЕ со всеми приложениями, например, перезапуск

Если всё сделано правильно, приложение будет запущено, а в каталоге /var/www/sockets/ появится файл сокета talos.sock (как было настроено в файле run.bash).

Подключение к nginx

Всё, что нужно теперь сделать - указать nginx путь к сокету и откуда брать статику для проекта. В общем-то, всё довольно просто. Nginx лучше ставить из официального репозитория проекта, а не из Debian'овского, там пакет не обновляется годами.

apt-get install nginx -y

Все настройки в каталоге /etc/nginx, главный файл - nginx.conf, настройки для сайтов в .conf-файлах, лежащих в каталоге /etc/nginx/conf.d/.

Создадим файл настроек, общих для всех сайтов: /etc/nginx/proxy_params.conf:

/etc/nginx/proxy_params.conf
proxy_redirect                          off;
proxy_set_header Host                   $http_host;
proxy_set_header X-Real-IP              $remote_addr;
proxy_set_header X-Url-Scheme           $scheme;
proxy_set_header X-Forwarded-For        $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto      $scheme;
client_max_body_size                    20m;
client_body_buffer_size                 1m;
proxy_buffering                         off;
proxy_send_timeout                      180;
proxy_read_timeout                      180;
proxy_connect_timeout                   180;
proxy_buffer_size                       4k;
proxy_buffers                           32 32k;
proxy_busy_buffers_size                 64k;
proxy_temp_file_write_size              1m;
add_header X-Frame-Options "SAMEORIGIN";

Возможно, данные настройки придётся доработать исходя из реальной конфигурации сервера. Здесь я на этом останавливаться не буду. Переходим к созданию файла конфигурации для нашего проекта:

/etc/nginx/conf.d/talos.conf
upstream talos {
    server unix:/var/www/sockets/talos.sock fail_timeout=0;
    keepalive 30;
}

server {
    listen 80;
    server_name talos.lo;
    keepalive_timeout 3;
    access_log off;
    error_log /var/www/talos/log/nginx_error.log crit;

    location / {
        proxy_pass http://talos/;
        include /etc/nginx/proxy_params.conf;
    }

    location /static/ {
        alias /var/www/talos/static/;
        expires 3d;
    }

    location /media/ {
        alias /var/www/talos/media/;
           expires 3d;
    }

    location ~* \.(7z|jpg|jpeg|gif|png|ico|css|bmp|swf|js|html|txt|doc|docx|pdf|rar|xls|xlsx|zip)$ {
        root /var/www/talos/;
        expires 3d;
        add_header Cache-Control: public;
        access_log off;
        error_log /var/www/talos/log/nginx_static_error.log;
    }
}

Перезапустите nginx, тобы он подхватил новый конфигурационный файл. На этом всё.

Обо всех ошибках или дополнения прошу писать мне на почту dunmaksim@yandex.ru

Nginx + PHP-FPM в Debian 7

Введение

В статье рассматривается настройка сервера Debian 7 для размещения проектов PHP в связке с Nginx и PHP-FPM.

Долгое время мне вполне хватало возможностей Apache для разработки моих проектов на PHP. Были некоторые трудности с .htaccess и перенаправлением всех запросов на Front Controller, однако, чуть позже я понял, что Apache уже не может полностью удовлетворить моей тяги к максимальной производительности и начал искать другие пути. Почитав, я выяснил, что доля интернет-серверов под управлением Apache неуклонно снижается на протяжении уже нескольких лет, на замену ему приходят другие, которые дают бОльшую произодительность с меньшей затратой машинных ресурсов. Среди множества решений я выбрал PHP-FPM. Судя по тестам производительности, произведённым многими энтузиастами, именно он в связке с Nginx является наиболее перспективным способом размещения сайтов на PHP.

Цели

  • Увязка PHP-FPM с nginx
  • Конфигурирование параметров сайта для его корректной работы через Front Controller

Установка необходимого ПО

Добавим в список репозитории nginx, предварительно создав файл nginx.list в каталоге /etc/apt/sources.list.d/:

cd /etc/apt/sources.list.d/
touch nginx.list
/etc/apt/sources.list.d/nginx.list
deb http://nginx.org/packages/debian/ wheezy nginx
deb-src http://nginx.org/packages/debian/ wheezy nginx

Скачиваем ключ репозитория:

wget http://nginx.org/keys/nginx_signing.key

Установка ключей в deb-системах делается так:

apt-key add nginx_signing.key

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

apt-get update && apt-get install php5 php5-fpm nginx -y
Кратко по списку:
  • php5 и php5-fpm - интерпретатор и FastCGI-сервер для него
  • nginx - проксирующий сервер, будет нашим Front-End, т.е. сервером, принимающим запросы пользователя. Не путать с Front Controller, это совершенно разные вещи!

Настройка PHP и PHP-FPM

При установке PHP-FPM создаёт внутри каталога /etc/php5 подкаталог fpm. Внутри этого каталога находится несколько нужных нам файлов и подкаталогов. Первым делом вносим изменения в файл /etc/php5/fpm/php.ini

cd /etc/php5/fpm
nano php.ini

Здесь множество настроек, которые влияют на поведение PHP. В качестве символа комментария следует использовать точку с запятой. Самыми полезными я считаю следующие настройки:

  • short_open_tag = Off - использование коротких тегов считать ошибкой. Официальная документация по PHP рекомендует не использовать короткие теги.
  • asp_tags = Off - мы тут всё-таки на PHP пишем, а не на ASP.NET
  • max_execution_time = 60 - для некоторых CMS, например, Drupal, стандартных 30 секунд часто не хватает для выполнения некоторых скриптов, например, обновления
  • error_reporting = E_ALL - разработчику нужно видеть все ошибки. Если сервер боевой, нужно выставить в значение E_ALL & ~E_DEPRECATED & ~E_STRICT
  • display_errors = On - для разработки, для боевых условий - Off
  • display_startup_errors = On - аналогично
  • post_max_size = 16M - можно и больше, если через POST будут передаваться достаточно большие объёмы данных
  • default_charset = "UTF-8" - обязательно, если хочется избавиться от большинства проблем с кодировкой. Осторожно! Настройка влияет на весь сервер. Также кодировку можно задать в самом скрипте!
  • cgi.fix_pathinfo=1 - пригодится, если приложение работает через Front Controller
  • upload_max_filesize = 32M - можно и больше, если на сервер будут загружаться большие файлы
  • default_socket_timeout = 60 - в нашем случае связь с Nginx будет именно через сокет. Не стоит выставлять слишком большие значения, это приведёт к падению производительности.

Остальные настройки в этом файле можете делать по своему усмотрению.

Теперь нужно подредактировать файл www.conf в каталоге pool.d/www.conf

nano pool.d/www.conf

Следует изменить user и group на те, от имени которых будут выполняться скрипты. Не следует выполнять их от имени root, это дыра в безопасности. Я рекомендую указывать здесь владельца каталога /var/www

user = www-data
group = www-data

Не менее важная настройка:

listen = /var/run/php5-fpm.sock

Именно через этот сокет будет производиться обмен данными с Nginx. Остальные настройки я рекомендую оставить по умолчанию. Их тонкая настройка заслуживает отдельной подробной статьи.

После сохранения изменений следует перезапустить php-fpm:

service php5-fpm restart

Настройка Nginx

Перейдём в каталог настроек nginx:

cd /etc/nginx/

Здесь нам нужны 2 файла:

  • nginx.conf - основной файл конфигурации сервера
  • fastcgi_params - настройки Nginx для работы с FastCGI-серверами, например, PHP-FPM

Я привёл содержимое этих файлов к следующему виду:

nginx.conf
user nginx;
worker_processes 2;         # По числу ядер/процессоров, либо эмпирическим путём
worker_priority  -5;        # Отрицательные числа задают более высокий приоритет
worker_rlimit_nofile 2048;  # Можно открыть сразу 2048 подключений без изменения параметров ядра

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;

events {
    worker_connections  1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    tcp_nopush      on;
    tcp_nodelay     on;

    keepalive_timeout  60; #Поддерживать подключение 1 минуту

    client_max_body_size 20m; #Разрешить загрузку файлов до 20 МБ
    server_tokens off;

    #Сжатие передаваемых данных
    gzip  on;
    gzip_disable "msie6";
    gzip_comp_level 2;
    gzip_proxied any;
    gzip_types text/plain text/css application/x-javascript text/xml application/xml+rss text/javascript;

    include /etc/nginx/conf.d/*.conf;
}
В конец файла fastcgi_params нужно добавить всего одну строчку, вот эту:
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

Теперь в каталоге /etc/nginx/conf.d/ создадим файл site.conf

nano /etc/nginx/conf.d/site.conf
index index.php;
server {
    server_name www.site.ru;
    rewrite ^ $scheme://site.ru$request_uri? permanent;
}
server {
    server_name site.ru;
    access_log /var/www/site.ru/log/access.log;
    error_log /var/www/site.ru/log/error.log;
    root /var/www/site.ru;

    location / {
        try_files $uri $uri/ /index.php?$request_uri;
        index index.php;
    }

    location ^~ /protected/ {
        deny all;
    }

    location ~ \.php$ {
        try_files $uri = 404;
        include fastcgi_params;
        fastcgi_pass unix:/var/run/php5-fpm.sock;
    }
}

Разберём по частям этот файл:

index index.php;

Считать страницей по-умолчанию index.php

server {
    server_name www.site.ru;
    rewrite ^ $scheme://site.ru$request_uri? permanent;
}

Все запросы на http://www.site.ru автоматически переадресовывать к http://site.ru/

server_name site.ru;
access_log /var/www/site.ru/log/access.log;
error_log /var/www/site.ru/log/error.log;
root /var/www/site.ru;

Собственно наш сервер, с указанием его имени, логов и указанием корневого каталога

location / {
    try_files $uri $uri/ /index.php?$request_uri;
    index index.php;
}

Тут указано, что все запросы вида http://site.ru/login/auth следует переадресовывать к http://site.ru/index.php - как раз то, что нужно для Front Controller'а

location ^~ /protected/ {
    deny all;
}

Запрещаем любые обращения к папке protected нашего сайта. Там лежат исходные коды, к которым будет обращаться только index.php, веб-сервер их отдавать не должен.

location ~ \.php$ {
    try_files $uri = 404;
    include fastcgi_params;
    fastcgi_pass unix:/var/run/php5-fpm.sock;
}

Выдавать ошибку 404, если запрошенный php-файл не найден. В остальном - просто переадресовать запрос на наш PHP-FPM-сервер. Также в этих строках выполняется подключение файла настроек /etc/nginx/fastcgi_params.

После выполнения всех операций следует перезапустить сервис Nginx

service nginx restart

Если исходные файлы сайта размещены в каталоге /var/www/site.ru/, а также этот каталог содержит подкаталог log, владельцем которого является www-data, то при обращении к сайту мы увидим главную страницу.

Gunicorn + NGinx + Virtualenv на Debian 7

В этой заметке я хочу рассмотреть наиболее перспективный в настоящее время способ размещения проектов на базе Django на боевых серверах. Будет использовано следующее ПО:

  1. Python 2.6. Вы можете использовать вообще любой, какой Вам нравится, просто проекты, которыми пользуется моя организация, используют именно его. По умолчанию в Debian 7 установлен Python 2.7, но без проблем можно установить и более старую версию. Проблем с 3-ей версией также не возникнет, он есть в стандартных репозиториях.
  2. Virtualenv и virtualenvwrapper. Благодаря этим пакетам можно создать свою песочницу для каждого проекта Django, указав свой собственный интерпретатор Python (2.6, 2.7, 3, 3.1 - и всё на одной машине) и набор пакетов (а также версий пакетов). Данное ПО позволяет изолировать проекты друг от друга, при необходимости используя разные версии библиотек. Допустим, в одном проекте используется lxml версии 2.3.1, а в другом 3.2.1. Как быть? Просто добавляем в одно окружение 2.3.1, а в другое - 3.2.1, и никаких конфликтов библиотек не будет.
  3. Gunicorn. Этот легковесный сервер позволяет размещать на одном сервере проекты, использующие разные версии Python. В отличие от apache2-mod-wsgi, он позволяет работать одновременно и с Python2.6, и с Python2.7 (имеется ввиду разделение по проектам). Работа же mod-wsgi глобальна, т.е. весь сервер сразу поддерживает либо одну версию Python, либо другую.
  4. Supervisor. Эта утилита позволяет демонизировать процессы. В рассматриваемом случае она будет запускать gunicorn-серверы проектов. Некоторые скажут, что можно использовать init. Пожалуйста, я никого не заставляю, но настроек для init'а у меня нет.
  5. PostgreSQL. Наиболее продвинутая из бесплатных и максимально соблюдающая SQL-стандарты из известных мне СУБД. С помощью утилиты pgtune можно в автоматическом режиме оптимизировать настройки (содержимое файла postgresql.conf) под конкретную машину с учётом размера оперативной памяти, дискового кэша и т.д.
  6. Nginx. В представлении не нуждается. Самый производительный web-сервер в мире на сегодня.
  7. Пакеты для работы с графикой, XML и сборки драйвера psycopg2 PostgreSQL для Python (ставить желательно, но не обязательно):
    • libfreetype6-dev - позволяет Pillow выводить надписи на изображения
    • zlib1g-dev - включает поддержку PNG-формата для Pillow
    • libjpeg8-dev - позволяет Pillow обрабатывать JPEG-изображения
    • libwebp-dev - позволяет Pillow работать с форматом webp
    • libtiff-dev - комментарии излишни
    • liblcms2-dev - для поддержки библиотеки управления цветовыми профилями Little Color Management System версии 2.
    • libpq-dev - без этого пакета в виртуальном окружении не соберётся пакет psycopg2, необходимый для использования PostgreSQL
    • libxml2-dev, libxslt1-dev - без этих двух пакетов в виртуальном окружении не соберётся пакет lxml

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

Для работы нам понадобится установить некоторые пакеты в систему, но прежде чем приступить к установке, нужно будет добавить репозитории PostgreSQL и Nginx в систему, чтобы всегда использовать последние стабильные версии ПО. Переходим в каталог, содержащий файлы с указанием дополнительных репозиториев apt:

cd /etc/apt/sources.list.d/

Создадим здесь два файла со следующим содержимым:

Первый - postgresql.list

deb http://apt.postgresql.org/pub/repos/apt/ wheezy-pgdg main

Второй - nginx.list

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

Конечно, можно добавить эти строки в файл /etc/apt/sources.list, но я предпочитаю не трогать его, а добавлять сторонние репозитории через создание отдельных .list-файлов в каталоге /etc/apt/sources.list.d/.

На следующем этапе следует добавить ключи для проверки пакетов. Здесь для nginx, здесь для PostgeSQL.

Собственно команды:

wget http://nginx.org/keys/nginx_signing.key
wget https://www.postgresql.org/media/keys/ACCC4CF8.asc --no-check-certificate
apt-key add nginx_signing.key
apt-key add ACCC4CF8.asc

Кратко опишу смысл указанных выше команд.

С помощью утилиты wget были скачаны 2 файла. При этом во втором случае была отключена проверка сертификата для шифрованного соединения по протоколу SSL. Сделано это потому, что у сайта PostgreSQL на момент написания статьи были какие-то проблемы с сертификатом и wget не начинал загрузку, выдавая вместо этого сообщение об ошибке.

Далее с помощью команды apt-key add идёт импорт скачанных ключей в системное хранилище. Debian не работает с репозиториями, для которых у него нет ключей проверки цифровой подписи.

Теперь нужно обновить список доступных пакетов с помощью команды apt-get update и установить необходимое ПО с помощью apt-get install:

apt-get update
apt-get install libfreetype6-dev zlib1g-dev libjpeg8-dev libwebp-dev postgresql-9.3 postgresql-server-dev-9.3 postgresql-contrib-9.3 pgtune libpq-dev libxml2-dev nginx python-pip python-setuptools python-virtualenv virtualenvwrapper python-elementtidy supervisor python2.6-dev

Пакет gunicorn следует ставить отдельно, прямо в виртуальное окружение, поэтому здесь его указывать не надо.

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

Запуск любых серверных скриптов должен происходить под ограниченной учётной записью. В Debian по умолчанию для этого используется учётная запись www-data. Откройте файл /etc/passwd и убедитесь также, что данному пользователю запрещён вход в систему. При необходимости приведите вот эту строку

www-data:x:33:33:www-data:/var/www:/bin/bash

к вот такому виду:

www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin

Перейдите в каталог /var и посмотрите, имеется ли там папка www. Если её нет, надо её создать и сделать владельцем пользователя www-data:

cd /var
mkdir www
chown www-data:www-data www/

Теперь нужно будет войти в систему от имени этого пользователя. Так как мы запретили ему вход с использованием шелла, придётся использовать sudo с параметром -s, задающим командную оболочку:

sudo su www-data -s /bin/bash

Без параметра -s система выдаст сообщение, что указанная учётная запись недоступна.

При первом входе в систему в домашнем каталоге пользователя будет создана папка .virtualenvs, о чём будут выведены сообщения.

Создадим виртуальное окружение:

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

Созданное окружение project будет содержать в себе Python 2.6, не будет видеть пакеты, установленные на уровне всей системы, и может быть активировано командой workon, а деактивировано командой deactivate:

workon project
deactivate

Если вызвать команду workon без параметров, она вернёт список имеющихся виртуальных окружений. Помните, что у каждого пользователя свой набор этих окружений, поэтому результат работы этой команды у каждого пользователя будет свой.

Настройка PostgreSQL

Перейдём в каталог с настройками PostgreSQL:

cd /etc/postgresql/установленная_версия/main/

Настроим доступ к базам по паролю, без наличия учетной записи в системе, для этого изменим в файле pg_hba.conf строку

local   all    all   ident

на

local   all    all   md5

Создадим резервную копию файла настроек, оптимизируем настройки с помощью pgtune и перезапустим службу:

cp postgresql.conf postgresql.conf.bak
pgtune -i postgresql.conf.bak -o postgresql.conf --type=web
service postgresql restart

В версиях PostgreSQL до 9.2 включительно оптимизация настроек с помощью pgtune требует также изменения параметров ядра. Прежде всего надо будет выполнить вот этот скрипт:

#!/bin/bash
# simple shmsetup script
page_size=`getconf PAGE_SIZE`
phys_pages=`getconf _PHYS_PAGES`
shmall=`expr $phys_pages / 2`
shmmax=`expr $shmall \* $page_size`
echo kernel.shmmax = $shmmax
echo kernel.shmall = $shmall

Строки, которые выведет этот скрипт, следует добавить в начало файла /etc/sysctl.conf и перезагрузить систему.

Для создания БД проекта следует запустить интерпретатор PostgreSQL от имени пользователя postgres:

su -c psql postgres

При выполнении SQL-запросов точка с запятой в конце обязательна. Команды, начинающиеся со знака \, вводятся как есть.

Создадим роль project с паролем project и возможностью подключаться к СУБД:

CREATE ROLE project WITH PASSWORD 'project' LOGIN;

Добавим БД project_db, с которой будет работать эта роль:

CREATE DATABASE project_db WITH OWNER project;

Проверим список имеющихся баз данных с помощью команды \l и завершим работу с интерпретатором \q.

Установка пакетов виртуального окружения

Снова залогинимся от имени пользователя www-data. Активируем виртуальное окружение и запустим в нем команду на установку пакетов:

sudo su www-data -s /bin/bash
workon prject
pip install lxml psycopg2 gunicorn Pillow

Если некоторые пакеты должны ставиться из каких-либо внешних репозиториев, следует передать дополнительные параметры:

pip install myPackage -i http://mysite.ru/simple/

Здесь после ключа i идёт указание стороннего репозитория.

Настройка Gunicorn

Для соединения Gunicorn'а с Nginx'ом будем использовать сокеты. В каталоге /var/www/ создадим каталог sockets

cd /var/www
mkdir sockets
chown www-data:www-data sockets/

Теперь в каталоге с настройками проекта /var/www/project/conf создадим файл gunicorn.conf.py со следующим содержимым:

#! coding: utf-8
import os
import multiprocessing

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

short_name  = 'project'                               # Это имя будет далее использоваться для создания сокета

bind      = "unix:/var/www/sockets/%s.sock" % (short_name)#Создаём сокет
proc_name = short_name
workers = multiprocessing.cpu_count() * 2 + 1         #Кол-во воркеров вычисляется по рекомендуемой формуле
print "Socket started"
user      = "www-data"
group     = "www-data"
errorlog   = "/var/www/project/log/gunicorn.log"
loglevel  = "warn"
timeout   = 30 # 30 секунд на завершение воркера при получении сигнала к завершению
keepalive = 5  #Поддерживать каждое подключение 5 секунд

Указанное содержимое затем будете передано gunicorn'у в качестве параметров. Подробно про переменные, использованные в этом файле, можно почитать здесь. Следует сделать этот файл исполнимым:

chmod u+x gunicorn.conf.py

Настройка supervisor

Установим:

apt-get install supervisor -y

Перейдём в каталог /etc/supervisor/conf.d и создадим файл настроек для нашего проекта (вместо vim новичкам советую использовать nano):

cd /etc/supervisor/conf.d
touch project.conf
vim project.conf

Содержимое файла project.conf

[program:project]
environment=DJANGO_SETTINGS_MODULE="project.settings",PYTHONIOENCODING="UTF-8",LANG="ru_RU.UTF-8",LC_ALL="ru_RU.UTF-8",LC_LANG="ru_RU.UTF-8"
command=/var/www/.virtualenvs/project_env/bin/gunicorn_django -c /var/www/project/conf/gunicorn.conf.py
directory = /var/www/project
user="www-data"   #По аналогии с gunciron.conf.py
group="www-data"

stderr_logfile = /var/www/project/log/supervisor_error.log

Пожалуй, самой интересной является здесь строка, описывающая переменные окружения. В частности, она указывает, какие локали использовать. Без указания правильных локалей при загрузке или обработке приложением файлов, содержащих в имени символы, коды которых выходят за границы основной таблицы ASCII, можно будет полюбоваться ошибкой Non-Unicode character at position ...

В принципе, последняя строка необязательна, но лог ошибок позволит быстро разобраться в возникающих проблемах приложения. После сохранения файла следует выполнить обновление данных supervisor'а и посмотрим статус нашего проекта:

supervisorctl update
supervisorctl status project

Если всё хорошо, на экран будет выведено примерно следующее:

project                        RUNNING    pid 2801, uptime 0:02:05

Больше делать ничего не надо, supervisor будет сам стартовать после перезагрузки системы и автоматически запускать все демоны. Для сервисного обслуживания БД или обновления сайта следует остановить демон командой stop, запустить заново командой start:

supervisorctl stop project
...
Какие-то сервисные работы, например, обновление Django, проведение миграций и т.д.
...
supervisorctl start project

Настройка nginx

Возможно, в Вашем случае на одной машине будет несколько сайтов, поэтому дабы не повторяться, некоторые настройки следует вынести в отдельный файл. В каталоге /etc/nginx создадим файл proxy_params.conf

Содержимое файла /etc/nginx/proxy_params.conf
proxy_redirect                          off;
proxy_set_header Host                   $http_host;
proxy_set_header X-Real-IP              $remote_addr;
proxy_set_header X-Forwarded-For        $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto      $scheme;
client_max_body_size                    20m;
client_body_buffer_size                 1m;
proxy_buffering                         off;
proxy_connect_timeout                   360;
proxy_send_timeout                      260;
proxy_buffer_size                       4k;
proxy_buffers                           4 32k;
proxy_busy_buffers_size                 64k;
proxy_temp_file_write_size              1m;

Данная конфигурация успешно работает на машине с 4 Гб RAM и далеко не топовой конфигурацией. Также следует немного модифицировать /etc/nginx/nginx.conf (Вы ведь не забыли сначала сделать его резервную копию, правда?).

user  nginx;
worker_processes  2;
worker_priority -5;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;

timer_resolution 100ms;

events {
    worker_connections  1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    tcp_nopush      on;
    tcp_nodelay     on;

    keepalive_timeout  2;

    client_max_body_size 10m;

    gzip  on;
    gzip_comp_level 2;
    gzip_proxied any;
    gzip_types text/plain text/css application/x-javascript text/xml application/xml+rss text/javascript;

    include /etc/nginx/conf.d/*.conf;
}

Единственное, на что здесь следует обратить внимание - это параметры client_max_body_size и gzip. По умолчанию максимальный размер одного пакета, который пропускает nginx, равен 24 КБ. В случае, если пользователь захочет загрузить на сайт фотографию размером 30 КБ (смешно, правда, особенно после 41 МП на некоторых смартфонах?), он получит ошибку 304, поэтому данное значение следует в разумных пределах увеличить. gzip позволяет снизить расход трафика, используя сжатие для указанных типов содержимого (список gzip_types). В остальном данный файл практически не отличается от оригинального.

В каталоге /etc/nginx/conf.d/ создадим файл project.conf

Содержимое файла /etc/nginx/conf.d/project.conf
upstream project {
    server unix:/var/www/sockets/project.sock fail_timeout=0;
}

server {
    listen 80;
    server_name project.ru www.project.ru;
    
    access_log off;
    
    error_log /var/www/project/log/nginx_error.log crit;

    location / {
        proxy_pass http://project;
        include /etc/nginx/proxy_params.conf;
    }
    
    location /static/ {
        root /var/www/project/static;
        expires 3d;
    }

    location ~* \.(jpg|jpeg|gif|png|ico|css|bmp|swf|js|html|txt|xlsx|doc|docx|pdf|rar|zip|7z|exe)$ {
        root /var/www/project/;
        expires 3d;
        add_header Cache-Control: public;
        access_log off;
        error_log /var/www/project/log/nginx_static_error.log;
    }    
}

Данный конфиг указывает, что динамические запросы следует передавать на сервер, подключенный к сокету /var/www/sockets/project.sock. Для раздачи статики, т.е. запросов, в которых встречается строка /static/, следует использовать содержимое каталога /var/www/project/static/. После сохранения настроек следует перезапустить nginx:

service nginx restart

Если всё сделано правильно, после ввода адреса в браузере мы попадём на сайт.

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, значительно разгружая нагрузку на него. Буду рад любой критике в комментариях.