Папа, настрой nginx

Есть задача, даже две. Первая - разобраться с настройкой nginx для отдачи rails-статики
и балансировки двух (трёх, сколько нужно), mongrel-ов. Вторая - всё то же самое, но без mongrel,
а с использованием passenger и, главное, через capistrano.

Nginx + mongrels

Первую задачу я реализовывал локально, на своей уютненькой убунте и всё решилось сравнительно
мирно.

Убрал из дефолтной конфигурации все директивы server и upstream,
оставил только директивы include - так гибче, если что.

##
# Virtual Host Configs
##

include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;

Добавил в отдельный файл конфигурацию апстрима на двух монгрелах (важен был сам принцип, а не количество)
и сервер:

upstream mongrel { # <-- имя upstream-а для ссылок на него
server localhost:3000; # Первый mongrel
server localhost:3001; # Второй mongrel
# <-- добавить mongrel-ы по вкусу
}

server {
listen 8084; # <-- номер порта
server_name ~^(?<subdomain>[-\w]+)\.lvh\.me$; # <-- Запоминаем subdomain
access_log off;

location / {
proxy_pass http://mongrel;
proxy_set_header Host $subdomain.lvh.me:8084; # <-- Передаём subdomain и port
}

location ~ ^/(image|stylesheet|javascript)s {
root /home/user_name/devel/rails/project_name/public;
}
}

Отдельный пункт в первой задаче был - сохранить для использования в рельсовских контроллерах
субдомен, пришедший в запросе. Что и было реализовано при помощи server_name с регулярным выражением
и proxy_set_header с использованием пришедшего из регэкспа поля $subdomain. Без передачи параметра
Host в запросе к приложению приходил параметр “mongrel” с никаким субдоменом, а без передачи порта
возникали проблемы с открытием следующих страниц (редиректы, ссылки и т.п.)

Теперь заходим на organization_name.lvh.me:8084 и в приложение приходят запросы с указанным субдоменом
и разбрасываются между монгрелами. Как-то так.

Capistrano, а потом passenger

На самом деле, мне просто хотелось лёгкого деплоя. И поэтому я решил что на другую машину я буду
деплоить при помощи capistrano. А passenger всплыл уже потом.

Начинаем, как водится, с нуля:

$ cd /home/user_name/devel/rails/project_name
$ capify .
$ emacs -nw config/deploy.rb # <-- Вместо emacs используйте Ваш любимый редактор ;)

Полученный Capfile оставляем в покое, а вот с config/deploy.rb пришлось потанцевать,
по целому ряду причин. Во-первых, мне категорически не хотелось использовать sudo
при деплое, во-вторых, мне не хотелось вводить пароли при каждом деплое, в третьих,
на удалённой машинке rvm был установлен из-под root-а.

Итак, берём deploy.rb и внимательно на него смотрим.

config/deploy.rb
### Где всё будет происходить
#
role :web, "195.151.207.37" # Машинка, куда всё будет деплоиться
role :app, "195.151.207.37" # Как правило, то же самое, что :web
role :db, "195.151.207.37", :primary => true # А здесь будет лежать база данных


### Что ставим, куда ставим
# 1. Имя приложения
set :application, "your_application_name"
# 2. Куда ставим
set :deploy_to, "/home/user_name/deploy_path"
# 3. Откуда берём исходники
# В нашем случае исходники лежали на bitbucket, но никто не запрещает держать
# их на github или другом хостинге для исходников.
#
set :repository, "ssh://hg@bitbucket.org/account_name/repository_name"
set :scm, :mercurial
# 4. Кем ставим
# Собой, любимым
set :user, "user_name"
set :use_sudo, false
# 5. Форвардим ssh-agent, чтобы удалённая машина знала, что это мы.
# Разумеется, на удалённой машине должен лежать Ваш public key,
# и на bitbucket - тоже
set :ssh_options, {:forward_agent => true}


### Bundler
# 1. bundle install автоматически
require 'bundler/capistrano'
# 2. bundle install в домашний каталог (чтобы не запускать рутом)
set :bundle_flags, "--deployment --quiet --path=/home/user_name/bundle"
# 3. bundle install, но с локальными настройками из $shared_path
before "bundle:install", "bundler:config_symlink"
after "deploy:setup", "bundler:config_setup"

namespace :bundler do
# Создаём каталог, куда будут сохраняться локальные настройки bundler-а
#
task :config_setup do
run "mkdir -p #{shared_path}/bundler_config"
end

# Создаём линк на каталог, чтобы bundler не создавал ./.bundler по-новой
#
task :config_symlink do
run "cd #{release_path} && ln -s #{shared_path}/bundler_config .bundle"
end
end


### Копируем данные, лежащие в shared/config в свой config
# config/database.yml, например
#
# Не забудьте предварительно создать сам каталог и всё, что в нём должно лежать!
#
after "deploy:update_code", "deploy:copy_config"

namespace :deploy do
### А этот код достаточно просто раскомментировать
task :start do ; end
task :stop do ; end
task :restart, :roles => :app, :except => { :no_release => true } do
run "touch #{File.join(current_path,'tmp','restart.txt')}"
end

task :copy_config do
run "cp #{shared_path}/config/* #{release_path}/config/"
end
end

Примерно так стал выглядеть deploy.rb на моей машинке (если комментарии не считать).
В процессе мне пришлось разбираться, как настроить ssh-forwarding, как запускать
на удалённой машине ssh-agent и как запускать passenger в standalone режиме
и что man-ы и официальная документация are the developer’s best friends.

И всё было почти хорошо. И деплой проходил, не заканчиваясь сообщением, что какая-то команда
не прошла, но вот рестарт passenger-а не происходил. Вот если бы passenger-а можно было бы
запускать с командной строки, указывая, в каком каталоге будет производиться запуск, тогда бы,
возможно, следующую часть можно было бы и опустить. А так мне пришлось устанавливать passenger
в конкретном gemset-е, в нём же запускать установку nginx-модуля для passenger и настраивать
nginx уже на работу с /home/user_name/deploy_path/public.

$ sudo rvm 1.9.2@project gem install passenger
$ sudo rvm 1.9.2@project passenger-install-nginx-module
$ sudo rvm 1.9.2@project passenger-config --root # <-- Отсюда возьмём путь к passenger-у
$ sudo vi /opt/nginx/conf/nginx.conf # <-- Да, иногда я пользуюсь vi. Особенно на удалённых машинах ;)

Дальше - правки, внесенные в /opt/nginx/conf/nginx.conf

http {
# Сюда пишем то, что выдала команда passenger-config --root
passenger_root /usr/local/rvm/gems/ruby-1.9.2-p290@crm/gems/passenger-3.0.9;
# Эту строчку выставляет passenger-install-nginx-module (а откуда ещё я мог её взять?)
passenger_ruby /usr/local/rvm/wrappers/ruby-1.9.2-p290@crm/ruby;
...
# А вот этот кусочек я написал, глядя в руководство
server {
listen 80;
server_name _;
root /home/user_name/deploy_path/current/public;
passenger_enabled on;
}
...
}

Перезапускаем nginx и идём пить кефир.

Список используемой литературы