maehachi08 Anything Blog

2013年02月26日
capistranoでgithubからデプロイしてみました

What's Capistrano?

CapistranoはRubyで記述されたデプロイメントツールで、SSH経由で複数のリモートマシン上で並列にコマンドを実行するためのフレームワークです。一般的には、git(github含む)やsubversion等といったSCM(Source Code Management)からソースコードをアプリケーションサーバへデプロイしたり、アプリケーション関連ディレクトリの管理やデータベースの更新等を行うことが多いと思います。また、CapistranoのフロントエンドWebUIツールとして、『webistrano』があります。詳細は、本家のwikiwikipediaのCapistranoページを参照頂いた方がいいかと思います。

以下内容は、私が個人環境でCapistranoサーバを1から作っていく過程の備忘録メモです。

  1. Make "deployer" user for deploy
  2. Install Capistrano
  3. Initilize Capistrano
  4. Configuration of config/deploy.rb
  5. Exec Capistrano task "deploy:setup"
  6. Exec Capistrano task "deploy"
  7. Exec Capistrano task "deploy:cleanup"
  8. Use "capistrano_rsync_with_remote_cache" Extension
  9. Use "capistrano-ext" Extension

4項目目の"Configuration of config/deploy.rb"で記載するdeploy.rbをベースに、以降の"capistrano_rsync_with_remote_cache"や"capistrano-ext"向けに修正していってます。また、念のため、ベースとの差分をdiffで記載しています。

Environment infrastructure

Capistranoインストールを試した環境はVMWareServer上で動作するCentOS6.3-x86_64です。

# cat /etc/redhat-release
CentOS release 6.3 (Final)

# uname -an
Linux pachi 2.6.32-279.el6.x86_64 #1 SMP Fri Jun 22 12:19:21 UTC 2012 x86_64 x86_64 x86_64 GNU/Linux

Make "deployer" user for deploy

ユーザ名は何でもいいですが、後述するconfig/deploy.rbに記述するユーザ名と一致している必要があります。

# groupadd -g 301 deployer
# useradd -g 301 -u 301 deployer
# passwd deployer

Install Capistrano

CapistranoはRuby製なので、gemパッケージが用意されています。RHEL系Distributionではrubygemsパッケージがインストールされていればgemコマンドでインストールできます。
capコマンド結果のコンソール表示に色付けしてくれるcapistrano_colorsもインストールする。

# yum -y install rubygems
# gem install capistrano capistrano_colors

Initilize Capistrano

初期化してCapistranoのタスクを利用できる状態にします。

# su - deployer
$ mkdir -p ~/app/capistrano
$ cd ~/app/capistrano

### capifyコマンドの引数に初期化したいディレクトリパスを指定する必要があります
$ capify ./

### capifyコマンド実行結果
[add] making directory './config'
[add] writing './config/deploy.rb'
[add] writing './Capfile'
[done] capified!

Configuration of config/deploy.rb

初期化によって、./Capfileとconfig/deploy.rb、2つのファイルが作成されました。
./Capfileは、Capistranoに必須のファイルで、MakefileやRakefileにあたるようなファイルです。config/deploy.rbはアプリケーションのデプロイ設定を記述する設定ファイルです。一般的にはconfig/deploy.rbに設定を記述していくことになります。

さっそく、config/deploy.rbを設定しますが、今回、scmにgit、repositroryにgithubを選択しています。
以下はこれからの話でベースとなる設定です。

# capistranoの出力をカラーにする
require 'capistrano_colors'

# capistranoで管理するためのアプリケーション名の指定
set :application, "tools"

# パスフレーズ聞かれる場合はこの設定を入れるほうがいいみたい
default_run_options[:pty] = true

# SCM(Source Code Management)を指定
set :scm, :git

# SCMのレポジトリURLを指定
set :repository,  "git://github.com/maehachi08/tools.git"

# SCMのレポジトリのブランチ名を指定
set :branch, "master"

# githubアカウント関連
# :scm_username
#  - scmレポジトリへのアクセスユーザを指定する
#    (ローカルマシンにログインしているユーザと異なる場合)
set :scm_username, "deployer"
set :scm_passphrase, "deployer"

# リモートでもローカルのssh keyを使えるようにする
#ssh_options[:forward_agent] = true

# githubからcloneするときの動作指定
#  :remote_cache
#    - capistranoサーバーでローカルgitレポジトリを保持し、
#      githubから全コピーしないのでエコな動作をする
set :deploy_via, :remote_cache

# capistranoのデプロイ先
# deploy:setupタスクでmkdir -pで作成されます
set :deploy_to, "/var/capistrano/#{application}"

# deploy:cleanupタスクで<deploy_to>/releases以下に残す世代数の指定
# デフォルトで5世代残します
set :keep_releases, 3

# gitのsubmoduleを利用している場合に設定しますが、
# 動作未検証のためコメントアウト
#set :git_enable_submodules, 1

# capistranoデプロイユーザ関連設定
set :user, "deployer"
set :password, "deployer"
set :use_sudo, true

# role
role :development, "192.168.146.10"

# cap deploy:setup実行後にdeploy_to以下のディレクトリのownerを変更します
namespace :setup do
  task :fix_permissions do
    sudo "chown -R #{user}.#{user} #{deploy_to}"
  end
end
after "deploy:setup", "setup:fix_permissions"

# cap deploy実行後に<deploy_to>/releases以下を掃除する
after "deploy", "deploy:cleanup"

Exec Capistrano task "deploy:setup"

Capistranoでは、複数のタスクが存在します。タスクについては本家のgithub wikiの『Capistrano Tasks』を参考に頂きたいと思います。

タスクの中で、deploy_pathで指定したディレクトリやCapistranoとして必要なディレクトリを作成するためのタスク『deploy:setup』を最初の1発目に実行します。この『deploy:setup』は最初の1発目だけでOKなはずです。また、ユーザ周りの設定でミスっていればここでコケるでしょう。。。特に/etc/sudoersの追加忘れとかw

『deploy:setup』タスクや後述の『deploy』タスクの詳細はソースコードを確認してください。実行結果でも分かることですが、-pオプション付きでmkdirを実行しているとか分かりますよ。

では、『deploy:setup』タスクを実行します。

$ cap deploy:setup

コマンド一発でした。作成されたディレクトリ構成をざっくり記載します。詳細は本家のgithub wikiの『2.x from the beginning』の中の『Deployment Directory Structure』という項目を参照してください。

<deploy_to> # 先述設定では/var/capistrano/toolsに該当
        |
        |--- releases # デプロイの実態は、releases以下にデプロイ実行時の
        |             # タイムスタンプディレクトリが作られて配置されます
        |
        +--- shared # Railsアプリケーションの中でログファイルやbundlerで
                 |  # 管理するgemパッケージ等、デプロイ毎に更新したくない
                 |  # ファイルが配置されます
                 |
                 |--- log 
                 |--- pids
                 +--- system

Exec Capistrano task "deploy"

プロジェクトのソースコードをgithubから取得してデプロイしてみましょう。

$ cap deploy

これもコマンド一発!!これでgithubからソースコードをデプロイできました。
先述したディレクトリ構成に少し変化があるので記載します。今回も詳細は本家のgithub wikiの『2.x from the beginning』の中の『Deployment Directory Structure』という項目を参照してください。

<deploy_to>
        |
        |--- current -> /var/capistrano/tools/releases/20130221153209
        |          # 常に最新のデプロイパス"releases/timestamp"ディレクトリへの
        |          # simlinkをcurrentとして作成します
        |
        |--- releases
        |          |--- 20130221153209 # デプロイ先の実態
        |                              # 最新のデプロイ分へのパスへcurrnetが向けられる
        |
        +--- shared # Railsアプリケーションの中でログファイルやbundlerで
                 |  # 管理するgemパッケージ等、デプロイ毎に更新したくない
                 |  # ファイルが配置されます
                 |
                 |--- log 
                 |--- pids
                 +--- system

Exec Capistrano task "deploy:cleanup"

deployタスクを実行していくと、<deploy_to>/releasesディレクトリ以下が汚くなってきます。
<deploy_to>/currentが常に最新バージョンを見ているので、release以下のことを忘れそうになりますが、放っておくと無限に増えていくのでディスクスペースを無駄に消費してしまいます。そこで、<deploy_to>/releasesディレクトリ以下を掃除するタスク『deploy:cleanup』を実行します。

$ cap deploy:cleanup

『deploy:cleanup』タスクはデフォルトで最新5リリース分を残して、それより古いリリース分を削除します。保存数を変更するにはconfig/deploy.rbに以下設定を追加します。

# <deploy_to>/releases以下に残す世代数の指定
set :keep_releases, 3

更に以下設定を追記しておくことで、deployタスク実行毎に自動的にdeploy:cleanupタスクを実行させることができますので楽できますね♪

# cap deploy実行後に<deploy_to>/releases以下を掃除する
after "deploy", "deploy:cleanup"

Use "capistrano_rsync_with_remote_cache" Extension

ところで、Capistranoサーバとアプリケーションサーバを分けてるケースって少なくないんじゃないでしょうか?
Capistranoサーバからアプリケーションサーバへのデプロイをrsyncで行うためのgemモジュールが公開されています。それが、『capistrano_rsync_with_remote_cache』です。

事前準備として、Capistranoサーバとアプリケーションサーバの間で公開鍵を通してパスフレーズなしでSSHとrsyncを実行できるようにしておきます。

$ su - deployer
$ ssh-keygen -t rsa # passphrase無しで作成
$ scp /home/deployer/.ssh/id_rsa.pub <相手のIP>:/home/deployer/.ssh/id_rsa.pub_<自分のIP>
$ cat /home/deployer/.ssh/id_rsa.pub_<相手のIP> >> /home/deployer/.ssh/authorized_keys
$ chmod 600 /home/deployer/.ssh/authorized_keys
$ rm /home/deployer/.ssh/id_rsa.pub_<相手のIP>

# /etc/hosts.allow,/etc/sysconfig/iptables等は環境に合わせて必要であれば適宜修正

capistrano_rsync_with_remote_cacheをインストールします。

$ sudo gem install capistrano_rsync_with_remote_cache

config/deploy.rbを修正、追記します。

1. ":deploy_via"オプションの変更

scmからcloneする際の動作設定を変更します。

set :deploy_via, :rsync_with_remote_cache

2. rsync関連の設定

capistrano_rsync_with_remote_cacheを使ってデプロイメントする場合、Capistranoサーバのローカルキャッシュパスとrsyncオプションの指定を行う必要があります。

set :local_cache, "/home/deployer/capistrano/cache/#{application}"
set :rsync_options, '-az --delete --delete-excluded --exclude=.git'

3. SSH関連の設定

SSH、rsyncを行う際の通信を公開鍵認証方式で、かつパスフレーズを聞かれないようにしたいと思います。

ssh_options[:port] = "22"
ssh_options[:auth_methods] = %w(publickey)
ssh_options[:keys] = %w(/home/deployer/.ssh/id_rsa)

4. デプロイメント先のIPアドレスの変更

今までは"Capistranoサーバ = デプロイメントサーバ"でしたが、デプロイメントサーバを別にするのであればroleのIPアドレスを変更する必要があります。

role :development, "192.168.146.110"

修正前後でのdiffは以下のとおりです。

--- config/deploy.rb    2013-02-26 22:24:27.796059225 +0900
+++ config/deploy.rb_for_rsync  2013-02-26 22:22:52.125907392 +0900
@@ -30,7 +30,15 @@
 #  :remote_cache
 #    - capistranoサーバーでローカルgitレポジトリを保持し、
 #      githubから全コピーしないのでエコな動作をする
-set :deploy_via, :remote_cache
+#  :rsync_with_remote_cache
+#    - rsyncでremoteサーバへcopyする
+set :deploy_via, :rsync_with_remote_cache
+set :local_cache, "/home/deployer/capistrano/cache/#{application}"
+set :rsync_options, '-az --delete --delete-excluded --exclude=.git'
+
+ssh_options[:port] = "22"
+ssh_options[:auth_methods] = %w(publickey)
+ssh_options[:keys] = %w(/home/deployer/.ssh/id_rsa)

 # capistranoのデプロイ先
 set :deploy_to, "/var/capistrano/#{application}"
@@ -48,7 +56,7 @@
 set :use_sudo, true

 # role
-role :development, "192.168.146.10"
+role :development, "192.168.146.110"

 # cap deploy:setup実行後にdeploy_to以下のディレクトリのownerを変更します
 namespace :setup do

Use "capistrano-ext" Extension

アプリケーションにしろインフラにしろ、"development" -> "staging" -> "production"と複数のステージを用意して開発するケースがあるのではないでしょうか?
『capistrano-ext』を使用することで、共通設定をconfig/deploy.rbに記述して、ステージごとの固有設定をconfig/deploy/production.rbのようなファイルパスで管理できます。

1. Install capistrano-ext

capistrano-extをインストールします。

$ sudo gem install capistrano-ext

2. make directory for stage configuration files

capistrano-extでは各ステージごとの固有設定はconfig/deployディレクトリ下に置く必要があります。

$ mkdir config/deploy

3. Configuration of config/deploy.rb

今回は"production"ステージと"staging"ステージを定義し、capコマンドでステージ名を明示しなかった場合のデフォルトステージを"staging"に設定するためのconfigを追記します。

# capistrano-ext設定
set :stages, %w(production staging)
set :default_stage, "staging"
require 'capistrano/ext/multistage'

検証している今、master branchしかなかったので、ステージごとにデプロイするディレクトリパスを変更してみます。rsync_with_remote_cacheで使用するキャッシュディレクトリとdeploy_toのパスをconfig/deploy.rbから削除します。
config/deploy.rbの修正前後でdiffを取った結果は以下のとおりです。

--- config/deploy.rb    2013-02-26 21:19:32.902878650 +0900
+++ config/deploy.rb_for_staging        2013-02-26 22:30:12.404917827 +0900
@@ -1,6 +1,11 @@
 # capistranoの出力をカラーにする
 require 'capistrano_colors'

+# capistrano-ext設定
+set :stages, %w(production staging)
+set :default_stage, "staging"
+require 'capistrano/ext/multistage'
+
 # capistranoで管理するためのアプリケーション名の指定
 set :application, "tools"

@@ -33,16 +38,12 @@
 #  :rsync_with_remote_cache
 #    - rsyncでremoteサーバへcopyする
 set :deploy_via, :rsync_with_remote_cache
-set :local_cache, "/home/deployer/capistrano/cache/#{application}"
 set :rsync_options, '-az --delete --delete-excluded --exclude=.git'

 ssh_options[:port] = "22"
 ssh_options[:auth_methods] = %w(publickey)
 ssh_options[:keys] = %w(/home/deployer/.ssh/id_rsa)

-# capistranoのデプロイ先
-set :deploy_to, "/var/capistrano/#{application}"
-
 # <deploy_to>/releases以下に残す世代数の指定
 set :keep_releases, 3

4. Configuration of config/deploy/production.rb & config/deploy/staging.rb

config/deploy.rbから削除したパス設定を各ステージ用のコンフィグファイルに追記します。

### config/deploy/production.rb
# rsync_with_remote_cache用のキャッシュファイルパス
set :local_cache, "/home/deployer/capistrano/production/cache/#{application}"

# capistranoのデプロイ先
set :deploy_to, "/var/capistrano/production/#{application}"
### config/deploy/staging.rb
# rsync_with_remote_cache用のキャッシュファイルパス
set :local_cache, "/home/deployer/capistrano/staging/cache/#{application}"

# capistranoのデプロイ先
set :deploy_to, "/var/capistrano/staging/#{application}"

5. Exec "deploy:setup" task

capコマンドにステージ名を明示することで該当ステージに対するタスクを実行できます。

$ cap staging deploy:setup
$ cap production deploy:setup

これでデプロイメント先のアプリケーションサーバのディレクトリが以下のように作成されました。

/var/capistrano
          |--- production
          |         +--- tools
          |                |--- releases
          |                +--- shared
          |                        |--- log
          |                        |--- pids
          |                        +--- system
          +--- staging
                   +--- tools
                          |--- releases
                          +--- shared
                                  |--- log
                                  |--- pids
                                  +--- system

6. Exec "deploy" task

各ステージごとにdeployタスクを実行します。

$ cap staging deploy
$ cap production deploy

Capistranoサーバ側でrsync_with_remote_cacheで使用したキャッシュファイルパスが作成されています。

/home/deployer/capistrano
                    |--- production
                    |          +--- cache
                    |                  +--- tools
                    +--- staging
                             +--- cache
                                     +--- tools

これでcapistrano-extを使って複数ステージを管理できました。
アプリケーション開発の場においては、":branch"でmasterとトピックブランチや開発用ブランチを分けたり、roleで"development"や"production"等のロール毎にサーバを分けたりすることがあるでしょうから、そのあたりの設定をconfig/deploy/*.rbに移せばいいかと思います。可読性の観点で共通設定であっても分けるかもしれません。

以上、Capistranoでした。