Настройка сервера под Ruby on Rails на nginx + Phusion Passenger + MySQL
Создание и запуск проекта на 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 комментариев