技術の犬小屋

Webプログラミングを餌に生きる犬のメモ帳

仕事でCapistranoを使っている。使い始めて半年経ったので,そろそろ使い方をまとめておく。
 

Capistranoとは

Capistranoは,複数のサーバー上でスクリプトを実行するためのオープンソースのツールであり,その主な用途は,ウェブアプリケーションのソフトウェアデプロイメントである。1つ以上のWebサーバ上のアプリケーションを新しいバージョンにする作業を自動化でき,データベースを変更するといった作業もできる。
 

Capistranoの使い方

Capistranoをインストールする

Capistranoはrubyで書かれているので,インストールするにはRubyとRubygemsとBundlerが必要である。
Gemfileという名前のファイルを作成し,以下の記述を追加して bundle install を実行することでインストールすることが出来る。

gem 'capistrano', '~> 3.0.1'

 

Capistranoの設定ファイルを作成する

Capistranoのインストールが終わったら,Capistranoの設定ファイルを作成する。以下のコマンドを実行する。

bundle exec cap install

 
実行すると,Capfile,configディレクトリ,libディレクトリが作成される。
 

config/deploy/ステージ名.rbを記述する方法

ステージ名.rbには、ステージ毎の設定を書くことが出来る。ステージ名.rbには以下のような内容を記述する。

  • ホスト名
  • ログインユーザ
  • サーバロール
  • SSH設定
  • その他、そのサーバに紐づく任意の設定

 
ファイル内では,次のような対応でルールを記述する。

  • server ホスト名
  • user: ログインユーザ名
  • roles: %{サーバロール}
  • その他の設定: 値

 
今回はtest.rbという名前で簡単な例を作成する。

server 'localhost', user: 'vagrant', roles: %w{web}

 
上記の一行には以下の設定情報が含まれている。

  • localhost(Capistranoを実行したホスト)を対象にする
  • そのサーバへはvagrantというユーザでログインする
  • そのサーバに「Webサーバ」というロールを与える

 

config/deploy.rbを記述する方法

config/deploy.rbにはステージ間で共通の設定を記述する。よくあるのは以下のような設定である。

  • アプリケーション名
  • レポジトリ名
  • 利用するSCM
  • タスク
  • それぞれのタスクで実行するコマンド

 
上記のような設定をDSLで記述する。DSLには以下を定義するための文法と語彙が存在する。

  • 設定値の変更と取得
  • これを定義するとグローバル変数のようにdeploy.rbやステージ.rbの全域で設定値を取り出すことが出来る。

  • タスクの定義
  • 実行したいコマンドなどをタスクとして定義することが出来る。

 

設定値の変更と取得
set :repo_url, 'git@github.com:whohu/sample_app'
fetch :repo_url
#=> "git@github.com:whohu/sample_app"

 
上記は set :名前, 値 で設定し, fetch :名前 で設定を取り出している。
 

タスクの定義
task :uptime do
  ここにタスクの内容
end

 
上記は,task :タスク名 do; … endのブロックでタスクを設定している。
 
taskブロックの中には run_locally do; … end ,もしくは on 対象サーバ do; … end ブロックを記述する。前者の run_locally ブロック内にはローカルマシン上で実行するコマンドについて記述する。また,後者の on ブロック内にはサーバ上で実行するコマンドについて記述する。
 

task :uptime do
  run_locally do
    #ここにローカルマシン上で実行するコマンド
  end
  on roles(:web) do
    #ここにサーバ上で実行するコマンド
  end
end

 
上記の on ブロックでは,Webサーバ(:web)のロールが与えられているサーバのみを作業対象とするような記述を行っている。
 

主なコマンド

run_locally と on ブロックで使用することが出来る主なコマンドとして以下の3つがある。

  • execute
  • コマンドを実行することが出来る。

    task :uptime do
      run_locally do
        execute "uptime"
      end
      on roles(:web) do
        execute "uptime"
      end
    end
    
  • capture
  • 標準出力の内容を受け取ることが出来る。

    task :uptime do
      run_locally do
        output = capture "uptime"
      end
      on roles(:web) do
        output = capture "uptime"
      end
    end
    

    上記では, output に標準出力の内容が代入される。

  • info
  • ログを出力することが出来る。

    task :uptime do
      run_locally do
        output = capture "uptime"
        info output
      end
      on roles(:web) do
        output = capture "uptime"
        info output
      end
    end
    

    上記では,標準出力を受け取ってログに出力している。
    ログレベルに応じて debug,info,warn,error,fatal など文法を使うことが出来る。

 

実際にレシピを書いてみる

実際に,config/deploy/test.rbとconfig/deploy.rbに設定を記述してみる。
 

server 'localhost', user: 'vagrant', roles: %w{web}

 

set :application, 'finalge_sample_app'
set :repo_url, 'git@github.com:whohu/sample_app.git'

#Gitからソースコードを取得する。
task :update do
  run_locally do
    application = fetch :application
    if test "[ -d #{application} ]"
#test.rbで記述した環境下にfinalge_sample_appディレクトリがあれば,ディレクトリ内に移動して git pull する。
      execute "cd #{application}; git pull"
    else
#test.rbで記述した環境下にfinalge_sample_appディレクトリが無ければ, git clone する。
      execute "git clone #{fetch :repo_url} #{application}"
    end
  end
end

#上記のタスク update が終わった後に archive が実行される。
task :archive => :update do
  run_locally do
#sbtコマンドでビルドする(sbtはScalaのビルドコマンド)。
    sbt_output = capture "cd #{fetch :application}; sbt pack-archive"
#パスを取得する処理が続く…。
    sbt_output_without_escape_sequences = sbt_output.lines.map { |line| line.gsub(/\e\[\d{1,2}m/, '') }.join
    archive_relative_path = sbt_output_without_escape_sequences.match(/\[info\] Generating (?<archive_path>.+\.tar\.gz)\s*$/)[:archive_path]
    archive_name = archive_relative_path.match(/(?<archive_name>[^\/]+\.tar\.gz)$/)[:archive_name]
    archive_absolute_path = File.join(capture("cd #{fetch(:application)}; pwd").chomp, archive_relative_path)

    info archive_absolute_path
    info archive_name

    set :archive_absolute_path, archive_absolute_path
    set :archive_name, archive_name
  end
end

#上記のタスク archive が終わった後に deploy が実行される。
task :deploy => :archive do
#archive タスクで設定したパスを取得する。
  archive_path = fetch :archive_absolute_path
  archive_name = fetch :archive_name
  release_path = File.join(fetch(:deploy_to), fetch(:application))

  on roles(:web) do
#前回デプロイした際に実行したアプリケーションのプロセスをkillする(2回目以降のデプロイを考慮)
    begin
      old_project_dir = File.join(release_path, capture("cd #{release_path}; ls -d */").chomp)
      if test "[ -d #{old_project_dir} ]"
        running_pid = capture("cd #{old_project_dir}; cat RUNNING_PID")
        execute "kill #{running_pid}"
      end
    rescue => e
      info "No previous release directory exists"
    end

    unless test "[ -d #{release_path} ]"
#test.rbで記述した環境下に release_path に格納したパスが存在しない場合は新しくディレクトリを作成する。
      execute "mkdir -p #{release_path}"
    end
#archive_path変数が参照しているディレクトリをrelease_path変数が参照しているディレクトリへコピーする。
    upload! archive_path, release_path
#release_path変数が参照しているディレクトリへ移動し,tarコマンドでアーカイブを作成する。
    execute "cd #{release_path}; tar -zxvf #{archive_name}"

    project_dir = File.join(release_path, capture("cd #{release_path}; ls -d */").chomp)

    launch = capture("cd #{project_dir}; ls bin/*").chomp
#nohupコマンドで起動スクリプトを実行する。
    execute "cd #{project_dir}; ( ( nohup #{launch} &>/dev/null ) & echo $! > RUNNING_PID)"
  end
end

 

capコマンドでタスクを実行する

必要な設定が記述し終わったら,capコマンドによってタスクを実行する。
capコマンドの第1引数がconfig/deploy/test.rbのtestの部分に対応する。第2引数がタスク名で,task :deployのdeployの部分に対応する。

bundle exec cap test deploy

 
 
以上
 
 
参考
capistrano/capistrano
Capistrano – Wikipedia
入門 Capistrano 3 ~ 全ての手作業を生まれる前に消し去りたい | GREE Engineers’Blog
【入門】Capistrano3で自動デプロイ – Qiita
capistrano 3 をできるだけシンプルにサーバーにコマンドを流し込むツールとして使いこなす – Qiita

Jenkinsの使い方 arrow-right
Next post

arrow-left 英語における前置詞の使い方
Previous post

コメントを残す