Skip to main content

Запуск ruby on rails на uwsgi на примере redmine

Для лично-рабочих нужд я активно использую task-tracking system под названием Redmine. Redmine практически всем хорош из открытых трекеров, но очень любит память. Традиционно он запускается через rails server (WEBRick) или rails-specific сервер thin. Во второй конфигурации у меня он жрал 300Мб оперативной памяти, а среднее время генерации страницы было близко к секунде, что совершенно неприемлимо. По этой причине я решил использовать uwsgi - совершенно прекрасный appserver, который я активно использую для своего творчества на python. Uwsgi работает очень быстро (действительно - очень!), достаточно экономно относится к памяти, обладает широчайшими возможностями конфигурирования - просто-таки мечта, а не сервер. До недавнего времени он умел работать только с python (под который интерфейс uWSGI, строго говоря, и создавался), но теперь умеет обрабатывать ruby и даже PHP. Минусом uwsgid является, во-первых, тот факт, что он находится на переднем крае разработок, а значит - может работать ой как своеобразно. Не менее своеобразно он настраивается, и документации по нему очень мало.

Идея

Запускать будем redmine, но это типовое rails приложение, все остальные запускаются так же. В качестве веб-сервера - nginx, апп-сервер - uwsgid, база данных - postgresql (но будет уточнение для mysql), для управления uwsgid будем использовать supervisord

Зависимости

Ставим системные компоненты:

apt-get update
apt-get install gcc cpp make nginx
#postgresql
apt-get install postgresql libpq-dev
#или mysql
apt-get install mysql libmysqlclient-dev

Лирическое отступление: я рекомендую использовать rvm вместо штатного руби. RVM живет вне пакетов, и обновлять его надо вручную, однако он позволяет получить самую свежую версию ruby, кроме того - он позволяет использовать несколько разных версий языка, а так же - несколько разных наборов библиотек (gem), не пересекая их друг с другом. Если у вас несколько приложений на ruby делят один сервер - это крайне удобный функционал.

Ставим ruby:

#системная версия
apt-get install ruby1.8 ruby1.8-dev rubygems
#ИЛИ rvm
curl -L https://get.rvm.io | bash -s stable
rvm install 1.9.3-p429

Теперь доустанавливаем gem bundle. Он отвечает за установку необходимых для работы веб-приложения библиотек

gem install bundle

Установка Redmine

Создадим базу данных и пользователя для работы с ней:

#вариант для postgresql
sudo -u postgres psql
psql# create user redmineuser nocreatedb nocreateuser;
psql# alter role redmineuser with password 'MySecretP@ssw0rd';
psql# create database redminedb owner redmineuser;

#вариант для mysql
mysql -p
mysql> create database redminedb;
mysql> grant all on redminedb.* to redmineuser@localhost identified by 'MySecretP@ssw0rd';
mysql> flush privileges;

Теперь скачиваем и ставим сам redmine. Последнюю версию берем [http://rubyforge.org/frs/?group_id=1850] тут.

wget http://rubyforge.org/frs/download.php/76933/redmine-2.3.1.tar.gz
tar -xf redmine-2.3.1.tar.gz
mv redmine-2.3.1 redmine

Для корректной работы надо исправить файл config/database.yml в корне приложения. Вот пример для postgresql:

production:
    adapter: postgresql
    database: redminedb
    host: localhost
    username: redmineuser
    password: MySecretP@ssw0rd
    encoding: utf8
    schema_search_path: public

А вот пример для mysql:

production:
    adapter: mysql
    database: redminedb
    host: localhost
    username: redmineuser
    password: MySecretP@ssw0rd
    encoding: utf8

Теперь самое сложное - установка GEM-ов. Gem - это библиотека в терминологии ruby. Их количество без преувеличения огромно, и делают они буквально все - appservers, драйвера к библиотекам, рисование графиков… Любое нормальное руби-приложение использует множество гемов для работы. Для упрощения установки используется (сюрприз!) gem под названием bundle. Заходим в папку redmine и выполняем:

#вариант для postgresql
bundle install --without development test sqlite mysql rmagick

#ИЛИ для mysql
bundle install --without development test sqlite pg rmagick

Запуск приложения (любого!) от root не рекомендуется, так как взлом этого приложения скомпрометирует систему полностью. Сделаем непривелигированного юзера:

useradd --system -m -d /var/www/redmine -s /bin/bash redmine

Послеустановочные действия:

rake generate_secret_token
RAILS_ENV=production rake db:migrate
RAILS_ENV=production rake redmine:load_default_data
mkdir -p tmp tmp/pdf public/plugin_assets
chown -R redmine:redmine files log tmp public/plugin_assets
chmod -R 755 files log tmp public/plugin_assets

Установка и подключение uwsgid

Ставим uwsgid. Его придется ставить из gem, потому, что тот, что идет в комплекте с системой, слишком старый - и не умеет работать с rails

gem install uwsgi

Конфигурация uwsgi расскажет ему о том, как запустить rails. Пишем в файл uwsgi.ini

[uwsgi]
socket = /tmp/redmine.sock
chmod-socket = 770
master = true
lazy = true
processes = 2 #по количеству ядер в системе. У меня atom 525, два ядра
post-buffering = 4096
#следующие три строки - только для тех, кто использует RVM
rvm-path = /usr/local/rvm
#имя ruby, указывать обязательно, даже, если он указан по умолчанию
rvm = ruby-1.9.3-p429
#название gemset в формате язык@гемсет
gemset = ruby-1.9.3-p429@redmine
uid = redmine
gid = www-data
env = RAILS_ENV=production
chdir = /var/www/redmine
rack = /var/www/redmine/config.ru
http-modifier1 = 7

Сам по себе supervisor умеет порождать процессы-обработчики (это так называемый режим императора), но это совсем молодая фишка и работает она… Ну, как любая совсем новая фишка. Лично я рекомендую использовать для управления supervisord. Его задача - запускать и останавливать другие процессы (в данном контексте - uwsgi). Он обладает очень простым (если не сказать - примитивным) конфигурационным файлом и очень прост в работе. Устанавливаем:

apt-get install supervisord
/etc/init.d/supervisor start

Теперь нам надо создать конфиг для нашего приложения. В папке /etc/supervisor/conf.d создаем файл с произвольным именем и окончанием .conf. Содержимое:

[program:redmine]
directory=/var/www/redmine
command=uwsgi --ini /var/www/redmine/uwsgi.ini
autostart=true

Теперь его надо подключить. У supervisord есть собственная консоль, она называется supervisorctl. Заходим:

supervisorctl
#поиск новых конфигов
supervisor> reread
#нашелся
redmine: available
#добавляем
supervisor> add redmine
redmine: added process group
#проверим, работает?
supervisor> status
redmine                          BACKOFF    can't find command 'uwsgi'

Не находится он потому, что supervisorctl не может найти rvm-версию uwsgi. Ок, укажем вручную:

find / -name uwsgi
[...]
/usr/local/rvm/gems/ruby-1.9.3-p429/bin/uwsgi
[...]

Вносим изменения в наш конфиг /etc/supervisor/conf.d/redmine.conf. Теперь он выглядит так:

[program:redmine]
directory=/var/www/redmine
command=/usr/local/rvm/gems/ruby-1.9.3-p429/bin/uwsgi --ini /var/www/redmine/uwsgi.ini
autostart=true

Обновим конфиг без перезагрузки supervisord:

supervisorctl update redmine
supervisorctl status
[...]
redmine                          RUNNING    pid 25365, uptime 0:00:18
[...]

Отлично, uwsgi работает, подключаем nginx. Debian-way предполагает, что конфигурации сайтов находятся в отдельных файлах каталога /etc/nginx/sites-enabled. Содержимое /etc/nginx/sites-enabled/redmine

server {
    listen 80;
    server_name redmine.office;
    location @redmine {
        uwsgi_modifier1 7;
        include uwsgi_params;
        uwsgi_pass unix:///tmp/redmine.sock;
    }
    location / {
        root /var/www/redmine/public;
        try_files $uri $uri/index.html $uri.html @redmine;
    }
}

Кроме того, потребуется описание типовых параметров uwsgi, файл /etc/nginx/uwsgi_params

uwsgi_param     QUERY_STRING            $query_string;
uwsgi_param     REQUEST_METHOD          $request_method;
uwsgi_param     CONTENT_TYPE            $content_type;
uwsgi_param     CONTENT_LENGTH          $content_length;

uwsgi_param     REQUEST_URI             $request_uri;
uwsgi_param     PATH_INFO               $document_uri;
uwsgi_param     DOCUMENT_ROOT           $document_root;
uwsgi_param     SERVER_PROTOCOL         $server_protocol;
uwsgi_param     UWSGI_SCHEME            $scheme;

uwsgi_param     REMOTE_ADDR             $remote_addr;
uwsgi_param     REMOTE_PORT             $remote_port;
uwsgi_param     SERVER_PORT             $server_port;
uwsgi_param     SERVER_NAME             $server_name;

Теперь достаточно перезапустить nginx - и редмайн готов к использованию!