[rails]capistrano3でrole指定したサーバが複数台ある場合に、ある1台でのみ実行する方法


[まとめ] 現在開催中のKindleセール情報はこちら

capistrano 3.2.1 で確認

capistrano3でrole指定したサーバが複数台ある場合に、ある1台でのみ実行する方法です。

namespace :deploy do
  task :primary_task do
    # primaryメソッドを使用
    on primary :app do
      # :app role のprimaryサーバでのみ実行される処理
    end
  end
end

上記のようにprimaryメソッドを使うことでできました。

db:migrateもこのprimaryメソッドで実行されているようです。

server設定で明示的にprimaryを指定していない場合は、該当roleの1台目のサーバがprimaryになるようです。

検証として、以前このブログで紹介した s3cmdを使ってs3にアップロードするコマンド をcapistrano3経由で実行したいと思ったので試してみました。

事前準備

まずは上記のコマンドをシェルスクリプトにまとめて s3sync.sh を作成

#!/bin/sh
conf="./.s3cfg"
if [ "${1}" = "s3://sample-development" ]; then
  conf="./.s3cfg_dev"
fi
s3cmd -c $conf -P --exclude-from .s3ignore --follow-symlinks sync ./public/ $1 $2

開発版(sample-development)の場合は開発版の設定を使用するようにしてみました。
これで

./s3sync.sh s3://sample-development --dry-run

という風に書けば、sample-developmentバケットにpublic以下をdry-run実行できるようになりました。

capistrano3設定

最初、taskに以下のようにroleがappの時にs3アップロードを実行するようにしようと思ったのですが、

namespace :deploy do
  task :s3update do
    on roles(:app) do
      # s3アップロード処理
    end
  end
end

これだとapp roleのサーバが複数ある場合に、各サーバでs3アップロードが実行されてしまいます。

そこでprimaryメソッドを使用するように変更します。

set :is_update_s3, ask('s3を更新しますか?', 'yes')

namespace :deploy do
  task :s3update do
    on primary :app do
      # rails_envを元にバケット名を指定
      set :bucket_name, case fetch(:rails_env)
        when :development
          'sample-development'
        when :staging
          'sample-staging'
        when :production
          'sample-production'
        end

      # アップロード内容確認
      execute "cd #{release_path} && ./s3sync.sh s3://#{fetch(:bucket_name)} --dry-run"

      # yesの場合はアップロード
      if fetch(:is_update_s3) =~ /yes/i
        execute "cd #{release_path} && ./s3sync.sh s3://#{fetch(:bucket_name)}"
      end
    end
  end

  before :restart, 'deploy:s3update'
end

こんな感じにすると、cap deploy時にapp roleのサーバ1台のみがs3アップロードコマンドを実行してくれるようになります。

参考

[まとめ] 現在開催中のKindleセール情報はこちら