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