Установка и настройка связки Nginx/uWSGI/Django

Есть у меня несколько своих небольших веб проектов на Django. Работают у меня они под управлением Apache/mod_wsgi. И вот, недавно я решил перевести их на связку Nginx/uWSGI/Django. Здесь вкратце описано, как я это сделал.

Цели перехода

Вообще-то для лично моих проектов меня вполне устраивал и Apache/mod_wsgi. Главной целью было освоение новых технологий. А вот зачем переходят другие, это интересный вопрос.

Если не углубляться в дебри архитектуры, то такое решение позволяет по сравнению с решением, основанным на Apache, снизить нагрузку на процессор и уменьшить потребление ОЗУ. А для нагруженных сайтов это имеет большое значение. Ну и я могу ошибаться, но такое решение легче ИМХО, устанавливается на кластер.

Да, вот еще вспомнил, в mod_wsgi мне не нравилось то, что приходится использовать ту версию Python, которая вкомпилирована в mod_wsgi. А я использую virtualenv и хочется использовать Python именно из виртуального окружения virtualenv.

Платформа, необходимые пакеты и как будем их устанавливать

В качестве платформы испольуется моя любимая, Ubuntu. В случае необходимости то же самое можно проделать и под Debian. Установку Django здесь я описывать не буду. Вкратце, устанавливать будем примерно так:

  • Nginx компилируем из исходников.
  • Nginx устанавливаем в иерархию /usr/local/ при этом для управления установкой, испольуем программу xstow.
  • Управление стартом/остановкой/рестартом Nginx производим скриптом в иерархии /etc/init.d/
  • Django устанавливаем в виртуальное окружение, созданное командой virtualenv
  • uWSGI тоже компилируем.
  • uWSGI устанавливаем в виртуальное окружение, подготовленоое для Django
  • Управление стартом/остановкой/рестартом uWSGI производим при помощи пакета supervisor дистрибутива.

Устанавливаем Nginx

Компиляция и установка

Загружаем nginx-1.2.0.tar.gz, распаковываем и компилируем:

1 ./configure --prefix=/usr/local/stow/nginx-1.2.0/nginx
2 make
3 sudo make install

Настраиваем директории под себя

1 sudo su
2 mkdir /usr/local/stow/nginx-1.2.0/etc
3 mkdir /usr/local/stow/nginx-1.2.0/etc/nginx/conf.d
4 mkdir /usr/local/stow/nginx-1.2.0/etc/nginx/sites-available
5 mkdir /usr/local/stow/nginx-1.2.0/etc/nginx/sites-enabled
6 mkdir /usr/local/stow/nginx-1.2.0/var
7 mkdir /usr/local/stow/nginx-1.2.0/var/nginx/
8 mkdir /usr/local/stow/nginx-1.2.0/var/nginx/www

Создаем для проверки HTML файл

1 echo "<html><head></head><body>This is default Nginx vhost</body></html>" > /usr/local/stow/nginx-1.2.0/var/nginx/www/index.html

Настраиваем общий конфигурационный файл под себя

Конфигурационный файл настраиваем под свои директории и в стиле Debian. Конфигурационные файлы виртуальных хостов будут у нас, как в Debian, в директории /usr/local/etc/nginx/sites-available со ссылками на них в директории /usr/local/etc/nginx/sites-enabled/

 1 user  www-data;
 2 worker_processes  1;
 3 pid /var/run/nginx.pid;
 4 
 5 events {
 6     worker_connections  1024;
 7 }
 8 
 9 http {
10      # Basic Settings
11      sendfile on;
12      tcp_nopush on;
13      tcp_nodelay on;
14      keepalive_timeout 65;
15      types_hash_max_size 2048;
16 
17      include       /usr/local/nginx/conf/mime.types;
18      default_type  application/octet-stream;
19 
20      # Logging Settings
21      access_log /var/log/nginx/nginx-1.2.0-access.log;
22      error_log /var/log/nginx/nginx-1.2.0-error.log;
23 
24      # Gzip Settings
25      gzip on;
26      gzip_disable "msie6";
27 
28      # Virtual Host Configs
29      include /usr/local/etc/nginx/conf.d/*.conf;
30      include /usr/local/etc/nginx/sites-enabled/*;
31 }

Настраиваем дефолтный конфигурационный файл виртуального хоста

1 emacs /usr/local/stow/nginx-1.2.0/etc/nginx/sites-available/default

Содержание:

 1 server {
 2 	listen   8001;
 3 	root /usr/local/var/nginx/www;
 4 	index index.html index.htm;
 5 
 6 	server_name localhost;
 7 
 8 	location / {
 9 		try_files $uri $uri/ /index.html;
10 	}
11 }

Включаем наш виртуальный хост

1 sudo su
2 cd /usr/local/stow/nginx-1.2.0/etc/nginx/sites-enabled/
3 ln -s ../sites-available/default

Создаем необходимые ссылки пакетным менеджером xstow

1 sudo su
2 cd /usr/local/stow/
3 xstow nginx-1.2.0

Тестируем работает ли Nginx

Запускаем Nginx
1 sudo su
2 cd /usr/local/ngingx/sbin
3 ./nginx

Проверям браузером

Вводим в адресную строку браузера адрес http://localhost:8001 и наслаждаемся результатом.

Подключам наш Nginx к стартовым скриптам Debian/Ubuntu

Я сделал это просто, установил Nginx из дистрибутива, взял оттуда стартовые скрипты и прикрутил к своей инсталляции.

1 sudo su
2 aptitude install nginx
3 mkdir /usr/local/stow/nginx-1.2.0/etc/init.d
4 cp /etc/init.d/nginx /usr/local/stow/nginx-1.2.0/etc/init.d/
5 aptitude purge nginx

Редактируем /usr/local/stow/nginx-1.2.0/etc/init.d/nginx

Меняем строчку:

1 DAEMON=/usr/sbin/nginx

на:

1 DAEMON=/usr/local/nginx/sbin/nginx

Подключаем наш стартовый скрипт:

1 cd /etc/init.d
2 ln -s /usr/local/etc/init.d/nginx nginx-local

Конфигурируем запуск нашего демона nginx

1 update-rc.d nginx-local defaults 23

Проверяем, стартует ли при Nginx загрузке компа и как выполняются команды:

1 /etc/init.d/nginx-local start | stop | restart

У меня все это замечательно работает.

Устанавливаем uWSGI

Я обычно устанавливаю пакеты не непосредственно из интернета, а из скачанных файлов, так легче потом повторить установку, если что.

Скачиваем uwsgi-1.1.2.tar.gz

Обычно для проектов Django я испольую virtualenv, Устанавливать будем в virtualenv. Виртуальное окружение находится в директории <корень проекта>/env

Итак, устанавливаем в uWSGI virtualenv командой:
source env/bin/activate
pip install ../packages/uwsgi-1.1.2.tar.gz

При этом, для того, чтобы uWSGI скомпилировался, в системе должны быть установлены заголовочные файлы Python.

Бинарник uwsgi установится в env/bin/

Проверка работоспособности

Создаем простейшее wsgi приложение и запускаем uwsgi:

mkdir /tmp/uwsgi

echo """
def application(env, start_response):
    start_response('200 OK', [('Content-Type','text/html')])
    return 'Hello World'
""" > /tmp/uwsgi/hello.py

./env/bin/uwsgi --http :9090 --wsgi-file /tmp/uwsgi/hello.py

После запуска проверяем есть ли что нибудь по адресу http://localhost:9090

Подключааем Django через uWSGI к Nginx

Создаем файл виртуального хоста в конфигурации Nginx

Содержание файла:

server {
       listen 8001;
       server_name project_name.localhost;
       access_log /var/log/nginx/nginx-1.2.0-project_name-access.log;
       error_log /var/log/nginx/nginx-1.2.0-047-project_name-error.log;

       root project_root/www/project_name/;

       location / {
             	try_files $uri /static/$uri
		@django_uwsgi;
       		}

       location @django_uwsgi {
	 	 uwsgi_pass 127.0.0.1:3031;
	 	 include uwsgi_params;
	 	 }

}

Проверка работоспособности

Рестартуем nginx и пробуем из браузера подключиться к URL виртуального хоста. Должны получить сообщение об ошибке: 502 Bad Gateway

Создаем файл django_wsgi.py

Создание:

cd project_root/www/project_name
mkdir nginx
cd nginx
touch  django_wsgi.py
emacs django_wsgi.py

Содержание файла:

 1 import os, sys
 2 
 3 # Python/Django location
 4 sys.path.insert(0,'project_root/www/env/')
 5 sys.path.insert(0, 'project_root/www/env/lib/python2.6/site-packages')
 6 # Project Location
 7 sys.path.insert(0,'project_root/www/project_name')
 8 sys.path.insert(0,'project_root/www/')
 9 
10 # Project configuration file
11 os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
12 import django.core.handlers.wsgi
13 application = django.core.handlers.wsgi.WSGIHandler()

Создаем файл nginx_django.xml

Это Конфигурационный файл для uWSGI)

cd project_root/www/project_name/nginx
touch nginx_django.xml
emacs nginx_django.xml

Содержание файла:

<uwsgi>
  <socket>127.0.0.1:3031</socket>
  <pythonpath>project_root/www/project_name/nginx/</pythonpath>
  <module>django_wsgi</module>
</uwsgi>

Запускаем uwsgi с нашим конфигом

source ./env/bin/uwsgi
./env/bin/uwsgi -x project_root/www/project_name/nginx/nginx_django.xml

Теперь уже можно проверить как работает наша связка и подправить если что, испольуя информацию, которую выдает на консоль uwsgi

Настройка старта/рестарта

Устанавливаем пакет supervisor

Тут была проблема, дистрибутивный пакет не устанавливался (Ubuntu 11.04). Поиск причин выяснил, что это из-за ошибки в библиотеке python-meld3. Пришлось решить проблему так:

aptitude install python-meld3
pip install meld3==0.6.7
aptitude install supervisor
Конфигурируем supervisor для старта uWSGI
создаем файл /etc/supervisor/conf.d/02_uwsgi_047-project_name.conf
[program:uwsgi_project_name]
command=project_root/www/project_name/nginx/uwsgi.sh
directory=project_root/www/
user=www-data
autostart=true
autorestart=true
redirect_stderr=true

Создаем обертку для старта uwsgi, в файле project_root/www/project_name/nginx/uwsgi.sh

#! /bin/bash
source project_root/www/env/bin/activate
exec project_root/www/env/bin/uwsgi -x project_root/www/project_name/nginx/nginx_django.xml

Запускаем supervisorctl и подаем команды:

reread
start all

Заключение

Ну вот вроде и все. Простенькие бенчмарки показали, что новая связка работает раза в два быстрее, чем Apache/mod_wsgi. Потребление памяти пока не замерял, но тоже должно уменьшится.

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

Опубликовано: April 29, 2012

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


Имя: Денис

Евгений, спасибо за статью.

А не лучше ли установить uwsgi глобально и использовать преимущества режима [Emperor](http://projects.unbit.it/uwsgi/wiki/Emperor)?



Имя: Евгений

В каких-то случаях конечно лучше. И даже наверняка, когда нужно массово обслуживать похожие сайты. У меня сделано так, что для каждый проект уникальный и чаще всего это просто для разработки, а сам сайт стоит где-то в другом месте. Поэтому для меня важно иметь ту же самую версию софта, что и на целевом сайте. Поэтому Django ставится в виртуальное окружение и поэтому интересно для uwsgi иметь свою версию для каждого проекта.
Это позволяет еще и иметь разную версию Python, что в случае, например с Apache/mod_wsgi не получается. А когда разные версии софта на рабочем месте разрабочика и на продакшене, это чревато разного рода неприятными ситуациями и потерями времени.



Имя: nnn

Do not delete this. Write under the line!
---



Имя: Евгений

Дружище, ты просто гений :)
Всё что надо и как надо собрано в одной статье, причём всё по делу - без воды и без косяков. Помогло сильно сэкономить время. Огромное спасибо.



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

Имя:

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