2012-12-09

RVM + Passenger + proxy Apache/Nginx

Переезд и проблемы

Однажды затеяли мы в компании переезд на новый сервер, ибо все когда-то устаревает и не справляется с новыми нагрузками и современными технологиями.
Имея на борту обоих серверов ISPManager, перенос простых проектов на PHP можно смело поручить службе поддержки. А что делать с капризными RoR-проектами? Отсутствие вменяемого системного администратора это даже плюс - не программингом единым жив программист, иногда полезно окунуться в системное администрирование.


Итак, имеем один годовалый сервер с RVM установленным в многопользовательском режиме (из под root, в общей папке) и пару проектов: один на Ruby on Rack и другой на Ruby on Rails. Оба работают из под Ruby версии 1.9.3. На самом деле, изначально первый проект (который на Rails) плясал под 1.9.2, но из-за проблем с содержанием нескольких версий Ruby на одном сервере был благополучно переключен на 1.9.3.
Была поставлена задача переноса со старого сервера древнейшего проекта Ruby 1.8.7 on Rails 2.2.2.
По руководствам с сайта RVM по приручению Passenger и разных версий Ruby мне не удалось решить проблему. Что ж, как говорится: "Даже если вас съели, у вас есть два выхода". Вот и я пошел другим путем.

Проксирование - наше все!

На новом сервере имеем фронтенд Nginx для статики с проксированием динамических запросов на Apache и RVM установленный в многопользовательском режиме, о чем уже было сказано выше.
Первым делом я попробовал установить RVM в однопользовательском режиме. Даешь каждому пользователю и его проекту по RVM! К сожалению, Apache не смог работать с несколькими mod_passenger. Откатываться не стал, да и вообще, как выяснилось, без разницы как установлен RVM.
Еще полезную вещь узнал. Оказыватся, чтобы нормально авторизоваться под любым пользователем из под root нужно:
> su username -
Тут мы попадаем в какую-то неполноценную эмуляцию терминала пользователя. Далее нужно залогиниться из под пользователя:
> /bin/bash --login
Вуаля! Мы полноценно авторизовались кем-то из под root. Помним про то, что пользователю должен быть включен доступ к shell и что можно им также нормально авторизоваться, зная его пароль, по ssh.
Команда для установки RVM в однопользовательском режиме доступна на официальном сайте RVM. Установим одной строкой. Затем нужно установить standalone-passenger, после чего запустить его демоном на отдельном порту, для дальнейшего проксирования на него. Перенесем файлы проекта в новую папку, настроим Apache/Nginx на подпапку проекта public. В этом нет ничего сложного, в интернете полно информации на тему.

RVM и Ruby

Дальнейшие действия выполняем из корневой папки.
Установим пользователю нужную версию Ruby:
> rvm install 1.8.7
Если у нас многопользовательский RVM, то полезно инициализировать отдельный gemset:
> rvm gemset create mygemset
и создать файл ".rvmrc" в корне c содержимым "rvm 1.8.7@mygemset", где "1.8.7" - нужная проекту версия Ruby и "mygemset" - имя коллекции гемов проекта.
Чтобы выбрать активную версию Ruby:
> rvm use 1.8.7
или если у нас используется gemset "mygemset":
> rvm use 1.8.7@mygemset
Далее ставим passenger:
> gem install passenger
Запускаем демона passenger на отдельном порту:
> passenger start -a 127.0.0.1 -p 3000 -d

Proxy Apache/Nginx

Вот так быстро мы добрались до проксирования. Будем "прокидывать" http-запросы на порт с запущеным Passenger с Ruby-проектом.
Я попробовал оба пути - проксирование Apache и Nginx. Да-да, длинная цепочка Nginx-Apache-Passenger, но я ее попробовал.
Возвращаемся под root и включаем модули Apache для проксирования:
> a2enmod proxy
> a2enmod proxy_http
Используем рестарт:
> apache2ctl restart
В настройках виртуального хоста указываем директивы (помним на каком порту запущен Passenger):
# Partial Apache configuration
<VirtualHost *:80>
    ServerName www.example.com
    DocumentRoot /var/www/example.com/public
    PassengerEnabled off
    ProxyPass / http://127.0.0.1:3000/
    ProxyPassReverse / http://127.0.0.1:3000/
</VirtualHost>
Здесь директива PassengerEnabled может выдать ошибку, если Passenger не установлен как модуль, так что можно просто закомментировать.
Важно указать слеш в конце адресов, а то без них у меня на статичные ресурсы Apache выдавал ошибку проксирования.
Теперь более правильный подход. Ну ни к чему нам посредник в лице Apache. Пишем для виртуального хоста в Nginx директивы:
# Partial Nginx configuration
server {
    listen 80;
    server_name www.example.com;
    root /var/www/example.com/public;
    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_set_header Host $host;
    }
}
Теперь все прекрасно работает в связке Nginx+Passenger.
Дело за малым - добавить в автозагрузку старт passenger на отдельном порту от имени пользователя проекта и из папки проекта, но это все потом, главное что сейчас завелось и можно пойти отпразновать победу.

UPD: Иногда проксирование на localhost (127.0.0.1) не работает, в этом случае можно просто везде вместо 127.0.0.1 указать IP-адрес сервера.