Capistrano は複数のサーバーに ssh して何らかの処理を実行するための汎用ツールです。こちらのページでは Rails のデプロイツールとしての Capistrano 3 の使用方法をまとめました。Rails のデプロイに関しては Capistrano の設定が gem で提供されているため、独自に Capistrano の設定をする必要はありませんでした。ここでは Rails に限定されない一般的な使用方法をまとめます。
参考ページ
bundler はインストール済みであるとします。Gemfile で capistrano をインストールします。
Gemfile
source 'https://rubygems.org'
group :development do
gem "capistrano", "~> 3.4"
end
現在有効な ruby に対して bundler で capistrano を vendor/bundle
にインストールします。
bundle install --path vendor/bundle
以下のファイルが生成されました。git プロジェクトの場合は必要に応じて .gitignore
に追記します。
.gitignore
で除外します).gitignore
で除外します)Gemfile.lock
はレポジトリに含めるようにしてください。
It is recommended to fix the version number when using Capistrano, and is therefore recommended to use an appropriate bundler.
http://capistranorb.com/documentation/getting-started/installation/
.gitignore
.bundle
bundle
以下のコマンドで動作確認できます。実行可能なタスク一覧が表示されます。
bundle exec cap -T
以下のコマンドで設定ファイルの雛形を生成します。公式ドキュメントではこれを "Capify" と表現しています。
$ bundle exec cap install
不要ですので "no" としました。
Enhance Capistrano with awesome collaboration and
automation features? (Yes/no): no
以下のファイルが生成されました。すべて git レポジトリに含めます。
Capfile
lib/capistrano/tasks/
config/deploy.rb
config/deploy/staging.rb
config/deploy/production.rb
Console を require
すると console
タスクが利用できるようになります。Capfile
に以下の一行を追記します。
require 'capistrano/console'
複数サーバーに対して一括して同じコマンドを発行できます。
config/deploy/staging.rb
server 'xxx.xxx.xxx.xxx', user: 'ec2-user'
server 'yyy.yyy.yyy.yyy', user: 'ec2-user'
実行例
$ bundle exec cap staging console
staging> uptime
03 uptime
03 22:50:13 up 2:12, 1 user, load average: 0.03, 0.02, 0.00
✔ 03 ec2-user@xxx.xxx.xxx.xxx 0.632s
03 22:50:13 up 2:12, 1 user, load average: 0.03, 0.02, 0.00
✔ 03 ec2-user@yyy.yyy.yyy.yyy 0.669s
Capify すると deploy タスクに関してのみ予め定義されたタスク群が利用できるようになります。
Capfile
# Include default deployment tasks
require "capistrano/deploy"
デプロイとは関係のない任意のタスクを用意するためには以下のようにします。
lib/capistrano/tasks/my_task.rake
# ユーザー入力を求めます。パスワード等については echo: false として内容が表示されないようにします。
set :my_variable, ask('Enter something:', '既定値', echo: false)
set :my_variable2, "you typed #{fetch(:my_variable)}"
# 設定が前後する場合はラムダ `{}` を利用して遅延評価します。
set :my_variable4, -> { "my_variable3 == #{fetch(:my_variable3)}" }
set :my_variable3, 123
desc "\"desc\" を記載すると cap -T に表示されるようになります。"
task :my_task do
run_locally do
execute "hostname"
end
on roles(:all) do |host|
execute "echo \"#{fetch(:my_variable2)}\""
execute "echo \"#{fetch(:my_variable4)}\""
end
on roles(:my_role1) do |host|
execute "echo \"#{host} has my_role1\""
end
on roles(:my_role2) do |host|
execute "echo \"#{host} has my_role2\""
end
end
config/deploy/staging.rb
server '127.0.0.1', user: 'user1', roles: [:my_role1]
server 'localhost', user: 'user2', roles: [:my_role1, :my_role2]
実行例
$ bundle exec cap staging my_task
Please enter Enter something: (既定値):
00:00 my_task
01 hostname ← 実行コマンド
01 vagrant ← 実行結果
✔ 01 vagrant@localhost 0.004s ← run_locally が完了
02 echo "you typed abc" ← on roles(:all) 開始
02 you typed abc
✔ 02 user1@127.0.0.1 0.570s
03 echo "my_variable3 == 123"
02 you typed abc
✔ 02 user2@localhost 0.617s
03 my_variable3 == 123
✔ 03 user1@127.0.0.1 0.622s
03 my_variable3 == 123
✔ 03 user2@localhost 0.611s ← on roles(:all) 完了
04 echo "localhost has my_role1" ← on roles(:my_role1) 開始
05 echo "127.0.0.1 has my_role1"
04 localhost has my_role1
✔ 04 user2@localhost 0.100s
05 127.0.0.1 has my_role1
✔ 05 user1@127.0.0.1 0.100s ← on roles(:my_role1) 完了
06 echo "localhost has my_role2" ← on roles(:my_role2) 開始
06 localhost has my_role2
✔ 06 user2@localhost 0.045s ← on roles(:my_role2) 完了
upload!
でローカルマシンのファイルを各リモートホストにアップロードできます。
task :my_task do
on roles(:all) do |host|
upload! 'Capfile', "/tmp/Capfile_#{host}"
end
end
capture
でコマンド実行結果を変数に格納できます。info
, warn
, error
でログ出力できます。
task :my_task do
run_locally do
output = capture "whoami"
info output
warn output
error output
end
end
invoke
による連携lib/capistrano/tasks/my_task.rake
task :my_task01 do
on roles(:all) do |host|
info "my_task01"
end
end
task :my_task02 do
on roles(:all) do |host|
invoke 'my_task01' # 他のタスクを実行
info "my_task02"
end
end
実行例
$ bundle exec cap staging my_task01
00:00 my_task01
my_task01
$ bundle exec cap staging my_task02
00:00 my_task01
my_task01
my_task02
before
, after
による連携lib/capistrano/tasks/my_task.rake
task :my_task01 do
on roles(:all) do |host|
info "my_task01"
end
end
task :my_task02 do
on roles(:all) do |host|
info "my_task02"
end
end
before :my_task02, :my_task01
# after :my_task02, :my_task01
before
, after
による連携 (ブロック形式)lib/capistrano/tasks/my_task.rake
task :my_task02 do
on roles(:all) do |host|
info "my_task02"
end
end
before :my_task02, :my_task03 do
on roles(:all) do |host|
info "my_task03"
end
end
実行例
$ bundle exec cap staging my_task03
00:00 my_task03
my_task03
my_task03
$ bundle exec cap staging my_task02
00:00 my_task03
my_task03
my_task03
00:00 my_task02
my_task02
my_task02
サーバー数が多い場合、例えば git pull などが同時に実行されるとホスティングサーバーに負荷がかかります。また、デプロイ後のプロセス再起動を全ホストで一斉に行うとダウンタイムが発生するため 1 ホストずつプロセス再起動 (rolling restarts) するのが好ましいです。これを実現するためには以下の設定を利用します。
on(in: :sequence, wait: 5) { ... }
on(in: :groups, limit: 2, wait: 5) { ... }
デフォルト値です。parallel
実行されます。
task :my_task do
on roles(:all), in: :parallel do |host|
execute "whoami && date"
execute "whoami && date"
end
end
実行例
$ bundle exec cap staging my_task
00:00 my_task
01 whoami && date
01 user1 ← リモートサーバー1
01 user2 ← リモートサーバー2
01 2016年 7月 4日 月曜日 01:33:55 JST
01 2016年 7月 4日 月曜日 01:33:55 JST
✔ 01 user2@localhost 0.630s
✔ 01 user1@127.0.0.1 0.614s
01 user2 ← リモートサーバー2
01 user1 ← リモートサーバー1
01 2016年 7月 4日 月曜日 01:33:56 JST
✔ 01 user2@localhost 0.661s
01 2016年 7月 4日 月曜日 01:33:56 JST
✔ 01 user1@127.0.0.1 0.643s
task :my_task do
on roles(:all), in: :sequence, wait: 5 do |host|
execute "whoami && date"
execute "whoami && date"
end
end
実行例
$ bundle exec cap staging my_task
00:00 my_task
01 whoami && date
01 user1 ← リモートサーバー1
01 2016年 7月 4日 月曜日 01:35:01 JST
✔ 01 user1@127.0.0.1 0.546s
01 user1 ← リモートサーバー1
01 2016年 7月 4日 月曜日 01:35:02 JST
✔ 01 user1@127.0.0.1 0.579s ← ここで `wait: 5` (秒) が始まります。
01 user2 ← リモートサーバー2
01 2016年 7月 4日 月曜日 01:35:07 JST
✔ 01 user2@localhost 0.531s
01 user2 ← リモートサーバー2
01 2016年 7月 4日 月曜日 01:35:08 JST
✔ 01 user2@localhost 0.557s ← このあとすぐに終了します。
limit: 1
とすると、ほぼ sequence
の場合と同じ処理方法になります。
task :my_task do
on roles(:all), in: :groups, limit: 1, wait: 5 do |host|
execute "whoami && date"
execute "whoami && date"
end
end
実行例
$ bundle exec cap staging my_task
00:00 my_task
01 whoami && date
01 user1 ← リモートサーバー1
01 2016年 7月 4日 月曜日 01:38:53 JST
✔ 01 user1@127.0.0.1 0.541s
01 user1 ← リモートサーバー1
01 2016年 7月 4日 月曜日 01:38:54 JST
✔ 01 user1@127.0.0.1 0.593s ← ここで `wait: 5` (秒) が始まります。
01 user2 ← リモートサーバー2
01 2016年 7月 4日 月曜日 01:38:59 JST
✔ 01 user2@localhost 0.526s
01 user2 ← リモートサーバー2
01 2016年 7月 4日 月曜日 01:39:00 JST
✔ 01 user2@localhost 0.541s ← ここで `wait: 5` (秒) が始まります (sequence との違い)
task :my_task01 do
on roles(:all), in: :groups, limit: 1, wait: 5 do |host|
execute "whoami && date"
execute "whoami && date"
end
end
task :my_task02 do
on roles(:all) do |host|
execute "whoami && date"
execute "whoami && date"
end
end
after :my_task01, :my_task02
実行例
$ bundle exec cap staging my_task01
00:00 my_task01
01 whoami && date
01 user1 ← リモートサーバー1
01 2016年 7月 4日 月曜日 01:58:28 JST
✔ 01 user1@127.0.0.1 0.661s
01 user1 ← リモートサーバー1
01 2016年 7月 4日 月曜日 01:58:28 JST
✔ 01 user1@127.0.0.1 0.606s ← ここで `wait: 5` (秒) が始まります。
01 user2 ← リモートサーバー2
01 2016年 7月 4日 月曜日 01:58:34 JST
✔ 01 user2@localhost 0.482s
01 user2 ← リモートサーバー2
01 2016年 7月 4日 月曜日 01:58:34 JST
✔ 01 user2@localhost 0.501s ← ここで `wait: 5` (秒) が始まります。
00:12 my_task02 ← my_task01 が完了してから、my_task02 は全サーバーでほぼ同時に実行されます↓
01 whoami && date
01 user2
01 2016年 7月 4日 月曜日 01:58:39 JST
✔ 01 user2@localhost 0.103s
01 user2
01 2016年 7月 4日 月曜日 01:58:40 JST
✔ 01 user2@localhost 0.326s
01 user1
01 2016年 7月 4日 月曜日 01:58:40 JST
✔ 01 user1@127.0.0.1 0.630s
01 user1
01 2016年 7月 4日 月曜日 01:58:40 JST
✔ 01 user1@127.0.0.1 0.054s