Самый простой deploy приложения на Ruby on Rails

  • Tutorial

Полгода назад я написал пост Deploy приложения на RoR 4 с помощью Capistrano 3. Прошло время, я получил много положительных отзывов, но были и отрицательные. Из них можно было понять следующее:
  • Инструкция слишком сложная для новичка
  • Очень много всего приходится делать «руками»

Я подумал и написал gem 'capistrano3-ubuntu-server-config', который полностью настраивает Ваш «чистый» Ubuntu сервер. Всё, что Вам нужно сделать руками — создать нового пользователя и дать ему права visudo (причем давать ему права на passwordless sudo ему не надо). Он может:
  • Настроить SSH (Добавить настройки 'PermitRootLogin no', 'UseDNS no', 'AllowUsers username')
  • Создать и настроить swap (размер запрашивается)
  • Сделать
    sudo apt-get update
    и
    sudo apt-get upgrade

  • Установить из исходников и настроить как чистый Nginx, так и с модулем Pagespeed
  • Установить PostgreSQL из репозитория, затем создать суперпользователя БД (имя пользователя и пароль запрашиваются)
  • Установить из исходников и настроить Redis
  • Установить RVM с последней версией Ruby и gem'ами Rails, Bundler
  • Скопировать Ваш приватный ssh ключ (например для доступа к приватному git репозиторию) с локальной машины на сервер и добавить его в ~/.ssh/config
  • Установить imagemagick из репозитория (Необходим для Paperclip, постоянно его забываю ставить)
  • Установить любые дополнительные пакеты из репозитория (Запрашивает какие именно)

Можно запустить конфигурационный wizard, который узнает, что именно из вышеперечисленного необходимо сделать и заранее спросит все настройки, чтобы можно было потом пойти попить кофе, а можно запустить отдельные таски. Данный gem будет полезен не только Rails разработчикам, а всем, кто использует Capistrano для деплоя.

Эта статья раскроет следующие темы:


gem 'capistrano3-ubuntu-server-config'

Что умеет делать этот gem, я уже рассказал. Перейдем непосредственно к работе с ним. Представим, что у нас чистый веб-сервер на Ubuntu (я тестировал на Ubuntu 14.04). Нам необходимо самим выполнить всего лишь две простые вещи: создать нового пользователя с правами sudo и обеспечить беспарольный вход с Вашей локальной машины на сервер по SSH. Начнем с первого, на сервере, залогинившись под root выполняем:
adduser deployer 
echo "deployer ALL=(ALL) ALL" >> /etc/sudoers

Вместо deployer может быть любое имя пользователя. Непомешало бы еще поменять пароль пользователя root коммандой passwd.

Теперь обеспечим беспарольный вход с локальной машины на сервер по ssh. Для этого на локальной машине выполним (где depoyer — имя пользователя, 111.111.111.111 — адрес сервера):
ssh-copy-id deployer@111.111.111.111

На этом вся настройка сервера завершена. В идеальной ситуации, Вам больше не придется заходить по ssh на сервер. Для просмотра логов я рекомендую gem 'tail'.
Приступим к использованию моего gem'a. В Gemfile добавяляем:
group :development do 
    gem 'capistrano' 
    gem 'capistrano3-ubuntu-server-prepare' 
end

Выполняем bundle install, cap install, добавляем строчку
require 'capistrano3/ubuntu-server-prepare'
в Capfile.

Практически все готово к работе. За исключением одного: для настройки Nginx и Redis мой скрипт берет .conf файлы из папок config/production/nginx и config/production/redis. Чтобы быстро скопировать мои конфигурационный файлы в эти папки, просто выполните:
rake ubuntu_server_prepare:copy_config

Бонусом также получаете настроенный конфиг Unicorn. В папке nginx лежат два файла: nginx.conf и nginx_with_pagespeed.conf. Второй используется при выборе установки pagespeed в конфигураторе.

Внимание! мой конфигурационный файл Nginx и Unicorn! настроен на Rails приложение, которое находится в '/var/www/application/current'. Измените все пути в этих файлах или просто добавьте строчку
set :application, 'application'
в Ваш deploy.rb для деплоя в эту папку.

В 'config/deploy/production.rb' необходимо прописать Ваш сервер, а также проследить, чтобы в Capfile строчка
require 'capistrano/rvm'
была закомментирована.

Теперь приступаем к самому вкусному:
cap production ubuntu_server_prepare

Конфигуратор задаст Вам много вопросов, получив ответы на которые, займется настройкой сервера. Этот процесс в меру долгий, так что можно пойти попить кофе.

Есть возможность запускать отдельные таски, например, выполнив
cap production ubuntu_server_prepare:nginx_conf
вы скопируете конфигурационный файл nginx.conf на сервер и перезагрузите nginx. Это удобно, чтобы быстро менять конфиг: поменяли что-то прямо в папке проекта и отправили на сервер одной командой.

gem 'capistrano3-git-push'

Маленький таск для Capistrano 3, выполняющий
git add -A
git commit -m "#{сообщение}"
git push
только в случае, если есть изменения. Если ввести «skip» в поле для запроса сообщения о коммите, то ничего не будет выполнено, что удобно, когда надо сделать deploy, но заливать в репозиторий изменения не надо.
Подключить проще простого. В Gemfile:
group :development do
	gem 'capistrano3-git-push'
end

В Capfile:
require 'capistrano3/git-push'

В deploy.rb:
before :deploy, 'git:push'


Моя текущая конфигурация Capistrano

Если вспомнить мою предыдущую статью, то мой deploy.rb был просто огромен. Теперь же моя конфигурация проста до безумия.
Gemfile
group :development do
	gem 'capistrano'
	gem 'capistrano-rails'
	gem 'capistrano-bundler'
	gem 'capistrano3-unicorn'
	gem 'capistrano-rvm'
	gem 'capistrano3-ubuntu-server-prepare'
	gem 'capistrano3-delayed-job'
end

group :production do
	gem 'unicorn'
end

Capfile
# Load DSL and set up stages
require 'capistrano/setup'
require 'capistrano/deploy'
require 'capistrano3/ubuntu-server-prepare'
require 'capistrano3/unicorn'
require 'capistrano3/git-push'
require 'capistrano/rvm'
require 'capistrano/bundler'
require 'capistrano/rails'

deploy.rb

set :application, 'application'
set :repo_url, "#{ВАШ_АДРЕС_РЕПО}"
set :unicorn_config_path, "#{current_path}/config/production/unicorn/unicorn.rb"
set :linked_dirs, fetch(:linked_dirs, []).push('bin', 'log', 'tmp/pids', 'tmp/cache', 'tmp/sockets', 'vendor/bundle', 'public/system') # Строчка есть по умолчанию в deploy.rb, ее просто надо откомментировать
namespace :deploy do
  task :setup do
    before "deploy:migrate", :create_db
    invoke :deploy
  end
  task :create_db do
    on roles(:all) do
      within release_path do
        with rails_env: fetch(:rails_env) do
          execute :rake, "db:create"
        end
      end
    end
  end
  task :restart do
    invoke 'unicorn:legacy_restart'
  end
end
before :deploy, 'git:push'
before 'deploy:setup', 'git:push'


То есть сначала надо выполнить все то, что я описывал в начале статьи, затем один раз выполнить
cap production deploy:setup
для создания базы данных. Все последующие разы выполняем
cap production deploy


Стоит наверное объяснить, что тут происходит.
Что тут происходит?
set :unicorn_config_path, "#{current_path}/config/production/unicorn/unicorn.rb"
Задает местоположения конфига unicorn для gem'а 'capistrano3-unicorn'.

set :linked_dirs, fetch(:linked_dirs, []).push('bin', 'log', 'tmp/pids', 'tmp/cache', 'tmp/sockets', 'vendor/bundle', 'public/system')
Создает симлинки на указанные папки из папки current в папку shared.

task :setup do
    before "deploy:migrate", :create_db
    invoke :deploy
  end
Вызывает таск :create_db, перед выполнением 'db:migrate' при первом деплое (deploy:setup).

task :create_db do
    on roles(:all) do
      within release_path do
        with rails_env: fetch(:rails_env) do
          execute :rake, "db:create"
        end
      end
    end
  end
Тот самый таск :create_db, который вызывает 'rake db:create' при первом деплое.

 task :restart do
    invoke 'unicorn:legacy_restart'
  end
перезапускаем unicorn при каждом деплое.


P.S. Если используете resque, стоит посмотреть на gem 'capistrano-resque', если используете delayedjob, стоит посмотреть на gem 'capistrano3-delayed-job'.
Share post
AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 34

    +7
    Ничего себе, самый простой! А, согласно этой идеологии chef или ansible считается монструозным и сложным, что ли?
    Или в данном случае «простотой» выступает количество различных инструментов? Тогда почему же не переписать все на bash?

    В общем, развертывание серверов через капистрано имеет место на жизнь, но никак не может называться «самым простым способом».
      0
      Вы мою предыдущую статью еще не видели, вот там было намного сложнее.
      Да и на самом деле, если убрать весь текст, то весь способ заключается в добавлении нескольких строчек в Gemfile, нескольких в Capfile, немного кода в deploy.rb и запуском трех команд. Не так уж и страшно вроде.
        0
        Я вот чем больше смотрю — тем больше понимаю, что технология скорее мертвая, чем живая. Насколько я вижу — сейчас по сути два полюса сражаются: один — generic deploy (через сборку пакетов ОС, как правило каким-то generic configuration management tool — типа того же chef / puppet / ansible), другой — абстрагирование по максимуму от инфраструктуры и задач ее поддержки и deploy полным автоматом через git push (всякие heroku, dokku и т.д.).

        Даже несмотря на вашу громадную работу (ни разу не шучу, я вполне представляю, чего стоит автоматизация создания такой здоровой инфраструктуры — и, судя по всему, у вас эту задачу получилось решить), я боюсь, что Capistrano с «несколько строчек сюда, потом несколько туда, тут подкрутить, там поставить» все равно будет проигрывать по простоте «развернул контейнер(ы) через веб-интерфейс + git push => все работает».
          0
          То есть вы считает, что пилить и гонять новые контейнеры по нескольку раз на день будет проще?
          Сомневаюсь.

          Все зависит от того, как построен цикл разработки и какие требования к деплою.
            0
            В смысле «пилить и гонять»? Контейнер один раз на приложение создается. Вы создаете несколько приложений в день? Я вот лично что-то типа 2-3 в год в лучшем случае.
              0
              Я о том, что деплой — это не только апдейт кода.

              Простой сценарий деплоя:
              — переводим приложение в сервисный режим
              — делаем бекап БД
              — обновляем код
              — заливаем необходимые конфиги (надеюсь вы конфиги не храните в vcs)
              — запускаем билд/миграции
              — переводим приложение в рабочий режим или откатываем предыдущую версию если что-то пошло не так

              Вот чем и как здесь помогут контейнеры?
                0
                Вы привели не «простой», а скорее «умеренно средний» сценарий. Простой (который вполне годится для stage как есть) скорее выглядит как-то так:

                — остановить приложение
                — сделать бэкап БД
                — обновить и собрать код, подсунув конфигурацию
                — запустить миграции
                — запустить приложение обратно или откатить

                Такой минимальный сценарий реализуется как раз системами типа heroku одной командой без каких-либо особенных дополнительных телодвижений. Конфигурация, опять же, тривиальна — один контейнер с приложением, одна база данных, одно к другому присоединяется либо кликом в веб-интерфейсе либо еще одной командой.

                По мере усложнения схемы деплоймента и перехода в production, разумеется, что-то придется доделывать вручную и что-то будет изменяться. Тривиальный вариант «сервисного режима» — показывать заглушку-статику — тоже получится более-менее автоматически на heroku-подобных системах. Введение полноценного режима read-only потребует уже кое-каких усилий по модификации собственно самого приложения, и, да, придется вписать в соответствующие строчки а ля «heroku features:enable -a myapp readonly» и «heroku features:disable -a myapp readonly» в процедуру деплоймента.

                Я совершенно верю, что по мере роста через какое-то время процедура станет весьма сложной — серверов станет несколько тысяч, захочется zero downtime deployment, БД станет такой, что забэкапить ее нереально — в итоге потребуются выделенные deployment engineers, которые будут ее проводить. Да, в конечном счете уже станет по сути без разницы, чем именно деплоить, т.к. 90-95% всего, относящегося к деплойменту все равно будет самописное, с учетом специфики приложения, типа и характера данных, ролей в кластерах и т.д. и т.п. Кроме того, скорее всего в какой-то момент облачные PaaS / IaaS сервисы типа heroku или даже amazon станут слишком дороги и целесообразно будет завести свои сервера / свой датацентр и штат обслуживающих все это людей.
                  0
                  Я примерно об этом и говорю.
                  Ситуации разные бывают, поэтому нельзя однозначно сказать, что вот это проще или лучше этого.
                  А судя по коментам, для многих деплой — это только апдейт кода.
                    0
                    Да я, собственно, о том, что есть «простые» сценарии и есть «сложные» сценарии.

                    Для простых — нишу потихоньку занимают инструменты типа heroku.
                    Для сложные — очевидно, команда devops-инженеров, которые будут использовать либо ту же существующую инфраструктуру, какие-то более общие configuration management инструменты типа chef / ansible / puppet / cfengine и т.п.

                    И Rails-специфичные инструменты типа Capistrano (равно как и любые другие инструменты, сурово привязанные к фреймворку — Mina, Moonshine, Rocketeer, Grunt, Fabric / Fabistrano и т.д.), в свете того, что попали между этих двух полюсов, стабильно теряют свою долю.
                      0
                      Эмм, а в чем выражается Rails-специфика или суровая привязанность к фреймворку Capistrano/Mina?
                      Мне и колегам, тот факт, что они написаны на руби, совсем не мешает деплоить кроме рельсов и ноду с пхп. И уверен, что с любым другим стеком сложностей бы также не возникло.
                      Честно, не спора ради, а интереса для.
                        +2
                        Да ситуация там примерно такая же самое, что и Puppet / Chef. Аргументация из серии «not invented here», «нам неудобно писать конфиги на этом вашем ruby», «зачем мне в системе еще один лишний язык и занятые 10-20-50-100 мегабайт места» и т.д.

                        Рациональных отличий никаких, на самом деле — примерно как и в любом подобном же споре Capistrano vs все остальные. Ну, всяких рецептов для Capistrano / Rails будет априори больше, чем Capistrano / Django. А в свою очередь, у Grunt, наверное, готовых рецептов по деплою node.js будет больше, чем у Capistrano / node.js. Но при этом если речь о том, что надо относительно небольшой командой поддерживать дикий зоопарк разнообразных инсталляций на разных фреймворках — все равно будет тенденция к унификации — и в этом месте, опять же, ангажированность Capistrano с одним из фреймворков будет скорее играть против признания его «золотым стандартом».

                        Реально — посмотрите просто на альтернативы Capistrano — до смешного же доходит. Я пока ни одной не видел, которая бы давала четкий ответ на вопрос «чем оно лучше», у всех детский лепет на тему «а мы переписали это на нашем языке» или «мы сделали больше magic / меньше писать руками» или, наоборот «меньше magic / больше писать руками».
                          0
                          Мерси.
                  0
                  Ну вы всё это сделаете на своём компе разработчика, а потом замените контейнер и на продакшне всё заработает как часы без простоя
                    0
                    На стейджинг- и qa-сервера ручками заливать/лазить?
                    Спасибо, не надо.
          0
          Люди, приготовьтесь к тому что это очень очень простой конфиг и если у вас приложение сложнее, чем уроки с блогом из рельсовых гайдов, то ваш deploy.rb и тп будет сильно больше. С добавлением в проект delayed_job, resque, faye и прочих сервисов, что надо запускать и мониторить.
          А chef это уже потом, когда серверов станет много и появится необходимость поднимать много машин автоматом с готовыми nginx/postgres/etc, его нет смысла особенного использовать только для деплоя приложений. И он не проще в этом смысле.

          Ну а кому надо проще «как php» — есть heroku, но это не для бедных развлечение, особенно сейчас, так что лучше учиться самим и понимать, как все устроено.
            0
            Для бедных есть dokku. Только я бы не стал говорить, что это «как php» — более-менее сложные php-приложения сравнимо разворачиваются.
          0
          Mina все ж лучше чем capistrano, деплои более быстрые получаются.
            0
            Вот сейчас прямо засек. У меня, если нет новых gem'ов в Gemfile, деплоится за 26 секунд. Вроде не так уж и медленно. Хотя Mina в чем-то выглядит интересней.
              0
              Mina выигрывает, если у вас деплой сложнее «один раз подключился и залил файлы».
                +1
                Mina хороша только в случае если у вас только один сервер, на который деплоится приложение. Чуть больше 1 машины — всё, Mina уже непригодна, либо требует слишком много плясок. С капой же будь у тебя 1, 2, 10, N серверов — добавил IP в массив и счастье. Именно поэтому большинство и пользуется капой — не потому что у всех подряд по 2 и более серверов для деплоя, а потому что если у тебя вдруг появится ещё один сервер, или проект с несколькими серверами — не придется менять deploy tool.
                А так да, я mina тоже люблю и уважаю. :)
              +1
              К сожалению, mina менее популярна, и как следствие, под неё нет того же обилия готовых recipes как под capistrano.
              –2
              Самый простой способ — это, когда код писать не надо. Ruby и RVM можно через Deploy4Me установить, вообще без кода (https://deploy4me.com/en/deployments/ruby.html). 2 минуты в визуальном редакторе и готово.
              Сервис сам настроит SSH, безопасность и Nginx по желанию.
                +1
                Если пользоваться подобными сервисами — никогда не научишься что то самостоятельно делать. Да и нет нечего сложного, всё как и с изучением обычного языка — в начале не понимаешь. Но потом чувствуешь себя очень круто.
                –2
                Все варианты деплоя приложений на руби такие монструозные?
                Я объясню: если даже не рассматривать php (слишком просто), то можно рассмотреть такие варианты деплоя проектов на чистый сервер:
                1. Django. Установка питона (если его внезапно нет), дальше pip: поставить фреймворк, поставить uwsgi. Осталось сочинить конфиг nginx (или спереть из гугла) и все.
                2. Perl. Perl, как правило, уже есть. Нужно установить, например, Mojo, у которого внутри уже есть веб-сервер. Осталось его запустить одной командой и сочинить конфиг nginx.

                Затем можно просто чекаутить репозиторий на продакшен-сервере. Ити я чего-то не понимаю в руби и все на самом деле просто?
                  +2
                  Деплой — это не только чекаут кода из репозитория.
                    0
                    Все варианты деплоя приложений на руби такие монструозные?


                    Реальные — да, минимальные — нет. Как и в случае с Python, Perl, Node.js, да, в общем-то, и с PHP тоже.
                      0
                      Здесь тоже есть простой сценарий. nginx + phusion passenger и все завелось. Но это голый минимум для запуска приложения, без различных сервисных задач. Впрочем, и в статье не то чтобы навороты какие-то показаны.
                        +3
                        А теперь автоматизируйте все через fabric и у вас будет практически тоже самое. Боюсь вы просто не в теме автоматизации деплоя.
                        0
                        Мы написали в свое время pushapp gem, пользуемся до сих пор и рады, хотя есть мысли перейти на docker.
                          +1
                          А почему в заголовке RoR? Я успешно использую capistrano 3 для деплоя и сборки symfony 2 приложения. Также пытался использовать огрызок capifony, но с ней оказалось много проблем.
                            0
                            Я знаю, что Capistrano используется не только с RoR и даже не только Ruby приложениями. Поэтому в начале статьи написано, мол это будет интересно не только RoR разработчикам. Но в конце статьи приведены настройки именно под RoR, поэтому решил назвать именно так, чтобы новичкам было легче найти.
                            0
                            День добрый.
                            Сервер настроен, благодарю. Приложение задеплойжено.
                            Но возникла проблема «We're sorry, but something went wrong.»
                            Ассеты прекп-л, с дб сконектился.
                            Никак не могу понять в чем проблема, уже несколько дней бьюсь.
                              0
                              вот чего откопал в var/www/log/nginx.error.log, содержимое следующее (повторяется)
                              2015/03/04 07:35:59 [crit] 279#0: *12 connect() to unix:/var/www/application/current/tmp/sockets/.unicorn.sock failed (2: No such file or directory) while connecting to upstream, client: 94.230.122.71, server: _, request: «GET / HTTP/1.1», upstream: «unix:/var/www/application/current/tmp/socke...», host: "...."
                                0
                                Проблема в том, что nginx не может найти .sock файл unicorn'a.
                                Проверьте, что:
                                — приложение присутсвует по адресу /var/www/application/current/
                                — unicorn запущен (ps aux | grep unicorn)

                                P.S. рекомендую этот gem для запуска unicorn при каждом деплое

                            Only users with full accounts can post comments. Log in, please.