Стратегии деплоймента в Capistrano

Пару месяцев назад я стал интересоваться Capistrano, но не смог найти интересующую меня информацию. Попадались либо обрывки каких-то вопросов, либо довольно старые статьи о Capistrano. Начиная с этого поста я собираюсь выкладывать интересную и я надеюсь полезную для многих информацию об этом фреймворке для деплоймента приложений под названием Capistrano.

На данный момент доступна версия Capistrano 2.5.0 и которой мы и поговорим.

А начну я со стратегий деплоймента.

* * *

Особенности настройки кеширования на production сервере

Решил я на днях заняться кешированием блога и напоролся на небольшую проблему…

Требовалось обработать JavaScript и CSS файлы (которых меня много, да и весят они немало) и получить на выходе всего лишь два файла. Для этого я использовал способ предоставляемый хелперными методами javascript_include_tag и stylesheet_link_tag, а именно: собрал все .js и .css файлы в два хелперных метода и установил у них опции :cache. Получился примерно следующий код:

<%= javascript_include_tag 'shCore.js', 'shBrushCSharp.js', 'shBrushXml.js', 'shBrushRuby.js', 'shBrushBash.js', 'shBrushPlain.js', :cache => 'sh' %>
<%= stylesheet_link_tag 'application', 'pagination', 'textilize', 'awesomebox', 'SyntaxHighlighter.css', cache => 'general_all' %>

Для того чтобы кеширование заработало в конфиге для production сервера (_config/environments/production.rb_) поле config.action_controller.perform_caching должно быть выставлено в true (на самом деле это значение по умолчанию для этого конфига)

Грабли же заключаются в том, что в этом случае в каталогах public/javascripts и public/stylesheets при первом обращении к серверу создаются файлы объединяющие все указанные вами скрипты и CSS и если у mongrel нет прав на запись в эти каталоги, по возникает исключение:


ActionView::TemplateError (Permission denied - /u/apps/blog4m/releases/20080928165941/public/javascripts/sh.js) on line #10 of layouts/main.html.erb:

10:     <%= javascript_include_tag 'shCore.js', 'shBrushCSharp.js', 'shBrushXml.js', 'shBrushRuby.js', 'shBrushBash.js', 'shBrushPlain.js', :cache => 'sh' %>
11:     <%= stylesheet_link_tag 'application', 'pagination', 'textilize', 'SyntaxHighlighter.css', :cache => 'main_all' %>

А прав у mongrel-а небыло потому что он запускался под специально созданной учётной записью (не под той под которой деплоились на сервер файлы)

Покапавшись в интернете я нашел следующий workaround: надо в deploy.rb добавить следующие задачи которые будут выполняться после создания Сapistrano проекта на сервере и после заливки новой версии на сервер:

task :after_cold, :roles => [:app, :web, :db] do
  sudo "chown -R mongrel:mongrel #{current_path}/"
end

task :after_update, :roles => [:app, :web, :db] do
  sudo "chown -R mongrel:mongrel #{current_path}/"
end

Задачи просто меняют владельца у находящихся на сервере файлов после выполнения задачь deploy:cold и deploy:update

* * *

Особенность работы метода run в Capistrano

Потратил сегодня полтора часа пытаясь заставить работать следующий код:

task :tmp_list, :roles => [:app] do
  run <<-CMD
if [ -d /tmp/ ];
then
  ls /tmp/
fi
CMD
end

и получал одну и ту же ошибку

  * executing `tmp_list'
  * executing "if [ -d /tmp/ ];\\\nthen\\\n  ls /tmp/\\\nfi"
    servers: ["111.111.111.111"]
    [111.111.111.111] executing command
*** [err :: 111.111.111.111] Syntax error: end of file unexpected (expecting "fi")
    command finished
failed: "sh -c \"if [ -d /tmp/ ];\\\\\nthen\\\\\n  ls /tmp/\\\\\nfi\"" on 111.111.111.111

Проблема заключалась в том что sh передаётся команда содержащая символы переноса строки. В man-е освящене этого момента я не нашел.

Обойти эту проблему можно как минимум двумя способами:

1) Избавится от переносов строк. Тогда код будет выглядеть примерно так:

task :tmp_list, :roles => [:app] do
  run "if [ -d /tmp/ ]; then ls /tmp/ ; fi"
end

2) Либо использовать условные операторы

task :tmp_list, :roles => [:app] do
  run "([ -d /tmp/ ] && ls /tmp/) || true"
end

Трюк с true нужен для того чтобы в случае неудачи (если директория не будет найдена) выполнение набора комманд завершилось с кодом ошибки равным нулю (что означает что проблем при выполнении не возникло)

* * *

Жонглируем файлами в Capistrano

Только б не дисконнект!!!! Только б не дисконнект!!!

Жонглировать файлами в Capistrano просто :), потому как в одной из последних версии Capistrano появились два замечательных метода upload и download которые позволяют закачивать файлы на сервер и скачивать их с него соответственно.

download <путь к файлу на сервере>, 
         <путь на локальной машине куда будет скопирован файл>, <опции>

upload <путь на локальной машине откуда будет скопирован файл>, 
         <путь к файлу на сервере>, <опции>

С первыми двумя аргументами думаю всё понятно. Третий аргумент принимает хеш, одним из значений которого может быть ключ :via указывающий какой клиент будет использован для передачи файлов (SCP или SFTP который используется по умолчанию)

Метод upload в качестве опции так же принимает ключ :mode и в случае его наличия вызывает поле окончания процесса загрузки команду chmod передав её значение указанное в ключе (например :mode => 755)

Пример:

task :get_production_log, :role => [:app] do
  download "#{current_path}/log/production.log", 'tmp/production.log', :via => :scp
end
* * *

Список конфигурационных параметров в Capistrano

Хасо - это всего лишь одно из состояний системы

Хоть список и не полный (да и наверное ошибок и неточностей в переводе я немало наделал), но думаю многим пользователям Capistrano он будет полезен т.к. к сожалению более побробную информацию по параметрам используемым в Capistrano достать невозможно (разве что потратив уйму времени на анализ исходников)

* * *

Полезные задачи для Capistrano (получение логов)

Довольно часто возникает желание посмотреть логи работы приложения, приходиться подключаться к серверу, заходить в каталог с приложением, и смотреть логи в маленьком терминальном окне. Мне не очень хочется это делать, поэтому я написал следующую задачу для Capistrano:

task :get_logs, :role => [:app] do
  name = "blog_logs.zip"
  logs_zip = "/tmp/#{name}"

  run "rm -f #{logs_zip}"
  run "cd #{current_path}/log/ && zip -r -9 #{logs_zip} **"
  download "#{logs_zip}", "tmp/#{name}"
  run "rm -f #{logs_zip}"
end

Задача просто упаковывает все логи Rails приложения в архив и передаёт их на локальный компьютер. Для небольших логов это вполне приемлемо, а вот когда размер production.log превысит с десяток мегабайт, то думаю настанет время заняться оптимизацией (например передавать только часть production.log обрезая его с помощью команды tail)

На самом деле некоторые шаги по оптимизации размера production.log можно проделать уже сейчас, например, исключить с помощью фильтра filter_parameter_logging который находиться в application.rb те поля которые содержат большие текстовые данные.

P.S.

Если у вас есть свои решения этой задачи, или другие задачи которые могут быть полезны пользователям, то присылайте их на мой e-mail указанный внизу страницы. Я буду публиковать их по мере поступления.

* * *

Полезные задачи для Capistrano (real-time лог)

Ноги, крылья... главное - хвост!!!

Продолжу тему с логами начатую в предыдущем посте. Иногда закачивать весь лог не нужно, а нужно всего лишь посмотреть последние записи которые в него поступают (желательно видеть ход заполнения лога в реальном времени).

* * *

Полезные задачи для Capistrano (управляем nginx сервером)

Вкл. / Выкл.

Конечно каждый из вас слышал о потрясающем веб-сервере под названием nginx, который создал наш Российский разработчик Игорь Сысоев. К достоинствам nginx-сервера можно отнести: высокую скорость работы, надёжность, удобство настройки и огромное количество разнообразных плагинов.

В Capistrano есть стандартные задачи которые позволяют управлять кластером из Mongrel-серверов на которых работает ваше Ruby on Rails приложение, а вот для управления nginx-сервером задач пока нет, поэтому я попробую восполнить этот пробел.

* * *

Полезные задачи для Capistrano (работаем с конфигами nginx сервера)

[ Нет описания ]

В предыдущем посте я рассказал про то, как запускать, останавливать и перезапускать nginx используя Capistrano. Попробуем сделать с nginx-сервером что-нибудь посложнее.

Не знаю как вам, но мне больше всего нравиться редактировать какие-либо файлы (в особенности конфигурационные) на локальной машине, а не в маленьком окне терминала с открытым в нём vi :) Вот для этих целей я и написал пару задач которые позволяют решить эту небольшую проблему.

* * *