Этот пост будет посвящён тому, как на Ubuntu Server 12.04 развернуть боевой (хотя и не приспособленный к высоким нагрузкам) свервер для Ruby On Rails приложениq. Почему, собственно, RoR? Есть ведь много других средств для веб-разработки, в том числе более популярных (WordPress и Drupal на LAMP) и не менее прогрессивных (например, Django под Python).
Отвечу кратко и честно: до Django я пока не добрался, но, в силу обстоятельств, недавно пришлось поднимать Rails-сайт на голом сервере с нуля (ни разу прежде не встречавшись с Rгин On Rails и с Ruby в вообще). Задача эта вылилась в бессонную ночь, а потом в увлекательный день, в течение которого я от души поковырялся в азах новой для себя технологии. Пол сравнению с привычным для меня и любимым мной Drupal`ом я увидел в Ruby On Rails, прежде всего, принципиально новую идеологию.
Очевидные для меня плюсы:
- Быстрый и лёгкий деплоймент (переход от разрабатываемой версии к «боевой»).
- Миграции базы данных, которые можно взять под версионный контроль. Любые изменения в схемах данных можно с лёгкостью накатывать и откатывать назад.
- Огромное количество средств в рамках фреймворка для генерации типового кода (почти не приходится создавать руками файлы и заготовки функционала).
- Строгое следование подходу MVC при проектировании (модель-вид-контроллер) при построении приложения. Не считаю подход идеальным и единственно возможным, но подобная типизация здорово упрощает поддержку «чужих» проектов. Заранее ясна глобальная логика любого приложения.
На мой взгляд, этого вполне достаточно, чтобы уделить внимание Ruby On Rails.
Мы в своей студии вряд ли переведём разработку на RoR в ближайшей перспективе, останемся на Drupal, потому что у него есть много других плюсов, как субъективных (он нами хорошо освоен), так и объективных (гораздо большая совместимость и переносимость). Но знакомства с RoR хватило, чтобы без опаски внедрять проекты, написанные на базе этого фреймворка (например, систему управления проектами Redmine), и для неспешного погружения в технологию в свободные от работы часы (что называется, для души).
Руководств по установке RoR на сервер — много. Но мой написан для тех, кто сталкивается с ruby вообще впервые. Поэтому кроме настройки рабочего окружения я, по возможности, буду рассказывать и об основных особенностях RoR.
Почему Passenger?
Общая схема работы RubyOnRails-сайта такова: application-сервер исполняет ваш ruby-проект, выступая в роли бэкэнда, потом передаёт всё полученное хозяйство на веб-сервер, который и показывает сгенерированные html-странички (и самостоятельно отдаёт всю статику).
И есть две популярных связки для выполнения всего процесса:
- Веб-сервер nginx + unicorn для Ruby
- Веб-сервер Apache + passenger для Ruby
Первая — максимально быстрая (и вполне стабильная), вторая — максимально совместимая с привычным LAMP-стеком и однозначная в настройке и установке. Проще говоря, если у вас на сервере будет крутится ещё и что-то традиционное на PHP (например, phpMyAdmin для управления базами) и если вы не желаете на уровне всей системы что-то собирать из исходников, то вам юудет проще ужиться со вторым вариантом.
Установка Apache2
Установим для начала Apache2, пока без каких либо модулей, но с dev-пакетами (ни PHP, ни, тем более, Ruby он на данном этапе обрабатывать не сможет)
sudo apt-get install apache2 apache2-dev
При запуске сервера есть шанс увидеть сообщение apache2: Could not reliably determine the server's fully qualified domain name, using 127.0.0.1 for ServerName
. Избавиться от него можно установив правильное имя хоста (командой sudo hostname example.com
) или более грубо. Но идейно правильным способом является именно установка валидного hostname`а.
После установки Apache вы можете уже заглянуть по адресу http://127.0.0.1 (здесь и далее поменяйте его на ваш внешний IP) и увидеть заглушку Apache вроде этой:
Её контент хранится в /var/www/public_html/index.html
. Но статическая заглушка нас не очень-то интересовала, наша цель — Ruby On Rails.
Создание пользователя и установка Ruby On Rails в рамках RVM окружения
В репозитория Ubuntu есть «рельсы», но далеко не самой свежей версии, которую очень хотелось бы иметь для обучения разработке. Собрать свежую версию можно было бы из исходников, но это не совсем в стиле Ubuntu. Даже самосборными пакетами засорять систему не очень приятно (ведь их потом придётся как-то ещё и обновлять).
Мы воспользуемся мощным решением — RVM (Ruby Version Manager). По сути, это набор скриптов, который позволяет собирать и запускать Ruby (и Ruby On Rails) от имени конкретного пользователя и с его правами. То есть мы будем получать свежие версии исходников, компилировать их, но ставить не на уровне всей системы, а в ~/.rvm
. Удалив пользователя вместе с его ~/.rvm
, мы избавимся от всех следов Ruby в системе. Это не наша цель, но это позволяет экспериментировать без последствий. Также RVM позволяет на одной машине иметь несколько версий ruby (нам это сейчас не потребуется, но опытным разработчикам, поддерживающим параллельно разные проекты — такое просто необходимо).
Создадим юзера с именем ror
и домашней директорией /var/www/ror
:
sudo useradd -d /var/www/ror -s /bin/bash ror
Ключ -d
указывает адрес будущей домашней директории, -s
задаёт командный интерпретатор (шелл) для создаваемого пользователя.
Зададим ему пароль (и запомним его):
sudo passwd ror
Разрешим юзеру выполнять команды от рута через sudo
, добавив нужную строчку в файл /etc/sudoers
:
sudo sh -c "echo 'ror ALL=(ALL:ALL) ALL' >> /etc/sudoers"
Теперь авторизуемся под созданным юзером и будем действовать далее от его имени. То есть закроме текущее соединение с сервером и авторизуемся уже под пользователем ror.
Установим кучу зависимостей, которые потребуются нам в дальнейшем (и для типовых RoR-проектов в том числе):
sudo apt-get install build-essential git-core screen curl libcurl3 libcurl3-gnutls libcurl4-openssl-dev mc imagemagick libxslt-dev libxml2-dev python-software-properties nodejs
Далее скачаем и запускаем установщик RVM (это готовый скрипт, который сделает много рутинной работы за нас):
bash -s stable < > ~/.bashrc
source ~/.bashrc
Теперь мы можем установить свежайшую версию Ruby (пока без рельсов) одной простой командой:
rvm install 1.9.3
После запуска команды вам предложат почитать информацию о зависимостях, мы выше уже поставили всё необходимое, поэтому выходите клавишей q
, чтобы продолжилась инсталляция.
Компиляция и установка может занять немало времени.
После того, как Ruby установится можно научить RVM использовать именно эту версию по умолчанию:
rvm use --default 1.9.3
Объясним RVM как добраться до терминала и сохраним настройки в профиль:
echo "shell -${SHELL}" >> ~/.screenrc
echo "source ~/.profile" >> ~/.bash_profile
echo "source ~/.bashrc" >> ~/.bash_profile
Рельсы, как и многие другие расширения Ruby, являются так называемыми gem`ами (шестеренками). Суть gem`ов в том, что они хранятся в едином репозитории и могут быть подключены к вашему приложению как зависимости. То есть вам не придётся паковать каждый гем в приложение, достаточно в зависимостях указать названия и версии нужны гемов, а при развёртывании приложения они подтянуться автоматически. Для простоты вы можете воспринимать гемы как пакеты дополнительных библиотек и программ, существующие не на уровне ОС, а на уровне Ruby.
А теперь установим свежайшую версию рельсов как гем:
rvm 1.9.3 do gem install rails -v 3.2.8
В моём случае, это вышедшая 5 дней назад версия 3.2.8. Посмотреть, какая версия будет свежайшей на момент ваших экспериментов, можно всегда на официально сайте RoR.
Кстати, после запуска команды вам придётся немного подождать, прежде чем вы увидите первые диагностические сообщения об установке, типа Fetching: i18n-0.6.0.gem (100%)
. Так что пустой экран консоли в начале установки вас пугать не должен.
Установка MySQL
sudo apt-get install mysql-client mysql-server libmysql-ruby libmysqlclient-dev
В процессе установки мастер попросит вас задать и подтвердить пароль администратора СУБД MySQL. Не забудьте этот пароль:
Теперь создадим юзера и базу данных для наших экспериментов.
Подключаемся к базе:
mysql -u root -p
В консоли mysql выполняем две команды. Сначала создаём базу:
CREATE DATABASE `rortestapp`;
Потом создаём юзера и даём ему полный доступ к базе:
GRANT ALL PRIVILEGES ON `rortestapp`.* TO `rordbuser`@`localhost` IDENTIFIED BY 'mypassword' WITH GRANT OPTION;
Выходим из консоли mysq:
quit;
Теперь установим библиотеку для поддержки MySQL со стороны Ruby:
rvm 1.9.3 do gem install mysql2
Создание первого приложения
Переходим в домашнюю директорию:
cd /var/www/ror
Создаём каркас приложения с именем testapp
(с поддержкой базы MySQL):
rails new testapp -d mysql
После этого в текущей директории должна появиться поддиректория testapp
(а в ней много директорий, включая директорию testapp/public
(публичная часть приложения, откуда оно будет запускаться и где можно хранить статические файлы), и testapp/config/environments
, где хранятся настройки приложения).
ror@localhost:~/testapp$ ls
app config config.ru db doc Gemfile Gemfile.lock lib log public Rakefile README.rdoc script test tmp vendor
Давайте для лучшего понимания ситуации заглянем в папку с настройками:
ls /var/www/ror/testapp/config/environments
В директории лежат три файла:
development.rb production.rb test.rb
Эти файлы позволяют задать раздельные настройки, соответственно, для разработки (evelopment.rb), боевого окружения (production.rb) и тестового окружения (test.rb). Одна из киллер-фич Ruby On Rails — это непрерывная интеграция (continuous integration), позволяющая в несколько консольных команд все новые фишки из окружения разработчика перенести в окружение тестировщика, а оттуда — на боевой публичный проект. Откатываться тоже можно в произвольном порядке.
Нам пока подойдут настройки по умолчанию. Но о наличии трёх независимых сред — теперь нам известно.
Но сейчас мы с вами поскорее хотим пощупать живое приложение, поэтому давайте поскорее перейдём к настройки базы данных.
Конфиг базы задаётся в файле config/database.yml
, поэтому будем править его:
nano /var/www/ror/testapp/config/database.yml
В файле будет представлено три (уже знакомых вам по окружениям) секции. Мы будем править продакшн-секцию и доведём её до такого состояния:
production: adapter: mysql2 encoding: utf8 reconnect: false database: rortestapp pool: 5 username: rordbuser password: mypassword socket: /var/run/mysqld/mysqld.sock
То есть изменим базу данных (database), пользователя базы данных (username) и зададим пароль (password).
Сохраним файл (клавиша F2
).
Запуск приложения
Наш первый запуск пройдёт с использованием встроенного веб-сервера WEBrick.
Запускаться мы должны обязательно из публичной директории приложения, поэтому перейдём туда:
cd /var/www/ror/testapp/public
И запустим сервер, указав продакшн-окружение:
rails server -e production
В консоль у нас после этого потечёт информация от сервера (в частности, будет сказано, что сервер запустился на 3000-м порту и что оставить сервер можно в любой момент нажав Ctrl+C
на клавиатуре).
Мы же, давайте, пройдём в браузере по адресу http://127.0.0.1:3000, чтобы увидеть нечто вроде этого:
Если же вы узрите заглушку вроде этой:
…то это значит, что вам показывается статический файл public/index.html
. Давайте, в любом случае, удалим его, потому что даже если вам этот статический файл не показал WEBrick, то позже точно покажет Apache. В общем, удаляем:
rm index.html
Обновите страницу и теперь вы тоже увидите сообщение об ошибке.
Это нормальный результат. Мы получаем его, потому что в нашем приложение пока ничего не размещено по адресу /
(об этом же можно прочитать и в интерактивном логе веб-сервера на консоли: ActionController::RoutingError (No route matches [GET] "/"):
).
Оставим веб-сервер с помощью Ctrl+C
(можно этого не делать, а открыть вторую консоль — это уж на ваш вкус).
Выполним команду для создания контроллера и экшена в нём:
rails generate controller home index
Рельсы создадут ряд файлов, о чём будет показана информация на консоли:
create app/controllers/home_controller.rb route get "home/index" invoke erb create app/views/home create app/views/home/index.html.erb invoke test_unit create test/functional/home_controller_test.rb invoke helper create app/helpers/home_helper.rb invoke test_unit create test/unit/helpers/home_helper_test.rb invoke assets invoke coffee create app/assets/javascripts/home.js.coffee invoke scss create app/assets/stylesheets/home.css.scss
В частности, для этого контроллера будет автоматически создано представление (шаблон страницы), который вы сможете посмотреть и отредактировать по адресу ~/testapp/app/views/home/index.html.erb
Но маршрут до контроллера нам придётся создать руками. Для этого в файл config/routes.rb надо добавить (например, в конце на предпоследней строке перед end) строку:
root :to => "home#index"
Таким образом мы создадим маршрут для /
и направим его на экшн index
контроллера home
.
Теперь надо скомпилировать шаблоны командой:
RAILS_ENV=production bundle exec rake assets:precompile
Теперь можно снова запускать веб-сервер (если вы его останавливали, а не подключались в дополнительную консоль):
cd /var/www/ror/testapp/public
rails server -e production
Перейдя по http://127.0.0.1:3000 вы должны увидеть пресловутый «Hello world» или что там у вас написано в app/views/home/index.html.erb
.
Дальше мы рассмотрим как вместо тестового WEBrick перетащить ваше приложение под вполне боевой Apache. По части создания своего первого полезного приложения на RoR отправляю вас к приятному чтению русского перевода Ruby on Rails Tutorial.
Установка passenger для Apache2
Passenger — это модуль для Apache2, который позволяет отдавать статику через Apache и обрабатывать динамику средствами Ruby On Rails.
Для начала установим гем passenger:
gem install passenger
Теперь нашему пользователю станет доступная такая вот команда (которую и выполним) для компиляции модуля passenger:
passenger-install-apache2-module
Ещё раз вас успокою: компиляция в данном случае касается только текущего пользователя ror, на системном уровне мы ничего не засорим.
После запуска установщика он сообщит вам, что в его планы входит три операции:
- Установить модуль passenger
- Сообщить вам как настроить Apache
- И как настроить Ruby on Rails
На старте компиляции будут проверены зависимости, но если вы всё делали по инструкции, то с ними у вас проблем не будет.
В финале инсталляции вы увидите сообщение с рекомендациями по подключению модуля, а, нажав Enter, ещё и примерный конфиг для виртуального хоста Apache:
Давайте внедрим предложенные рекомендации на нашем сервере.
Для начал подключим модуль к Apache, создав новый пустой конфигурационный файл в /etc/apache2/conf.d/
(откуда конфиги подхватываются автоматически):
sudo nano /etc/apache2/conf.d/passenger
И вставим в файл следующие строки:
LoadModule passenger_module /var/www/ror/.rvm/gems/ruby-1.9.3-p194/gems/passenger-3.0.15/ext/apache2/mod_passenger.so PassengerRoot /var/www/ror/.rvm/gems/ruby-1.9.3-p194/gems/passenger-3.0.15 PassengerRuby /var/www/ror/.rvm/wrappers/ruby-1.9.3-p194/ruby
Сохраним файл (F2
).
Теперь отключим виртуальный хост Apache по умолчанию:
sudo a2dissite default
Создадим новый виртуальный хост:
sudo nano /etc/apache2/sites-available/rortestapp
Вставим в него такой конфиг:
# ServerName example.com # Раскомментируйте, если у вас есть домен DocumentRoot /var/www/ror/testapp/public AllowOverride all Options -MultiViews
Если вы собираетесь запускать несколько приложений на одном сервере (что вполне нормально), то вам для них потребуется создать разные виртуальные хосты по данному шаблону и в каждом указать разные домены в ServerName
. И, естественно, направить эти домены на ваш IP (через A-запись в DNS).
Активируем виртуальный хост:
sudo a2ensite rortestapp
Перезапустим Apache:
sudo service apache2 restart
По адресу http://127.0.0.1 теперь начнёт отображаться ваш «Hello world». Первый запуск приложения может быть достаточно медленным, не удивляйтесь.
А, в остальном, всё готово. Продуктивной вам разработки.
»