RoadMovie

write down memos or something I found about tech things

Elixir/Phoenixで作ったアプリの簡単なデプロイ方法 by ansible

f:id:mr7myself:20190127105617p:plain

こんにちは! Ruby/Railsプログラマーの方がElixir/Phoenixにチャレンジしていると、「Capistranoみたいに簡単にデプロイする方法ないの?」と思うかもしれません。

私が調べたところ、完璧にCapistranoの代替になるようなライブラリはなさそうでした。そこで下記の2つの方法を試してみようと思ったのですが、

  • Deployment via docker
  • Deployment via Ansible

docker管理は慣れているとそれほどですが、プライベートでやるにはtoo much workかなと思い今回は避けました。なので、今回ご紹介する方法はansibleによるデプロイ方法です。これはとても簡単なのでぜひ試してみてください。

なぜ簡単なのか。それはAnsistranoというライブラリがほとんどCapistranoのような振る舞いをしてくれるからです。

Ansible role to deploy scripting applications like PHP, Python, Ruby, etc. in a capistrano style

そう、ここではElixir/Phoenixをあげましたが、それらでなくても問題なくデプロイできます。 今回はPhoenixのデプロイプロセスをこのライブラリに当てはめていきます。

参考までに私が作ったレポジトリになります。

ansible-for-phoenix


使い方

1. 環境設定
書き換える必要がある箇所を"TODO"としていますので、grepしてみてください。

2. サーバを用意

$ ansible-playbook -i production centos.yml -u root

3. Deploy app

$ ansible-playbook -i production deploy.yml -u deploy

基本的にはこれだけになります!細かいところは先程あげたレポジトリに譲りますが、それほど詰まることなくデプロイまで行けると思います。

ansible-for-phoenix

中規模Railsアプリのアーキテクチャ設計

Railsでアプリを作っていますか?設計に問題を抱えている、あるいは悩んでいませんか?もしそうであればこの記事が役に立つかもしれません。

アプリケーションが大きくなっていくに連れて、コードがカオスになってきたり、どこで何が起こっているのか追いにくくなってくるものです。いわゆる「ファットコントローラ」「ファットモデル」と呼ばれる問題ですね。ところで、どうしてこの問題が起こるのでしょうか。それはMVCアーキテクチャが常に完璧ではないからです。Railsはアプリケーションの立ち上げに関しては本当に簡単な方法を提供してくれています。素晴らしいです。単にMVCと呼ばれる構造に従ってコードを書いていけばアプリケーションを書けるようになるのです。ただ、前述のように、どこかのタイミングで設計の変更を余儀なくされていきます。もうあなたはそのフェーズにいるかも知れません。どうやって何を変更したら良いのでしょう。

ここでは、ひとつの解決方法として、中規模サイズのRailsアプリケーションにおけるストラテジーを紹介します。ここでの方法はDDD(Domein Driven Design)と呼ばれる手法をベースにしています。記事の最後に本を紹介しているので興味があればぜひ手にとってみてください。


Environment

f:id:mr7myself:20190126174244j:plain
設計図



コントローラ層

コントローラ層はすべてのリクエストを受け付けます。基本的に、この層のみがドメイン層を呼べます。この層は可能な限りシンプルに保たれるべきです。

  def save
    @request.assign_attributes(assignable_request_params)
    if @request.save
      RequestFlow::ReceiveFromAPI.execute(@request, @request_token)
      render json: { result: 'success' }
    else
      render json: { result: 'failed' }
    end
  end

ここでの RequestFlow::ReceiveFromApiドメインです。

ドメイン

これは最も重要な層のひとつです。ドメインは技術者のみでなく、ビジネスやカスタマーサポートの人にも理解可能な共通言語で名前付けしてください。このタイミングでみんなで話し合うのもいいですね。例えば私は SelectXXX, SendXXX, CreateXXX のようにつけることが多いです。ここでEmailなどの通知のようなフックイベントを定義します。理想的にはドメインはプロセス(手順)であったほうがよいです。

module RequestFlow
  module Update

    module_function

    def execute(request, options={})
        GeocodeJob.perform_later(request) unless request.geocoded?
        Pipedrive::Updator.update_parameters(
          PipedriveStage.hash[request.status],
          request.pipedrive_deal,
          request
        )
        request.update_status
      end

      unless options[:skip_notification] == true
        # This is a Service Layer
        Notifier::Request.complete_updating(request_offer)
      end
    end
  end
end 

ドメインはモデルとサービスを呼ぶことができます。しかし彼らから"呼ばれる"のは禁止です。 ドメインは中で何が起こっているのか理解しやすいようにしましょう。そうすると、このような会話が生まれることが想定されます。

CS: ユーザーがリクエストを更新したとき、何がおきるんだっけ? IT: ドメインをチェックしてみるね。そのリクエストは位置情報を付与されて、pipedriveに情報を投げているね。その後アップデート完了という流れだね。

いい感じですね。

サービス層

ここはステートレスな層です。サービスはコマンド(命令)のようなものだと思ってください。例えば、geocode_setter, locale_finder, distance_calculator などです。私の経験では Notifier (通知者)をここに置くのは良いと思います。

  class Notifier::Request
    def initialize(request, timing = :wait, skip_validation = false)
    end

    ...

    def complete_request
      klass = new(request)
      klass.notify_internaly
      klass.via_sms
      klass.via_email
    end

    def via_email
      # call ActionMailer
    end
  end

モデル層

サービス層とドメイン層のおかげで、モデル層は薄くできるようになったはずです。モデルは自身が何を表しているかだけに関心を持ちます。純粋なRubyクラスもどんどん作りましょう。ActiveRecord::Base を継承してることは必須ではないですからね。pureなRubyクラスは綺麗なデザイン、理解しやすい設計に大いに役立つはずです。

バリデーション層

この層もモデル層を薄く保つのに役立ちます。もしRailsにおいてバリデーション層をどう実装していいか知らなければ検索してみてください。実装方法は簡単で、ActiveModel::Validator を継承するだけです。 それから validates_with などを実装しましょう。当然ですが、ここではバリデーションのみに関心を持ちましょう。



これが基本的な設計の考え方にになります。もちろん、実際のプロダクトではもっと複雑になるでしょうけど、いままで私が関わったプロダクトではこのやり方はスケーラビリティにおいてもうまくいきました。この設計で重要なのは「誰が誰を呼べるか」の「方向(矢印)」です。これが正しく守られていないと、またカオスに逆戻りです。

もしもっとこの設計の理解を深めたければ下記の本がおすすめです。

それでは、Enjoy coding!

redux-formでwizard実装

最近onBoardingページにReact.jsをredux, redux-formと使い始めました。 今回はその際に私が躓いた3つのポイントを紹介します。

  1. デフォルト値の設定方法
  2. ラジオボタンを使ったラベル選択方法
  3. ユーザーが次のフォームに移った時にどうやって自動的に値を設定するか

ちなみに開発環境は下記になります

  • "react": "15.4.2"
  • "redux": "3.6.0"
  • "redux-form": "6.4.3"

デフォルト値の設定方法

もしデフォルトのフォームを使っているのであれば、単純にinitialValueを使って実現できます。

const FirstForm = (props) -> (
  <div>
    ...
  </div>
)

export default reduxForm({
  form: "wizard",
  initialValue: { name: "My Name" }
})

しかし、redux-formのwizardを利用している場合、少し厄介なことになります。wizard formでは、複数個のformが出現しますが、値をイニシャライズするタイミングが最初の一度しかないからです。各フォームが出現するたびに何度もイニシャライズを走らせ直して値を再設定する方法もありますが、少し気持ち悪いですよね。結果として、単純ですが、各Componentの初期化タイミングで設定するとうまく行きます。具体的には componentWillMount() で設定してます。

実装はこんな感じです。

class FirstForm extends React.Component {
  componentWillMount() {
    this.props.change("form-name", "value");
  }
}

export default reduxForm({
  form: 'wizard',                 // <------ same form name
  destroyOnUnmount: false,        // <------ preserve form data
  forceUnregisterOnUnmount: true,  // <------ unregister fields on unmount
  validate
})(FirstForm);

ラジオボタンを使ったラベル選択方法

ラジオボタンをカスタマイズしたい時によくぶつかる問題です。これは単純に下記のように実装できます。

const setSelected = element => {
  document.getElementById(element.currentTarget.htmlFor).checked = true
  element.currentTarget.className = "selected"

  return false
}

export default setSelected;

そして、onClickメソッドとしてこのメソッドを呼び出します。

<label htmlFor="form-name" onClick={setSelected}>
...
</label>
<Field name="form-name" className="hidden">

ユーザーが次のフォームに移った時にどうやって自動的に値を設定するか

私は電話番号の入力に react-phone-input というライブラリを使用しました。この時、2つのフォームを用意しました。1つ目が、このライブラリが返してくるユーザーインプット。もうひとつが、redux-formが提供するhidden formです。 ユーザーが何かを入力すると、そのhidden-formを更新することになります。

setPhoneNumber = () => {
  var phoneNumber = document.getElementsByClassName("react-tel-input")[0].children[0].value
  this.props.change("phone_number", phoneNumber)
}

...

<ReactPhoneInput
  defaultCountry="de"
  onChange={() => this.setPhoneNumber()}
/>
<Field name="phone_number" type="text" component={renderPhoneNumberField} className="hidden" />

こんな感じになります。redux-formは便利ですが癖があるのでよくdocumentを読むことをおすすめします。

【メール送信エラー】Net::SMTPAuthenticationError

メール送信周りでちょっとはまりかけたのでメモ。
ponyというgemを使ってSMTPでメール送信をしようとしてました。
サイトからユーザーが申し込みしてきたら、申込完了メールをユーザーに送信するイメージです。

★環境
Ruby
・pony on sinatra
Gmailで送信(from @gmail.com)

たぶんRails(ActionMailer)でもあまり変わらないと思います。

普通にドキュメントに書かれてる通り実装すると、

Net::SMTPAuthenticationError - 534-5.7.14 <https://accounts.google.com/ContinueSignIn ...

というエラーが出てしまいました。
http://www.google.com/accounts/DisplayUnlockCaptcha
上記にアクセスして許可すればいいとう記事もありましたが、うまくいかず。

結論をいうと、送信したいGmailアカウントの設定から2段階認証を有効にして、
アプリ固有のパスワードを作り、実装にそのパスワードを組み込むのが手っ取り早いと思います。

スクリーンショット 2014-10-24 11.22.22.png

↑ 左上の「2段階認証プロセス」の設定を有効にすると「アプリパスワード」という項目が現れるので設定してください。

以上でうまくいくと思います。

※参考までにponyを使った実装を載せておきます。

def send_complete_mail
  # charsetはデフォルトでutf-8なのですが、指定してないと怒られた気がします。
  Pony.mail(
    to: '送信先メールアドレス',
    subject: '件名',
    body: send_mail_body,
    charset: 'utf-8',
    via: :smtp,
    via_options: {
      address: 'smtp.gmail.com',
      user_name: 'アカウント名@gmail.com',
      password: 'アプリ固有のパスワード',
      authentication: :plain,
      domain: 'サイトのドメイン名'
    }
  )
end

def send_mail_body
  # send_mail.erbというファイルを用意しておけばよいです。
  # localsのハッシュでview内の変数に値を渡せます。
  erb :send_mail, locals: {
    hoge: session[:hoge],
    fuga: session[:fuga]
  }
end

Mechanizeでページ遷移しながらスクレイピング

ちょっとダルいポイントが有ったのでメモ程度に。
スクレイピング対象サイトとスクレイピングの流れは

  • ページャで何ページか一覧ページがある
  • 一覧ページのタイトルをクリックすると詳細ページが見れる
  • 詳細ページの一部を使用
  • また他のタイトルをクリックしていく
  • CSVで出力(別にいらないけどメモ代わりに。。)

みたいな感じです。mechanizeだけでやります。

require 'mechanize'
require 'csv'

class ScrapingPages

  def initialize
    @agent = Mechanize.new
    @data = []
  end

  def retrieve
    # 1ページ目から10ページ目までスクレイピングする
    (1..10).each do |i|
      page = @agent.get(url(i)
      each_section(page) do |section|
        title = section.css('h2.title > a').first.text
        detail = @page.links_with(text: title).first.click
        @data << {
          title: title,
          detail: detail.links.first.text
        }
      end
    end
  end

  def each_section(page)
    page.search('.articleBox').each do |section|
      yield section
    end
  end

  def url(current_page)
    "https://hogehoge.com/#{current_page}"
  end

  def make_file_as_csv
    CSV.open("./csv/scraping-#{Time.now.to_i}.csv", "wb", encoding: 'Shift_JIS') do |csv|
      csv << %w(title detail)
      @data.each do |record|
        csv << [record[:title], record[:detail]]
      end
    end
  end
end

scraping_pages = ScrapingPages.new
scraping_pages.retrieve
scraping_pages.make_file_as_csv

コードは実際のものとちょこちょこ変更箇所あるので流しでいいのですが、ポイントはclickのところ。
mechanizeは中でnokogiriを使っているようで、
上記の@agent, pageとかはmechanizeクラスが 親のオブジェクトなのですが、
.cssとか使うと返ってくるオブジェクトがnokogiriクラスのインスタンスオブジェクトが返ってきます。

で、clickメソッドはmechanizeクラスに対してしか使えないので、微妙に工夫が必要。

title = section.css('h2.title > a').first.text
detail = @page.links_with(text: title).first.click

# .css('h2.title > a').first.clickとかすると、
# nokogiriがclickメソッド持ってないのでエラーになる。

ここです。title変数で一旦クリック個所のテキストを格納しておいて、
mechanizeクラス継承の@pageに対してリンクを辿って
テキストを指定してクリックする、と。

csvはよく忘れるのでメモ程度に載せただけです。

もっと良いやり方あるかもだけど。

Supervisor経由でunicornを立ち上げている環境にCapistrano3で自動デプロイ

前回の続きです。前回はCapistrano3の導入について書きました。
【入門】Capistrano3で自動デプロイ

★★★

私の環境ではsupervisor経由でunicornを監視しているのですが、supervisorをリスタートしてしまうとhot deploy出来ない問題がありました。

そちらに関しての解決策としては下記を参考にしてください。
supervisord + unicornでhot restart (deploy) する


さて、今回は上記を踏まえて

  • capistrano3
  • unicorn
  • supervisor
  • Rails4
  • Ruby2.1.1(ここのバージョンはあまり関係ない)

という環境で自動デプロイしたいと思います。

# config/deploy.rb

# config valid only for Capistrano 3.1
lock '3.2.0'

set :application, 'アプリ名'
set :repo_url, 'git@hogehoge:hogehoge/application.git'
set :branch, 'master'
set :scm, :git

set :format, :pretty
set :log_level, :info # :info or :debug
set :keep_releases, 3

set :rbenv_type, :user
set :rbenv_path, '~/.rbenv'
set :rbenv_ruby, '2.1.1'
set :rbenv_prefix, "RBENV_ROOT=#{fetch(:rbenv_path)} RBENV_VERSION=#{fetch(:rbenv_ruby)} #{fetch(:rbenv_path)}/bin/rbenv exec"
set :rbenv_map_bins, %w{rake gem bundle ruby rails}
set :rbenv_roles, :all


namespace :deploy do

  task :stop do
    on roles(:app) do
      execute 'kill -USR2 `cat tmp/pids/unicorn.pid`'
    end
  end

  task :graceful_stop do
    on roles(:app) do
      execute 'kill -USR2 `cat tmp/pids/unicorn.pid`'
    end
  end

  task :reload do
    on roles(:app) do
      execute 'kill -USR2 `cat tmp/pids/unicorn.pid`'
    end
  end

  task :restart do
    on roles(:app) do
      stop
    end
  end

  after :finishing, 'deploy:cleanup'
end

capistranoが通常の方法でリスタートしにいくところを上書きしに行ってるような感じです。

あとは環境ごとの設定を好きなように。

# config/deploy/production.rb

set :stage, :production

set :rails_env, 'production'
set :bundle_gemfile, -> { release_path.join('Gemfile') }
set :bundle_dir, -> { shared_path.join('bundle') }
set :bundle_flags, nil
set :bundle_without, %w{development test}.join(' ')
set :bundle_binstubs, nil
set :bundle_roles, :all

role :app, %w{ユーザー名@デプロイ先IP}
role :web, %w{ユーザー名@デプロイ先IP}
role :db,  %w{ユーザー名@デプロイ先IP}

set :deploy_to, '/home/ユーザー名/アプリ名'
set :ssh_options, {
  port: ポート番号,
  forward_agent: true
}

namespace :db do

  task :db_create do
    on roles(:db) do |host|
      execute "mysql -uroot -e 'CREATE DATABASE IF NOT EXISTS production_db;'"
    end
  end
end

ポイントが有るとすると、Rails4からbinstubsが原因でバグる可能性が出てくるので、 binstubsオプションにnilを渡してコマンドを作らないようにしている。
(Rails3まではrailsのサブコマンドがscriptディレクトリ以下だったが、Rails4からbin以下になったため)
⇛参考 railsのサブコマンドが使えなくなる問題の原因はbinstubs


あとはデプロイするのみです。通常通り行ってください。

$ bundle exec cap production deploy

ちなみに後ろに--traceオプションをつけると、より詳細のログが見れます。

【入門】Capistrano3で自動デプロイ

※この記事はcapistrano3についてです。capistrano2.x系には対応していません。


ちょこちょこ新規開発しているのですが、毎回リモートサーバーにsshで入って
pullして手順見ながらbundleなんちゃらして・・・。
みたいなのが非常にめんどくさいので、capistranoを使ってみました。
一度覚えてしまうと楽チンなので損はないと思います!そんなに難しくないです!

今回はインストールから実際のデプロイまで順を追って説明していきたいと思います。

★やろうとしていること

  • Rails4のアプリをリモートサーバーにローカルからデプロイ
    • web, db, appサーバーはとりあえず同じサーバーで
    • git pullとかassets:precompileとかmigrationとか自動でやりたい
  • テスト的にvagrantで作った仮想環境にデプロイするまでを説明します。

★できてないこと

■環境

  • Ruby2.1.1(あまり今回は関係ない)
  • rbenvを使っている
  • vagrant(CentOS6.4)
     

まずcapistranoの導入です。Gemfileに書くだけなので超簡単です。

# Gemfile

group :development do
  gem :capistrano
  gem :capistrano-rails
  gem :capistrano-bundler
  gem :capistrano-rbenv
end

そしてあとはいつもどおりインストール。

$ bundle install

さて、ここまでできたらcapistranoのデフォルトファイル群を用意します。
これもコマンドひとつでできます。

$ bundle exec cap install

mkdir -p config/deploy
create config/deploy.rb
create config/deploy/staging.rb
create config/deploy/production.rb
mkdir -p lib/capistrano/tasks
Capified

こんな感じでいくつかファイルが出来たと思います。
この時オプション指定でdevelopmentを作ったりもできます。
今回はstagingを使ってvagrantにテストデプロイしていきます。
 


Capfile

まずはここからいきましょう。とりあえずいろいろ書かれていますが全部消しちゃっていいです。

require 'capistrano/setup'
require 'capistrano/deploy'
require 'capistrano/rails'
require 'capistrano/rails/assets'
require 'capistrano/rails/migrations'
require 'capistrano/rbenv'
require 'capistrano/bundler'

# Loads custom tasks from `lib/capistrano/tasks' if you have any defined.
Dir.glob('lib/capistrano/tasks/*.rake').each { |r| import r }

使うものをrequireしているだけです。これでOK。
 

config/deploy/staging.rb

次にデプロイ先の環境を設定しましょう。今回はvagrantにデプロイします。

set :stage, :staging

role :app, %{vagrant@デプロイ先IP}
role :web, %{vagrant@デプロイ先IP}
role :db, %{vagrant@デプロイ先IP}

 

config/deploy.rb

これがメインとなるファイルです。ここにデプロイのtaskを書いていきます。
と、その前にデプロイ後どういうディレクトリ構造になるのかというのを知っておいたほうが理解しやすいと思うので下に図示しておきます。

[vagrant@localhost ~]$ tree アプリ名/ -L 2
アプリ名/
├── current -> /home/vagrant/アプリ名/releases/20140421075958
├── releases
│   ├── 20140421062631
│   ├── 20140421064312
│   ├── 20140421064620
│   ├── 20140421074708
│   └── 20140421075958
├── repo
│   ├── FETCH_HEAD
│   ├── HEAD
│   ├── branches
│   ├── config
│   ├── description
│   ├── hooks
│   ├── info
│   ├── objects
│   ├── packed-refs
│   └── refs
├── revisions.log
└── shared
    ├── bin
    ├── bundle
    └── public

17 directories, 6 files

上のように、capistranoは[current, releases, repo, shared]という4つのディレクトリを作ります。
releasesにリリースごとのバージョンが管理されていき、currentはreleasesの最新へのシンボリックリンクになっているという感じです。
その他のディレクトリについてはまた調べてみてください。

では、config/deploy.rbに戻りましてとりあえず必要最低限のところから設定していきましょう。

# config valid only for Capistrano 3.1
lock '3.1.0'

set :application, 'アプリ名'
set :repo_url, 'cloneしてくるレポジトリのURL.git'
set :branch, 'master' # デフォルトがmasterなのでこの場合書かなくてもいいです。
set :deploy_to, "/home/vagrant/アプリルートディレクトリ"
set :scm, :git # capistrano3からgitオンリーになった気がするのでいらないかも?

set :format, :pretty
set :log_level, :debug # :info or :debug
set :keep_releases, 3 # 何世代前までリリースを残しておくか

set :rbenv_type, :user
set :rbenv_ruby, '2.1.1'
set :rbenv_prefix, "RBENV_ROOT=#{fetch(:rbenv_path)} RBENV_VERSION=#{fetch(:rbenv_ruby)} #{fetch(:rbenv_path)}/bin/rbenv exec"
set :rbenv_map_bins, %w{rake gem bundle ruby rails}
set :rbenv_roles, :all # default value

こんな感じでしょうか。 set :key, :valuieのような構造になっています。

さて、いよいよデプロイ時に走らすタスクを書いていくというフェーズなのですが、
実はcapistranoはもともとデフォルトで既にいくつかのタスクを用意しています。

$ bundle exec cap -T

とやるとタスクの一覧が見れるのでみてみましょう。

cap bundler:install                # Install the current Bundler environment
cap bundler:map_bins               # Maps all binaries to use `bundle exec` by default
cap deploy                         # Deploy a new release
cap deploy:check                   # Check required files and directories exist
cap deploy:check:directories       # Check shared and release directories exist
cap deploy:check:linked_dirs       # Check directories to be linked exist in shared
cap deploy:check:linked_files      # Check files to be linked exist in shared
cap deploy:check:make_linked_dirs  # Check directories of files to be linked exist in shared
cap deploy:cleanup                 # Clean up old releases
cap deploy:cleanup_assets          # Cleanup expired assets
cap deploy:cleanup_rollback        # Remove and archive rolled-back release
cap deploy:compile_assets          # Compile assets
cap deploy:finished                # Finished
cap deploy:finishing               # Finish the deployment, clean up server(s)
cap deploy:finishing_rollback      # Finish the rollback, clean up server(s)
cap deploy:log_revision            # Log details of the deploy
cap deploy:migrate                 # Runs rake db:migrate if migrations are set
cap deploy:normalize_assets        # Normalize asset timestamps
cap deploy:published               # Published
cap deploy:publishing              # Publish the release
cap deploy:restart                 # Restart application
cap deploy:revert_release          # Revert to previous release timestamp
cap deploy:reverted                # Reverted
cap deploy:reverting               # Revert server(s) to previous release
cap deploy:rollback                # Rollback to previous release
cap deploy:rollback_assets         # Rollback assets
cap deploy:started                 # Started
cap deploy:starting                # Start a deployment, make sure server(s) ready
cap deploy:symlink:linked_dirs     # Symlink linked directories
cap deploy:symlink:linked_files    # Symlink linked files
cap deploy:symlink:release         # Symlink release to current
cap deploy:symlink:shared          # Symlink files and directories from shared to release
cap deploy:updated                 # Updated
cap deploy:updating                # Update server(s) by setting up a new release
cap install                        # Install Capistrano, cap install STAGES=staging,production

よく見てみると、すでにgit pullやrake db:migrateやassets:precompuleも用意されています。
なのでミニマムではタスクは何も書かなくてもよいでしょう。

でもせっかくなのでDBがデプロイ時になければ作成するというタスクを追加しておきましょう。

# デプロイ前に実行する必要がある。
desc 'execute before deploy'
task :db_create do
  on roles(:db) do |host|
    execute "mysql -uroot -e 'CREATE DATABASE IF NOT EXISTS データベース名;'"
  end
end

これはnamespace :deployの外に書いて個別に実行すると良いと思います。

いくつかタスクについて説明すると、まずnamespaceで名前空間を切ってそれぞれを管理できます。
あとはrolesというのがポロポロ出てきていると思うのですが、これは例えばroles(:db)としておくと、dbサーバーに対してのみ実行します。(dbサーバーはconfig/deploy/以下で指定したものですね。)

taskの前にdescを書くことが出来て、タスクの説明なんかも残しておけます。  

デプロイ

さて、ここまでくれば後は実際にデプロイするのみです。 ローカルからdeployコマンドをうってみましょう。

$ bundle exec cap db_create
$ bundle exec cap staging deploy

うまくできましたか?本番リリース時はstagingのところをproductionにしてデプロイして下さい。

 


 

余談

僕が導入しようと思った環境はsupervisor経由でunicornを動かしていて、リリースごとにsupervisorを再起動する必要があります。
なのでsupervisorの再起動中に503がでてしまって"ゔッ!"ってなるのでcapistrano経由でunicornホットデプロイをできないかとも
考えていたのですが、そもそもsupervisorを使ったunicornホットデプロイのベストプラクティスがわかっておらず、
(先輩が調べてくれたのですがなかなかやっかいそう…)
そこんとこまだ完全に自動化しきれていないなー、、というのが現状です。。

ともかくcapistrano自体の導入はそれほど敷居は高くないので導入してみてはいかがでしょうか!(๑╹ڡ╹๑)