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

17 апреля 2011 г.

Ruby on Rails

Создание и запуск проекта на 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 ru.archive.ubuntu.com/ubuntu/ maverick main restricted universe multiverse
deb-src ru.archive.ubuntu.com/ubuntu/ maverick main restricted universe multiverse

###### Ubuntu Update Repos
deb ru.archive.ubuntu.com/ubuntu/ maverick-security main restricted universe multiverse
deb ru.archive.ubuntu.com/ubuntu/ maverick-updates main restricted universe multiverse
deb ru.archive.ubuntu.com/ubuntu/ maverick-proposed main restricted universe multiverse
deb ru.archive.ubuntu.com/ubuntu/ maverick-backports main restricted universe multiverse
deb-src ru.archive.ubuntu.com/ubuntu/ maverick-security main restricted universe multiverse
deb-src ru.archive.ubuntu.com/ubuntu/ maverick-updates main restricted universe multiverse
deb-src ru.archive.ubuntu.com/ubuntu/ maverick-proposed main restricted universe multiverse
deb-src ru.archive.ubuntu.com/ubuntu/ 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 rubyenterpriseedition.googlecode.com/files/ruby-enterprise_1.8.7-2011.03_amd64_ubuntu10.04.deb
root@server# dpkg -i ruby-enterprise_1.8.7-2011.03_amd64_ubuntu10.04.deb

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

Настроим gem, так, чтобы он не устанавливал документацию и укажем репозитарии. Правим ~/.gemrc:
---
:sources:
- gemcutter.org
- gems.github.com
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 nginx.org/download/nginx-0.8.54.tar.gz
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 www.project.ru;

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

charset utf-8;

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

# redirect www.project.ru/some/url/ -> www.project.ru/some/url
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

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

Теги: рубрика FreeBSD, Linux
  • Похожие статьи
  • Предыдущие из рубрики