Есть задача, даже две. Первая - разобраться с настройкой nginx для отдачи rails-статики
и балансировки двух (трёх, сколько нужно), mongrel-ов. Вторая - всё то же самое, но без mongrel,
а с использованием passenger и, главное, через capistrano.
Nginx + mongrels
Первую задачу я реализовывал локально, на своей уютненькой убунте и всё решилось сравнительно
мирно.
Убрал из дефолтной конфигурации все директивы server и upstream,
оставил только директивы include - так гибче, если что.
123456
##
# Virtual Host Configs
##
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
Добавил в отдельный файл конфигурацию апстрима на двух монгрелах (важен был сам принцип, а не количество)
и сервер:
1234567891011121314151617181920
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 всплыл уже потом.
Начинаем, как водится, с нуля:
123
$ cd /home/user_name/devel/rails/project_name
$ capify .
$ emacs -nw config/deploy.rb # <-- Вместо emacs используйте Ваш любимый редактор ;)
Полученный Capfile оставляем в покое, а вот с config/deploy.rb пришлось потанцевать,
по целому ряду причин. Во-первых, мне категорически не хотелось использовать sudo
при деплое, во-вторых, мне не хотелось вводить пароли при каждом деплое, в третьих,
на удалённой машинке rvm был установлен из-под root-а.
Итак, берём deploy.rb и внимательно на него смотрим.
### Где всё будет происходить#role:web,"195.151.207.37"# Машинка, куда всё будет деплоитьсяrole:app,"195.151.207.37"# Как правило, то же самое, что :webrole: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_pathbefore"bundle:install","bundler:config_symlink"after"deploy:setup","bundler:config_setup"namespace:bundlerdo# Создаём каталог, куда будут сохраняться локальные настройки bundler-а#task:config_setupdorun"mkdir -p #{shared_path}/bundler_config"end# Создаём линк на каталог, чтобы bundler не создавал ./.bundler по-новой#task:config_symlinkdorun"cd #{release_path} && ln -s #{shared_path}/bundler_config .bundle"endend### Копируем данные, лежащие в shared/config в свой config# config/database.yml, например## Не забудьте предварительно создать сам каталог и всё, что в нём должно лежать!#after"deploy:update_code","deploy:copy_config"namespace:deploydo### А этот код достаточно просто раскомментироватьtask:startdo;endtask:stopdo;endtask:restart,:roles=>:app,:except=>{:no_release=>true}dorun"touch #{File.join(current_path,'tmp','restart.txt')}"endtask:copy_configdorun"cp #{shared_path}/config/* #{release_path}/config/"endend
Примерно так стал выглядеть 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.
1234
$ 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
123456789101112131415
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;
}
...
}