Capistrano事始め

インストールするまで

サーバを沢山管理することになりました.それらのサーバではrubyを使っていたので,rubyベースのdeployツールCapistranoを使ってみる事にしました.Capistranoは2009/2に作者がもうメンテナンスしないぜと宣言したプロダクトです.とはいっても,コミュニティがメンテすることになるだろうから今後も問題ないだろうなと思ってます.それはそれとして,CapistranoをインストールするにはRubyGemsが必要なのでまずはそこからインストールします.RubyGems の使い方 - WebOS Goodiesを見ながらwgetでファイルをダウンロードして,

# ruby setup.rb

でインストールします.Capistrano

$ sudo gem install capistrano

でインストールできます.

秘密鍵の管理

普段色々鍵を使っていてdefaultの".ssh/id_dsa"とかを使うようにはしたくなかったので,明示的に鍵を指定するようにしました.もしかしたら,.ssh/configが使えたのかもしれないですが,調べてないです…

role :test1, "sv100.foo.co.jp"

task :hostname, :roles => :test1 do
  run "hostname"
end

ssh_options[:keys] = %w(/home/daiba/.ssh/capistrano)
ssh_options[:auth_methods] = %w(publickey)

これで,test1というロールで指定したホスト群に対して,/home/daiba/.ssh/capistranoという秘密鍵sshして,hostnameを実行し,その結果を表示します.

メニュー化

Capistranoはdefaultでcapfileという名前のファイルを選んで,その上で定義したルールを実行しますが,-fオプションを付けるとファイルを指定することもできます.

$ cap hostname
$ cap -f CAPFILE hostname

ロールを沢山作った時にロールが覚えられなくなるだろうと思ったので,menuというロールを作って,そこから選択できるようにしてみました.

role :test1, "sv100.foo.co.jp"

desc "menu list"
task :menu do
  Capistrano::CLI.ui.say("\nThis is with a oneline menu layout...")
  Capistrano::CLI.ui.choose do |menu|
    menu.layout = :one_line

    menu.header = "Execute"
    menu.prompt = "Application?  "

    menu.choice :hostname do
      hostname
    end
    menu.choice :whoami do
      whoami
    end
    menu.choices(:skip, :exit) do
      Capistrano::CLI.ui.say("Choose not to run..")
    end
  end
end

task :hostname, :roles => :test1 do
  run "hostname"
end
task :whoami, :roles => :test1 do
  run "whoami"
end

この場合だと,hostnameとwhoamiが選択できます.

sudoでパスワードを入力

sshするだけではなく,別ユーザになってコマンドを実行したいときがあります.この場合はsudoを利用するわけですが,そのときはこんな感じに使います.

role :test1, "sv100.foo.co.jp"

set :sudo_password, 'Password:'

task :whoami do
  run "#{sudo :as => 'oops'} whoami", roles => :test1 do |channel, stream, data|
    if stream == :out
      puts channel[:host] + ': ' + data
    end
  end
end

"set :sudo_pasword, 'Password:'"はsudoプログラムが出力する文字列を指定しているので,システムによっては変更する必要があるでしょう.defaultでは"sudo password: "が割り当てられています.これで,sudoになる時のパスワードがキャッシュされていなければCapistranoが質問してきます.sudo変数を使うのでなければ,以下のような使い方もできます.

role :test1, "sv100.foo.co.jp"

set(:my_password) { Capistrano::CLI.password_prompt("My password: ") }

task :whoami do
  run "sudo -u ops whoami", :roles => :test1 do |channel, stream, data|
    if data =~ /^Password:/
      channel.send_data "#{my_password}\n"
    end
    if stream == :out
      puts data
    end
  end
end

dynamicにロールのターゲットを作成

role対象のホストを一個づつ書くのは面倒なので,スクリプトをそこに書きたくなります.ターゲットは"hostA, hostB, hostC"のような文字列か,配列を与えることができるので,こんな使い方ができます.

role(:servers) do
  hosts = Array.new
  100.upto 200 do |x|
    hosts.push('sv' + String(x) + '.foo.co.jp')
  end
  hosts
end

task :hostname, :roles => :servers, :max_hosts => 10 do
  run "hostname"
end

[5/20追加]1000台のマシンに対して実行したらプロセスがうんともすんとも言わなかったので調べてみたら,defaultではsshセッションの上限が設定されてないようでした.そのため,大量のホストにコマンドを実行する際には,":max_hosts"で一度に張るセッションの上限を設定する必要があります.":max_hosts"は将来なくなるかもしれないオプションのようです.

scpでファイル配布(書くのを忘れてたので追加)

ファイル配布にはsftpとscpが使えます.普段scpを使ってるのでそっちしか試してません.設定は以下のようになるのですが,

role :test1, "sv100.foo.co.jp"

desc "upload file"
task :upload!, :roles => :test1  do
  upload("target_file.txt", "/tmp", :via => :scp)
end

uploadはローカルマシンからターゲットマシンへの転送,逆方向の場合はdownloadになります.

さて

苦労した割にはtipsが溜まらなかったです.ここでやっているようなことをArcher - yet another deployment tool - metacpan.orgでやり直すかどうかを悩んでいるところ.