Настройка сервера под Ruby on Rails на nginx + Phusion Passenger + MySQL

image

Создание и запуск проекта на Ruby on rails в девелопменте делается в несколько строк. Как настроить рабочий сервер в продакшене еще неделю назад я представлял себе довольно смутно. Но так уж сложилось, что мне пришлось этим заняться, и все оказалось не так уж плохо. В статье рассмотрим настройку рабочего сервера для Rails приложений с нуля.

Самой быстрой и удобной связкой для Ruby on Rails на сегодняшний день считается nginx + Phusion Passenger. К тому же заявляется, что при использовании Ruby Enterprise Edition экономим в среднем 33% памяти. Их и будем устанавливать. База данных — MySQL. Все это будем запускать на Ubuntu 10.10 64bit.

Кроме самого веб-сервера еще нужно где-то хранить код и удобно его развертывать. Для этого у нас будет Git, Gitolite для управления репозитариями и Capistrano для развертывания.

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

Сразу скажу, что я тренировался на VirtualBox’e, так что будет несколько специфичных шагов, типа настройки SSH.

Для простоты условимся, что адрес сервера server, пользователь на сервере deployer, на локальной машине user, а работать будем с проектом project.

Если пользователя deployer нет, добавим:
root@server# adduser deployer


По умолчанию в Ubuntu нельзя залогиниться под рутом, так что либо перед каждой командой вводим sudo, либо эмулируем рута с помощью:
deployer@server$ sudo -i


1. Обновляемся

Для начала нам нужно обновить систему. Для этого пропишем нужные репозитарии в /etc/apt/sources.list.

Список репозитариев можно сгенерировать на http://repogen.simplylinux.ch/: выбираем страну, версию, ставим все галки и вперед. Получим что-то такое:
#############################################################
################### OFFICIAL UBUNTU REPOS ###################
#############################################################

###### Ubuntu Main Repos
deb <a href="http://ru.archive.ubuntu.com/ubuntu/">ru.archive.ubuntu.com/ubuntu/</a> maverick main restricted universe multiverse 
deb-src <a href="http://ru.archive.ubuntu.com/ubuntu/">ru.archive.ubuntu.com/ubuntu/</a> maverick main restricted universe multiverse 

###### Ubuntu Update Repos
deb <a href="http://ru.archive.ubuntu.com/ubuntu/">ru.archive.ubuntu.com/ubuntu/</a> maverick-security main restricted universe multiverse 
deb <a href="http://ru.archive.ubuntu.com/ubuntu/">ru.archive.ubuntu.com/ubuntu/</a> maverick-updates main restricted universe multiverse 
deb <a href="http://ru.archive.ubuntu.com/ubuntu/">ru.archive.ubuntu.com/ubuntu/</a> maverick-proposed main restricted universe multiverse 
deb <a href="http://ru.archive.ubuntu.com/ubuntu/">ru.archive.ubuntu.com/ubuntu/</a> maverick-backports main restricted universe multiverse 
deb-src <a href="http://ru.archive.ubuntu.com/ubuntu/">ru.archive.ubuntu.com/ubuntu/</a> maverick-security main restricted universe multiverse 
deb-src <a href="http://ru.archive.ubuntu.com/ubuntu/">ru.archive.ubuntu.com/ubuntu/</a> maverick-updates main restricted universe multiverse 
deb-src <a href="http://ru.archive.ubuntu.com/ubuntu/">ru.archive.ubuntu.com/ubuntu/</a> maverick-proposed main restricted universe multiverse 
deb-src <a href="http://ru.archive.ubuntu.com/ubuntu/">ru.archive.ubuntu.com/ubuntu/</a> maverick-backports main restricted universe multiverse 


Дальше обновляем систему:
root@server# apt-get update
root@server# apt-get upgrade
root@server# apt-get install build-essential


Добавляем русскую локаль:
root@server# locale-gen ru_RU.UTF-8


В /etc/default/locale прописываем
LANG="ru_RU.UTF-8"


Настраиваем системное время:
root@server# dpkg-reconfigure tzdata
root@server# apt-get install ntp
root@server# ntpdate ntp.ubuntu.com


2. SSH

Если у вы тоже тренируетесь на VirtualBox за NAT’ом, то сначала нужно добавить еще одну сетевую карту и настроить host-only подключение.

Устанавливаем и добавляем ssh-server в автозапуск
root@server# apt-get install openssh-server
root@server# update-rc.d -f ssh defaults
root@server# /etc/init.d/ssh start


Пробуем зайти на сервер:
user@localhost$ ssh deployer@server


Ввели пароль, зашли. По-умолчанию вход через ssh происходит по паролю. В целях безопасности это лучше отключить и настроить вход только по ключу, это пригодится и в дальнейшем.

Сгенерируем ключи, если их еще нет на своей рабочей машине, при желании можно задать ключевую фразу:
user@localhost$ mkdir ~/.ssh
user@localhost$ chmod 700 ~/.ssh
user@localhost$ ssh-keygen -t rsa


Копируем сгенерированный открытый ключ на сервер:
user@localhost$ ssh-copy-id deployer@server


Если у Вас нет ssh-copy-id (как у меня под Mac’ом), то копируем ключ через scp:
user@localhost$ scp ~/.ssh/id_rsa.pub deployer@server:~/.ssh/authorized_keys


Тут мы просто заменяем файл authorized_keys на сервере, так как у нас первый пользователь, если добавлять других, дописываем в конец файла. Заходим на сервер еще раз:
user@localhost$ ssh deployer@server


Если вы зашли и не вводили пароль (или вводили только ключевую фразу), то теперь можно выключить аутентификацию через пароль (крайне рекомендую перед этим все хорошенько перепроверить) — в файле /etc/ssh/sshd_config:
PasswordAuthentication no


3. Git + Gitolite

На локальной машине Git установлен и настроен.

Теперь настроим Gitolite, который позволяет удобно управлять git-репозитариями и разграничивать права доступа для различных пользователей.

Устанавливаем Git и Gitolite на сервере:
root@server# apt-get install git-core
root@server# apt-get install gitolite


Доступ к gitolite осуществляется с помощью Вашего публичного ssh-ключа. Скопируем его на сервер с именем локального пользователя и зарегистрируем в gitolite. У нас добавится новый пользователь с именем gitolite.

На локальной машине:
user@localhost$ scp ~/.ssh/id_rsa.pub deployer@server:/tmp/user.pub


На сервере:
root@server# su gitolite
gitolite@server$ gl-setup /tmp/user.pub


На локальной машине клонируем управляющий репозитарий, с помощью которого дальше будем добавлять рабочие репозитарии, устанавливать на них права и т.п.
git clone gitolite@server:gitolite-admin.git


Управление репозитариями и доступ к ним настраивается в conf/gitolite.conf. Для добавления репозитария просто добавляем его в конфиг, как в примере ниже, для добавления пользователя копируем его публичный ключ в keydir. Коммитим, пушим, и у нас все работает.

Простой пример gitolite.conf, из которого должно быть все понятно (тут добавлен пользователь deployer с сервера, об этом будет дальше):
@repos = project project_2

repo gitolite-admin
 RW+ = user

repo testing
 RW+ = @all

repo @repos
 RW = user
 R = deployer


Клонируем репозитарий с проектом на локальную машину:
user@localhost$ git clone gitolite@server:project.git


4. MySQL

Тут все просто:
root@server# apt-get install mysql-server
root@server# apt-get install libmysqlclient16-dev # Для гема mysql2


Зададим пароль на рута:
root@server# mysqladmin -u root password PASSWORD


Запустим:
root@server# service mysql start


5. Ruby

Ставим последнюю версию Ruby Enterprise Edition:
root@server# wget <a href="http://rubyenterpriseedition.googlecode.com/files/ruby-enterprise_1.8.7-2011.03_amd64_ubuntu10.04.deb">rubyenterpriseedition.googlecode.com/files/ruby-enterprise_1.8.7-2011.03_amd64_ubuntu10.04.deb</a>
root@server# dpkg -i ruby-enterprise_1.8.7-2011.03_amd64_ubuntu10.04.deb


6. Добавим кое-что еще

Настроим gem, так, чтобы он не устанавливал документацию и укажем репозитарии. Правим ~/.gemrc:
---
:sources:
- <a href="http://gemcutter.org">gemcutter.org</a>
- <a href="http://gems.github.com">gems.github.com</a>
gem: --no-ri --no-rdoc


Чтобы работала консоль в Rails:
root@server# apt-get install libreadline-ruby1.8


Java (нужна, например, для сжатия статики с помощью Jammit)
root@server# apt-get install openjdk-6-jre


ImageMagick
root@server# apt-get install imagemagick


7. Phusion Passenger и Nginx

Passenger с nginx ставится не намного сложнее, чем Ruby.

Если не нужна поддержка дополнительных модулей nginx, то:
root@server# passenger-install-nginx-module


Везде выбираем «Да» или «1». Если чего-то будет нехватать, то установщик об этом скажет.

Если нужно собрать nginx с каким-либо дополнительным модулем (мне нужен был http_gzip_static_module для отдачи статики в gzip), то тут немного другой путь:
deployer@server$ wget <a href="http://nginx.org/download/nginx-0.8.54.tar.gz">nginx.org/download/nginx-0.8.54.tar.gz</a>
deployer@server$ tar -xzf nginx-0.8.54.tar.gz
root@server# passenger-install-nginx-module # Выбираем 2 и указываем /home/deployer/nginx-0.8.54


Когда спросят Extra arguments to pass to configure script:
--with-http_gzip_static_module


Все должно собраться и работать.

Для мониторинга за системой можно использовать:
root@server# passenger-status
root@server# passenger-memory-stats
root@server# htop


Правим конфиг nginx (/opt/nginx/conf/nginx.conf). Для тонкой настройки смотрим документацию.

Прописываем нужные пути для логов и pid. Для работы passenger’а в секции server прописываем passenger_enabled on; и путь к каталогу public нашего Rails-проекта.

У меня получилось примерно следующее:
user deployer deployer;
worker_processes 1;

error_log /var/log/nginx/error.log info;
pid /var/run/nginx.pid;

events {
 worker_connections 1024;
}

http {
 passenger_root /usr/local/lib/ruby/gems/1.8/gems/passenger-3.0.5;
 passenger_ruby /usr/local/bin/ruby;

include mime.types;
 default_type application/octet-stream;

log_format main '$remote_addr - $remote_user [$time_local] "$request" '
 '$status $body_bytes_sent "$http_referer" '
 '"$http_user_agent" "$http_x_forwarded_for"';

access_log /var/log/nginx/access.log main;

sendfile on;
 tcp_nopush on;

keepalive_timeout 65;

gzip on;
 gzip_static on;

server {
 listen 80;
 server_name project.ru <a href="http://www.project.ru;">www.project.ru;</a>

passenger_enabled on;
 root /home/deployer/sites/project/current/public;

charset utf-8;

# redirect project.ru -> <a href="http://www.project.ru">www.project.ru</a>
 if ($host != 'www.project.ru' ) {
 rewrite ^/(.*)$ <a href="http://www.project.ru/">www.project.ru/</a>$1 permanent;
 }

# redirect <a href="http://www.project.ru/some/url/">www.project.ru/some/url/</a> -> <a href="http://www.project.ru/some/url">www.project.ru/some/url</a>
 rewrite ^(.+)/$ $1 permanent;

# static assets expires
 location ~* \.(jpg|jpeg|gif|giff|png|flv|css|swf)$ {
 expires max;
 }

location ~* ^/assets/* {
 expires max;
 }
 }

}


Не забудем сделать папку для логов:
root@server# mkdir /var/log/nginx


Добавим скрипт /etc/init.d/nginx для автозапуска nginx:
#! /bin/sh

### BEGIN INIT INFO
# Provides: nginx
# Required-Start: $all
# Required-Stop: $all
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: starts the nginx web server
# Description: starts nginx using start-stop-daemon
### END INIT INFO

PATH=/opt/nginx/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
DAEMON=/opt/nginx/sbin/nginx
NAME=nginx
DESC=nginx
PID=/var/run/${NAME}.pid

test -x $DAEMON || exit 0

# Include nginx defaults if available
if [ -f /etc/default/nginx ] ; then
 . /etc/default/nginx
fi

set -e

case "$1" in
 start)
 echo -n "Starting $DESC: "
 start-stop-daemon --start --quiet --pidfile $PID --exec $DAEMON --$DAEMON_OPTS
 echo "$NAME started."
 ;;
 stop)
 echo -n "Stopping $DESC: "
 start-stop-daemon --stop --quiet --pidfile $PID --exec $DAEMON
 echo "$NAME stopped."
 ;;
 restart|force-reload)
 echo -n "Restarting $DESC: "
 start-stop-daemon --stop --quiet --pidfile $PID --exec $DAEMON
 sleep 1
 start-stop-daemon --start --quiet --pidfile $PID --exec $DAEMON -- $DAEMON_OPTS
 echo "$NAME restarted."
 ;;
 reload)
 echo -n "Reloading $DESC configuration: "
 start-stop-daemon --stop --signal HUP --quiet --pidfile $PID --exec $DAEMON
 echo "$NAME reloaded."
 ;;
 configtest)
 echo -n "Testing $DESC configuration: "
 if test_nginx_config
 then
 echo "$NAME."
 else
 exit $?
 fi
 ;;
 status)
 status_of_proc -p $PID "$DAEMON" nginx && exit 0 || exit $?
 ;;
 *)
 N=/etc/init.d/$NAME
 echo "Usage: $N {start|stop|restart|force-reload|reload|configtest}" >&2
 exit 1
 ;;
esac

exit 0


Добавим в автозапуск:
root@server# chmod +x /etc/init.d/nginx
root@server# update-rc.d -f nginx defaults


8. Capistrano

Думаю все в курсе, что это, так что сразу начнем.

На локальной машине в каталоге вашего проекта:
user@localhost$ gem install capistrano
user@localhost$ capify .


Настройка Capistrano довольно хорошо описана, остановимся на основных моментах. В config/deploy.rb нужно задать название приложения, адрес репозитария и путь на сервере, куда деплоим:
set :application, "project"
set :repository, "gitolite@server:#{application}.git"
set :scm, :git

set :user, "deployer"
set :use_sudo, false
set :deploy_to, "/home/#{user}/sites/#{application}"
server "server", :app, :web, :db, :primary => true

set :keep_releases, 5
set :deploy_via, :remote_cache

namespace :deploy do
 task :start, :roles => :app do
 run "touch #{current_release}/tmp/restart.txt"
 end

task :stop, :roles => :app do
 # Do nothing.
 end

desc "Restart Application"
 task :restart, :roles => :app do
 run "touch #{current_release}/tmp/restart.txt"
 end

desc "Regenerate css with Sass and package assets with Jammit"
 task :package_assets, :roles => :app do
 # Add `gem 'sass'` in your gemfile.rb if no task sass:update
 run "RAILS_ENV=production cd #{deploy_to}/current && rake sass:update && jammit"
 end
end

after "deploy:update", "deploy:cleanup"
before "deploy:restart", "deploy:package_assets"


Перед началом развертывания нужно зайти на сервер, сгенерировать ssh ключи для пользователя deployer и добавить их в Gitolite:

На сервере:
deployer@server$ ssh-keygen -t rsa
deployer@server$ ssh deployer@server # Чтобы ключ добавился в ~/.ssh/known_host


На клиенте в каталоге gitolite-admin (в конфиге выше deployer уже даны права на репозитарии):
user@localhost$ scp deployer@server:~/.ssh/id_rsa.pub keydir/deployer.pub


Коммитим, пушим, как обычно:
user@localhost$ git add .
user@localhost$ git commit -m «Add deployer.pub»
user@localhost$ git push


Подготовим дерево каталогов на сервере:
user@localhost$ cap deploy:setup


Проверяем все ли в порядке:
user@localhost$ cap deploy:check


Если все ок, то пушнем последнюю версию на сервер:
user@localhost$ cap deploy:update


Заходим на сервер в каталог с проектом (~/sites/project/current), устанавливаем гемы, даем доступ к БД и заполняем ее:
root@server# bundle install
deployer@server$ mysql -u root -p
> grant all privileges on project.* to deployer@localhost identified by 'PASSWORD';
deployer@server$ RAILS_ENV=production rake db:setup


При этом config/database.yml выглядит следующим образом:
development:
 adapter: mysql2
 database: project_development
 username: root
 encoding: utf8

production:
 adapter: mysql2
 database: project
 username: deployer
 password: mydeployer
 host: localhost
 encoding: utf8

test:
 adapter: mysql2
 database: project_test
 username: root
 encoding: utf8


Проверяем:
deployer@server$ rails c production
> app.get("/")


Должны получить 2xx или 3xx код ответа сервера.

Стартуем:
user@localhost$ cap deploy:start


Дальше можно будет деплоить с помощью:
user@localhost$ cap deploy:migrations


9. Ответственный момент

root@server# /etc/init.d/nginx start


Наслаждаемся.


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

Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.