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

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

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

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

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