Pull to refresh

Настройка сервера под 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 http://ru.archive.ubuntu.com/ubuntu/ maverick main restricted universe multiverse
deb-src http://ru.archive.ubuntu.com/ubuntu/ maverick main restricted universe multiverse

###### Ubuntu Update Repos
deb http://ru.archive.ubuntu.com/ubuntu/ maverick-security main restricted universe multiverse
deb http://ru.archive.ubuntu.com/ubuntu/ maverick-updates main restricted universe multiverse
deb http://ru.archive.ubuntu.com/ubuntu/ maverick-proposed main restricted universe multiverse
deb http://ru.archive.ubuntu.com/ubuntu/ maverick-backports main restricted universe multiverse
deb-src http://ru.archive.ubuntu.com/ubuntu/ maverick-security main restricted universe multiverse
deb-src http://ru.archive.ubuntu.com/ubuntu/ maverick-updates main restricted universe multiverse
deb-src http://ru.archive.ubuntu.com/ubuntu/ maverick-proposed main restricted universe multiverse
deb-src http://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 http://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:
- http://gemcutter.org
- http://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 http://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 ^/(.*)$ http://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

Наслаждаемся.
Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.