Настройка Gunicorn и uWSGI, сравнение производительности

Решил сменить связку Apache (mod_wsgi) + nginx на что-то более легковесное и проворное. Хоть выбор и велик решил пока ограничится двумя кандидатами - Gunicorn и uWSGI.

nginx

Начнем с установки nginx он понадобиться как буфер перед gunicorn и для отдачи статики. До версии 0.8.40 нужно было собирать nginx с поддержкой модуля uwsgi (про uwsgi ниже) в последующих версиях модуль уже добавлен.

Под Ubuntu последнюю версию nginx можно установить из любого из двух PPA:

Stable version (на данный момент 1.0.5)

sudo apt-add-repository ppa:nginx/stablesudo apt-get updatesudo apt-get install nginx

Development version (на данный момент 1.1.0)

sudo apt-add-repository ppa:nginx/developmentsudo apt-get updatesudo apt-get install nginx

Под CentOS последнюю версию nginx можно установить из CentALT репозитария

rpm -ihv http://centos.alt.ru/repository/centos/5/i386/centalt-release-5-3.noarch.rpmyum install nginx 

Проверим версию nginx и поддержку uwsgi

nginx -V

Gunicorn

Со слов разработчиков gunicorn'a это WSGI HTTP сервер написанный на питоне и легкий в настройке. Среди заявленных возможностей:

  • поддержка WSGI Django и Paster
  • async sync worker
  • балансировка нагрузки через pre-fork и shared socket
  • разные хуки для расширение возможностей

Установим gunicorn через pip (если pip не работает то вам сюда). Я устанавливаю gunicorn сразу в виртуальное окружение (virtualenv) проекта.

pip install gunicorn setproctitle

В директории с проектом разместим файл с настройками - gunicorn.conf.py.

bind = "127.0.0.1:8888"# или через сокет# bind = "unix:/home/proft/projects/blog/run/blog.socket"workers = 5user = "www"group = "www"logfile = "/home/proft/projects/blog/log/gunicorn.log"loglevel = "info"proc_name = "blog"#pidfile = "/home/proft/projects/blog/gunicorn.pid"

Число worker'ов расчитываеться по такой формуле 2xCPUs + 1.

Тестовый запуск для django проекта

gunicorn_django -c gunicorn.conf.py

Перезагрузка gunicorn (этот способ использовать в случаи если не настроен supervisor и есть параметр pidfile в конфигурационном файле).

kill -HUP /home/proft/projects/blog/gunicorn.pid

Рецепт перезапуска gunicorn при работе из vim.

Для управления gunicorn'ом с консоли с curses-интерфейсом есть пакет gunicorn-console.

Настройки proxy_params для nginx остался таким же.

server {    listen 127.0.0.1:80;    server_name blog.local;    server_tokens off;    access_log /home/proft/projects/blog/log/nginx_access.log;    error_log /home/proft/projects/blog/log/nginx_error.log;    location / {        proxy_pass http://127.0.0.1:8888;        # или через сокет http://unix:/home/proft/projects/blog/run/blog.socket        # proxy_pass         include proxy_params;    }    location /static {        root /home/proft/projects/blog/apps;        expires 24h;    }} 

Для запуска нескольких проектов на том же сервере - просто меняем порт в настройках gunicorn и nginx.

Осталось настроить supervisor для удобного управления gunicorn-сервером с консоли и автоматического перезапуска в случаи аварии.

Установим supervisor и зависимый модуль elementtree под Ubuntu

pip install elementtreesudo apt-get install supervisor

Установим supervisor и зависимый модуль elementtree под CentOS

pip install elementtree supervisor

Все ниже описанные действия касательно supervisor относятся только к CentOS. Добавим скрипт для управления supervisord

wget http://proft.me/static/linux/supervisord.txtmv supervisord.txt /etc/init.d/supervisordchmod +x /etc/init.d/supervisord

В /etc/init.d/supervisord нужно подправить путь к python.

Добавим supervisord в автозагрузку

chkconfig supervisord on

Создадим директорию для файлов с настройками для проектов

mkdir -p /etc/supervisor/conf.d/

Добавим в /etc/supervisord.conf

[include]files = /etc/supervisor/conf.d/*.conf

Настроем supervisor для тестового проекта на django для этого сохраним в файл /etc/supervisor/conf.d/blog.conf следующие строки (для Ubuntu и CentOS)

[program:blog]command=/home/proft/.virtualenvs/blog/bin/gunicorn_django -c /home/proft/projects/blog/gunicorn.conf.pydirectory=/home/proft/projects/blog/appsuser=wwwgroup=wwwautostart=trueautorestart=trueredirect_stderr=Truedaemon = Falsedebug = Falselogfile = /home/proft/projects/blog/log/supervisor.logloglevel = "info"

Если gunicorn.conf.py не лежит рядом с settings.py то в command через пробел нужно дописать полный путь к settings.py.

Настроем supervisor для тестового проекта на flask для этого сохраним в файл /etc/supervisor/conf.d/blog.conf следующие строки (для Ubuntu и CentOS)

[program:blog]command=/home/proft/.virtualenvs/blog/bin/gunicorn -c /home/proft/projects/blog/gunicorn.conf.py main_pyfile:appdirectory=/home/proft/projects/blog/appsuser=wwwgroup=wwwautostart=trueautorestart=trueredirect_stderr=Truedaemon = Falsedebug = Falselogfile = /home/proft/projects/blog/log/supervisor.logloglevel = "info"environment = PYTHON_EGG_CACHE=/home/proft/projects/blog/.python-eggsPYTHONPATH=/home/proft/projects/blog:$PYTHONPATH

Изменилась команда вызова gunicorn в параметре command и добавился параметр environment.

Запустим supervisord

service supervisord start

Для управления созданными настройками проектов для supervisor'а можно воспользоваться supervisorctl

sudo supervisorctl {startstatusstop} blog

uWSGI

uWSGI — быстрый легко конфигурируемый и самовосстановливаемый сервер написанный на C. Про скорость еще ниже поговрим а самовосстановливаемый означает что если запрос длится больше заданого времени то главный процесс прибьет текущего worker'a обрабатующего текущий запрос и запустит нового. Разработчики называют это - Harakiri mode.

Еще из приятных моментов

  • опиця idle - убить worker'a если он не используеться n секунд - удобно для экономии памяти
  • возможность перезапуска сервера при измении кода (как runserver в django) или по touch как было для mod_wsgi
  • при большом количестве python-проектов можно создать общию директории с настройками и перезапуск по touch или редактированию конфигурационного файла - режим Emperor

Установим последнюю версию uWSGI

pip install http://projects.unbit.it/downloads/uwsgi-latest.tar.gz

Конфигурационный файл с настройками для django-проекта

[uwsgi]djangoproject = /home/proft/projects/blog/apps/env = DJANGO_SETTINGS_MODULE=settingsvirtualenv = /home/proft/.virtualenvs/blog/chdir = %(djangoproject)module = django.core.handlers.wsgi:WSGIHandler()socket = /home/proft/projects/blog/blog.sockmaster = trueprocesses = 5idle = 3600uid = wwwgid = wwwharakiri = 30max-requests = 3000logto = /home/proft/projects/blog/log/uwsgi.logbuffer-size = 32768post-buffering = 8192

Настройки supervisor для проекта сохраним в файл /etc/supervisor/conf.d/blog_uwsgi.conf

[program:blog_uwsgi]command=/usr/local/bin/uwsgi -ini /home/proft/projects/blog/uwsgi.inidirectory=/home/proft/projects/blog/appsuser=wwwgroup=wwwautostart=trueautorestart=trueredirect_stderr=Truedaemon = Falsedebug = Falselogfile = "/home/proft/projects/blog/log/supervisor_uwsgi.log"loglevel = "info"

Настройки для nginx

server {    listen 127.0.0.1:80;    server_name blog.local;    server_tokens off;    access_log /home/proft/projects/blog/log/nginx_access.log;    error_log /home/proft/projects/blog/log/nginx_error.log;    location / {        uwsgi_pass unix:///home/proft/projects/blog/blog.sock;        include uwsgi_params;    }    location /static {        root /home/proft/projects/blog/apps;        expires 24h;    }} 

Сравнение производительности Gunicorn vs uWSGI vs mod_wsgi

А теперь самое интресное - кто быстрее всех из трех заявленных конкурсантов :). Тесты проводились на двух ядерном ноуте каждое ядро по 1.66Hz 2 GB RAM Ubuntu 11.04.

Замеры оценок проводились так:

  • для каждого типа сервера производился холодный старт последовательность старта: nginx процес supervisor(отвечающий за проект) ab. При повторном запуске ab (для того же типа сервера) показатили Requests per second возрастали примерно на 1 - 1.5 позиции.

Команда для тестирования

ab -c10 -n100 http://blog.local/

Замер используемой памяти (RSS (resident set size)) (в МБайт) проводилась с помощью команды

# для gunicornps -eo fnamerss | grep gunicorn | awk '{sum +=$2} END {print sum/1024}'# для uwsgips -eo fnamerss | grep uwsgi | awk '{sum +=$2} END {print sum/1024}'# для apache2 + mod_wsgips -eo fnamerss | grep apache2 | awk '{sum +=$2} END {print sum/1024}'

после запуска ab.

Gunicorn + nginxuWSGI + nginxApache2 + mod_wsgi + nginx
Сравнение производительности Gunicorn vs uWSGI vs mod_wsgi Сравнение производительности Gunicorn vs uWSGI vs mod_wsgi Сравнение производительности Gunicorn vs uWSGI vs mod_wsgi
Concurrency Level:      10Time taken for tests:   8.906 secondsComplete requests:      100Failed requests:        0Write errors:           0Total transferred:      1733400 bytesHTML transferred:       1721900 bytesRequests per second:    11.23 [#/sec]Time per request:       890.607 [ms]Time per request:       89.061 [ms]Transfer rate:          190.07 [Kbytes/sec]
Concurrency Level:      10Time taken for tests:   11.738 secondsComplete requests:      100Failed requests:        0Write errors:           0Total transferred:      1733400 bytesHTML transferred:       1721900 bytesRequests per second:    8.52 [#/sec]Time per request:       1173.770 [ms]Time per request:       117.377 [ms]Transfer rate:          144.22 [Kbytes/sec]
Concurrency Level:      10Time taken for tests:   12.824 secondsComplete requests:      100Failed requests:        0Write errors:           0Total transferred:      1736252 bytesHTML transferred:       1721900 bytesRequests per second:    7.80 [#/sec]Time per request:       1282.441 [ms]Time per request:       128.244 [ms]Transfer rate:          132.21 [Kbytes/sec]
RSS: 95.9492 МБайт RSS: 98.1445 МБайт RSS: 133.008 МБайт

Таким образом gunicorn оказался самым проворным и менее прожордивый по ресурсам памяти.


Оригинал статьи http://proft.me/2011/08/16/nastrojka-gunicorn-i-uwsgi-sravnenie-proizvoditeln/

0 комментариев

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