Capistrano は複数のサーバーに ssh して何らかの処理を実行するための汎用ツールです。Rails のデプロイに関しては Capistrano の設定が gem で提供されているため、独自に Capistrano の設定をする必要はありませんでした。ここでは Rails に限定されない一般的な使用方法をまとめます。
参考ページ
- Capistrano 公式ドキュメント
- 初期設定について: The Readme, start here!
- コンソールモード: Console
- deploy タスクについて
- タスク全般について
- 入門 Capistrano 3 ~ 全ての手作業を生まれる前に消し去りたい
インストール
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
に追記します。
- Gemfile.lock (git レポジトリに含めます)
- .bundle (
.gitignore
で除外します) - vendor/bundle (
.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
複数サーバーに SSH ログインしてコマンドを発行
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
関連記事
- レイアウトおよび部分テンプレートに関するまとめ (Rails4)レイアウトおよび部分テンプレートはどちらもテンプレート (*.html.erb) に共通の要素をまとめておき、任意のテンプレートから利用できるようにしておくための仕組みです。フッターやヘッダーといった大枠はレイアウト、小さなパーツは部分テンプレートというイメージで使い分けましょう。 レイアウトの使用方法 クラス毎に指定する方法と、アクション毎に指定する方法があります。 app/views/layo...
- Ruby コードスニペット (正規表現)sample.rb str = "001: This is a string." var1,var2 = 2,3 # 'EOS'とすると#{}による変数展開がなされない (%03dは展開される) doc = (<<"EOS" % var1) # 括弧は省略可。要は<<"EOS"の次の行からEOSまで。(参: <<-"EOS"とすると前に空白...
- OAuthを用いずにTwitterに自動投稿する (回数制限あり, Selenium with Ruby)Seleniumを用いて、OAuthを用いずにTwitterに自動投稿するRubyスクリプトを記述してみます。連続で複数回実行すると、ボット判定としてキャプチャ認証が発生します。その認証までは通過できませんので悪しからず。また、Twitterの仕様変更次第ではDOMの構造が変化するため、下記サンプルは機能しなくなる恐れが有ります。 twitter_post.rb #!/usr/bin/ruby r...
- Ruby における日本語のエンコーディング日本語を含めて多言語対応する際には、Asciiコード以外の文字コードセットが必要になります。日本語が主となる場合、よく使われる文字セットにはUnicode, Shift_JIS, EUC-JPがあります。このうち Unicode だけは特殊であり、世界中のあらゆる文字を収録しようとしていることから 1 文字を表現するために必要なバイト数が大きくなってしまっています。そのため Unicode のうち...
- Rails3ビューテンプレートの基本的な使用方法 (Ruby)Railsでは、ERB (eRuby (テキストファイルにRubyスクリプトを埋込む書式の仕様) をRubyで実装したもの) を用いてHTML内にRubyスクリプトを埋込むことができます。 <% %> で囲むと出力されません (if-elseなど制御構文を記述します) <%= %> で囲むとエスケープ出力されます <%== %> で囲むとエスケープされずに...