Contents
概要
Capistranoとはオープンソースの複数のサーバ上で同時にスクリプトを動かすツールである。
主にデプロイツールとして使用される。
内部的にRubyのビルドツールRakeを使用しており、デプロイにおける挙動をRakeのTask定義方法で記述する。
環境
- OS
CentOS release 6.4 (64bit)
- Ruby
ruby 2.0.0p247 (2013-06-27 revision 41674) [x86_64-linux]
- Capistrano Version
Capistrano Version: 3.0.1 (Rake Version: 10.1.0)
サーバの構築
事前準備
- wgetのインストール
yum install wget -y
- gccのインストール
yum install gcc -y
- makeのインストール
yum install make -y
- openssl-develのインストール
yum install openssl-devel -y
- zlib-develのインストール
yum install zlib-devel -y
Rubyのインストール
パッケージでインストールすると古いバージョンのRubyがインストールされてしまうが、
Capistranoの最新版ではVer1.9以降のRubyが必要なので、手動でインストールを行う。
ソースコードからインストール
- Rubyダウンロードサイトから最新版の安定版URLを調べる
- Rubyをダウンロードする
wget http://cache.ruby-lang.org/pub/ruby/2.0/ruby-2.0.0-p247.tar.gz
- 展開する
tar xzfv ruby-2.0.0-p247.tar.gz
- インストールする
cd ruby-* export CFLAGS="-O3 -m64" ./configure --prefix=/usr/local/ make | tee make.log # openssl、zlibにエラーが出ていないか確認 cat make.log | grep Failed | grep openssl cat make.log | grep Failed | grep zlib # Failed to configure XXX. It will not be installed. というメッセージが表示されると失敗 # 失敗した場合は次のコマンドをから再度やり直す # # ./configure --prefix=/usr/local/ --with-ext="openssl,zlib" # make | tee make.log # 既に古いRubyがインストールされている場合は、以下のようなエラーが出る可能性がある。 # # Directory .ext/rdoc already exists, but it looks like it isn't an RDoc directory. # # Because RDoc doesn't want to risk destroying any of your existing files, # you'll need to specify a different output directory name (using the --op <dir> # option) # # この場合は、./configureに--disable-install-rdocオプションをつけることで、エラー回避が可能。ただし、rdoc (rubyコードからドキュメントを生成するツールが使用できなくなる) make install
- 正しくインストールされたことを確認する
which ruby ruby --version
デプロイユーザの作成
デプロイを実行するユーザを作成する。
更にキーペアを生成する。
CapistranoではSSHを使用してデプロイ先のサーバにログインするが、パスワードよりキーペアを使用した認証が推奨されている。
useradd -U deploy su - deploy ssh-keygen # Enter file in which to save the key (/home/deploy/.ssh/id_rsa): に何も入力せずにエンター # Enter passphrase (empty for no passphrase): に何も入力せずにエンター # Enter same passphrase again: に何も入力せずにエンター ls .ssh/id_rsa* # 以下のキーファイルが生成されていることを確認する。 # id_rsa id_rsa.pub
- ssh使用時に初接続のホストでも確認無しに接続できるようにする
- 特定のユーザのみに適用する場合
- configファイルがないかを確認
ls ~/.ssh/config
- 以下をconfigに書く
- ~/.ssh/configがあった場合は編集して以下を追記
vi ~/.ssh/config host * StrictHostKeyChecking no
- ない場合は以下を実行
echo "host *" > ~/.ssh/config echo -e "\tStrictHostKeyChecking no" >> ~/.ssh/config
- ~/.ssh/configがあった場合は編集して以下を追記
- configファイルがないかを確認
- 全体に適用する場合
sudo vi /etc/ssh/ssh_config # 以下を追記する。 # 既に "host *" 行があれば、StrictHostKeyChecking行のみ追記。 host * StrictHostKeyChecking no
- 特定のユーザのみに適用する場合
- パスの追加
vi ~/.bashrc # 以下を追加して保存。更に実行しておく。 PATH=$PATH:/usr/local/bin
デプロイ先サーバの準備
各デプロイ先にログインし、それぞれ以下の操作を行う。
- デプロイ専用ユーザの作成
su # ユーザ名と同じグループを作成・適用 useradd -U deploy su deploy cd mkdir -m 700 ~/.ssh vi ~/.ssh/authorized_keys # 先に生成した、id_rsa.pubの中身をコピーして貼り付け。ただし、行末の "user@host" 部分は不要。 chmod 600 ~/.ssh/authorized_keys
デプロイ元サーバからログインできるか確認する場合は以下を実行
ssh -i deploy_key -l deploy <宛先ホスト>
Capistranoの構築
Capistranoのインストール
gem install capistrano # y/Nを聞かれるのでyを入力
Capistranoの設定ファイル作成
# Capistranoの設定ファイルを配置する mkdir -p <任意のパス> cd <任意のパス> cap install
※以降では
/home/deploy/capistranoにインストールしたと仮定する。
Capistranoの設定ファイル作成(自作用)
cap installするとテンプレートファイルが生成されるが、あまり役に立たないので自前でインストールを行う。
mkdir -p <任意のパス> cd <任意のパス> mkdir -p config/deploy mkdir -p lib/capistrano/tasks echo "require 'capistrano/setup'" > Capfile echo "Dir.glob('lib/capistrano/tasks/*.cap').each { |r| import r }" >> Capfile touch config/deploy.rb
Capistranoの使い方
Capistranoは主にsetコマンドやserverコマンドによるパラメータ設定と、Rake形式のタスク定義を記述しておき、
Capistranoの起動時にどの設定・タスクを採用するかを引数として与える、という使用方法をとる。
Readme
Capistranoをインストールした際に作成されるReadmeファイル。
/usr/local/lib/ruby/gems/2.0.0/gems/capistrano-3.0.1/README.md
# Capistrano [![Build Status](https://travis-ci.org/capistrano/capistrano.png?branch=v3)](https://travis-ci.org/capistrano/capistrano) [![Code Climate](https://codeclimate.com/github/capistrano/capistrano.png)](https://codeclimate.com/github/capistrano/capistrano) ## Requirements * Ruby >= 1.9 (JRuby and C-Ruby/YARV are supported) ## Installation Add this line to your application's Gemfile: ``` ruby gem 'capistrano', '~> 3.0.0' ``` And then execute: ``` sh $ bundle install ``` Capify: *make sure there's no "Capfile" or "capfile" present* ``` sh $ bundle exec cap install ``` This creates the following files: ``` ├─ Capfile ├─ config │ ├─ deploy │ │ ├─ production.rb │ │ └─ staging.rb │ └─ deploy.rb └─ lib └─ capistrano └─ tasks ``` To create different stages: ``` sh $ bundle exec cap install STAGES=local,sandbox,qa,production ``` ## Usage ``` sh $ bundle exec cap -vT $ bundle exec cap staging deploy $ bundle exec cap production deploy $ bundle exec cap production deploy --dry-run $ bundle exec cap production deploy --prereqs $ bundle exec cap production deploy --trace ``` ## Tasks ``` ruby server 'example.com', roles: [:web, :app] server 'example.org', roles: [:db, :workers] desc "Report Uptimes" task :uptime do on roles(:all) do |host| info "Host #{host} (#{host.roles.join(', ')}):\t#{capture(:uptime)}" end end ``` ## Before / After Where calling on the same task name, executed in order of inclusion ``` ruby # call an existing task before :starting, :ensure_user after :finishing, :notify # or define in block before :starting, :ensure_user do # end after :finishing, :notify do # end ``` If it makes sense for your use-case (often, that means *generating a file*) the Rake prerequisite mechanism can be used: ``` ruby desc "Create Important File" file 'important.txt' do |t| sh "touch #{t.name}" end desc "Upload Important File" task :upload => 'important.txt' do |t| on roles(:all) do upload!(t.prerequisites.first, '/tmp') end end ``` The final way to call out to other tasks is to simply `invoke()` them: ``` ruby task :one do on roles(:all) { info "One" } end task :two do invoke :one on roles(:all) { info "Two" } end ``` This method is widely used. ## Getting User Input ``` ruby desc "Ask about breakfast" task :breakfast do breakfast = ask(:breakfast, "What would you like your colleagues to get you for breakfast?") on roles(:all) do |h| execute "echo \"$(whoami) wants #{breakfast} for breakfast!\" | wall" end end ``` Perfect, who needs telephones. ## Running local tasks Local tasks can be run by replacing `on` with `run_locally` ``` ruby desc "Notify service of deployment" task :notify do run_locally do with rails_env: :development do rake 'service:notify' end end end ``` ## Console **Note:** Here be dragons. The console is very immature, but it's much more cleanly architected than previous incarnations and it'll only get better from here on in. Execute arbitrary remote commands, to use this simply add `require 'capistrano/console'` which will add the necessary tasks to your environment: ``` sh $ bundle exec cap staging console ``` Then, after setting up the server connections, this is how that might look: ``` sh $ bundle exec cap production console capistrano console - enter command to execute on production production> uptime INFO [94db8027] Running /usr/bin/env uptime on leehambley@example.com:22 DEBUG [94db8027] Command: /usr/bin/env uptime DEBUG [94db8027] 17:11:17 up 50 days, 22:31, 1 user, load average: 0.02, 0.02, 0.05 INFO [94db8027] Finished in 0.435 seconds command successful. production> who INFO [9ce34809] Running /usr/bin/env who on leehambley@example.com:22 DEBUG [9ce34809] Command: /usr/bin/env who DEBUG [9ce34809] leehambley pts/0 2013-06-13 17:11 (port-11262.pppoe.wtnet.de) INFO [9ce34809] Finished in 0.420 seconds command successful. ``` ## A word about PTYs There is a configuration option which asks the backend driver to as the remote host to assign the connection a *pty*. A *pty* is a pseudo-terminal, which in effect means *tell the backend that this is an **interactive** session*. This is normally a bad idea. Most of the differences are best explained by [this page](https://github.com/sstephenson/rbenv/wiki/Unix-shell-initialization) from the author of *rbenv*. **When Capistrano makes a connection it is a *non-login*, *non-interactive* shell. This was not an accident!** It's often used as a band aid to cure issues related to RVM and rbenv not loading login and shell initialisation scripts. In these scenarios RVM and rbenv are the tools at fault, or at least they are being used incorrectly. Whilst, especially in the case of language runtimes (Ruby, Node, Python and friends in particular) there is a temptation to run multiple versions in parallel on a single server and to switch between them using environmental variables, this is an anti-pattern, and symptomatic of bad design (i.e. you're testing a second version of Ruby in production because your company lacks the infrastructure to test this in a staging environment). ## Configuration The following variables are settable: | Variable Name | Description | Notes | |:---------------------:|----------------------------------------------------------------------|-----------------------------------------------------------------| | `:repo_url` | The URL of your Git repository | file://, https://, or ssh:// are all supported | | `:git_https_username` | The (optional) username for accessing your Git repository over HTTPS | if this is an SSH connection, this setting will have no effect. | | `:git_https_password` | The (optional) password for accessing your Git repository over HTTPS | if this is an SSH connection, this setting will have no effect. | | `:tmp_dir` | The (optional) temp directory that will be used (default: /tmp) | if you have a shared web host, this setting may need to be set (i.e. /home/user/tmp/capistrano). | __Support removed__ for following variables: | Variable Name | Description | Notes | |:---------------------:|---------------------------------------------------------------------|-----------------------------------------------------------------| | `:copy_exclude` | The (optional) array of files and/or folders excluded from deploy | Replaced by Git's native `.gitattributes`, see [#515](https://github.com/capistrano/capistrano/issues/515) for more info. | ## SSHKit [SSHKit](https://github.com/leehambley/sshkit) is the driver for SSH connections behind the scenes in Capistrano, depending how deep you dig, you might run into interfaces that come directly from SSHKit (the configuration is a good example). ## Licence The MIT License (MIT) Copyright (c) 2012-2013 Tom Clements, Lee Hambley Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
ファイル構成
cap install直後にはカレントディレクトリに以下のファイルが生成される。
+-Capfile +-config | | | +-deploy | | | | | +-production.rb | | +-staging.rb | | | +-deploy.rb | +-lib | +-capistrano | +-tasks
Capfile
起動時に読み込むファイルなどを定義する。
このファイルの初期内容は以下のとおりである。
# Load DSL and Setup Up Stages require 'capistrano/setup' # Includes default deployment tasks require 'capistrano/deploy' # Includes tasks from other gems included in your Gemfile # # For documentation on these, see for example: # # https://github.com/capistrano/rvm # https://github.com/capistrano/rbenv # https://github.com/capistrano/chruby # https://github.com/capistrano/bundler # https://github.com/capistrano/rails/tree/master/assets # https://github.com/capistrano/rails/tree/master/migrations # # require 'capistrano/rvm' # require 'capistrano/rbenv' # require 'capistrano/chruby' # require 'capistrano/bundler' # require 'capistrano/rails/assets' # require 'capistrano/rails/migrations' # Loads custom tasks from `lib/capistrano/tasks' if you have any defined. Dir.glob('lib/capistrano/tasks/*.cap').each { |r| import r }
ここでrequire構文によってインポートされているファイルは以下に存在する。
/usr/local/lib/ruby/gems/2.0.0/gems/capistrano-3.0.1/lib/require ‘capistrano/deploy’に関してはサンプルファイルであるので、このrequire文はコメントアウトしてしまってよい。
config/deploy.rb
全デプロイ先に共通のパラメータ設定を行う。
デフォルトファイルを編集しても良いが、破棄して新規に作成したほうが早い。
このファイルは中身は空でもよいが、必須である。
config/production.rb
本番環境にデプロイする際のパラメータ設定を行う。
デフォルトファイルを編集しても良いが、破棄して新規に作成したほうが早い。
config/staging.rb
ステージング環境にデプロイする際のパラメータ設定を行う。
デフォルトファイルを編集しても良いが、破棄して新規に作成したほうが早い。
lib/capistrano/tasks/
この階層に拡張子.capでタスクを定義したファイルを作成する。
設定系記法
/usr/local/lib/ruby/gems/2.0.0/gems/capistrano-3.0.1/lib/capistrano/dsl/env.rb
/usr/local/lib/ruby/gems/2.0.0/gems/capistrano-3.0.1/lib/capistrano/configuration.rb
にて多くが定義されている。
set
Capistranoは内部に連想配列を保持しており、そこに特定のキーで値を指定することで、挙動を制御できる。
このメソッドによって値を登録する。
- 使用方法
set <key(シンボル)> <値>
- keyについて
- :stage
デプロイ環境。標準インストールの場合、staging、productionが存在する。
ここで設定した環境名とファイル名は一致する必要がある。
(productionを設定する場合、production.rbファイル内で行わなければならない。) - :deploy_to
デプロイ先のパス - :release_path
- :stage
- :repo_url
- :git_http_username
- :git_http_password
- :format
- :log_level
- :default_env
- :sshkit_backend
- :pty
- :connection_timeout
- :ssh_options
- :application
- :scm
- :revision_log_message
- :branch
- :locale
- :git_environmental_variables
- :tmp_dir
- :no_old_releases
- :keep_releases
- :linked_dirs
- :linked_files
- :primary
- :rollback_timestamp
fetch
キーを指定して、setメソッドで保存した値を取り出す。
- 使用方法
fetch(<key(シンボル)>)
通常以下のように変数化して使用する
#{fetch(<key(シンボル)>)}
role
アプリケーション、データベースなど特定のデプロイグループ(ロール)に対応するデプロイサーバのリストを登録する。
認証方法や認証鍵が異なる場合は、serverメソッドで個別に登録することもできる。
- 使用方法
role <グループ名(シンボル)> <デプロイ先サーバ配列> [オプション連想配列]
デプロイ先サーバは ユーザ名@ホスト名 の書式で書く。
server
デプロイ先のサーバ情報を登録する。
- 使用方法
server <サーバ名> [サーバ情報連想配列]
サーバ情報として渡せるのは以下である。
- user
ログインユーザ名 - roles
ロールを配列として渡す - ssh_options
SSHログインする際の情報の辞書配列
- keys
認証キーファイル - auth_methods
認証方法の適用順序。
%w(publickey password)
の場合、公開鍵認証を行い、失敗した場合パスワード認証を行う。 - password
パスワード
- keys
タスク系記法
namespace
指定した文字列で名前空間を作成し、その内部での定義のスコープを名前空間内にのみ限定する。
Capistranoでは、この空間内で、タスクを定義することで、複数の同名のタスクを定義できる。
それらのタスクは
名前空間:タスク名として識別される。
名前空間をネストして使用することも可能。
※rakeで実装されているメソッド
/usr/local/lib/ruby/gems/2.0.0/gems/rake-10.1.0/lib/rake/dsl_definition.rb
- 使用方法
namespace <名前空間名(シンボル)> do タスクの定義 end
desc
定義するタスクに説明をつける。
taskメソッドの直前に呼び出す。
ここで記述した内容は cap -T コマンドで使用できるタスクの一覧表示を行った際に表示される。
descで説明を記述していないtaskでも呼び出し可能。
- 使用方法
desc <説明文> task <タスク名> do タスク定義 end
task
タスクを定義するメソッド。
- 使用方法
task <タスク名> do タスク定義 end
on [SSHKit]
指定したホストに対してSSHを開始する。
通常このメソッドを使用してタスクを定義する。
- 書式
on <ホスト名> [オプション連想配列] do end
within [SSHKit]
onメソッドブロック内で使用し、SSH接続先で作業を行うパスを指定する。
- 書式
on ~ do within <パス> do end end
invoke
タスク定義内で使用し、他のタスクを呼び出す
- 書式
invoke <タスク名>
execute
ネイティブコマンドを実行する。
コマンドの引数を可変長引数としてこのメソッドに与えることができる。
- 書式
execute <コマンド名> [<オプション>...]
capture
ネイティブコマンドを実行しつつ、出力結果を返す。
コマンドの引数を可変長引数としてこのメソッドに与えることができる。
- 書式
execute <コマンド名> [<オプション>...]
any?
setメソッドでパラメータが既に設定されているかどうか
after
特定のタスクの実行後に行うタスクを登録する
- 書式
after <前タスク>, <後タスク>
or
after <前タスク>, <後タスク> do 更に追加処理 end
capコマンド
Capistranoを使用する際のコマンド。
タスク一覧表示
cap -T
タスク起動
cap <stage> <task>
サンプル
HelloWorld
STAGING_RB=/home/deploy/capistrano/config/deploy/staging.rb echo "set :stage, :staging" > $STAGING_RB echo "role :app, %w{deploy@<デプロイ先サーバ1、デプロイ先サーバ2、、、>}" >> $STAGING_RB # 例:echo "role :app, %w{deploy@127.0.0.1}" >> $STAGING_RB TEST_CAP=/home/deploy/capistrano/lib/capistrano/tasks/test.cap echo "namespace :test do" > $TEST_CAP echo " desc 'echo Hello'" >> $TEST_CAP echo " task :hello do" >> $TEST_CAP echo " on roles :app do" >> $TEST_CAP echo " execute :echo, 'Hello'" >> $TEST_CAP echo " execute 'pwd'" >> $TEST_CAP echo " end" >> $TEST_CAP echo " end" >> $TEST_CAP echo "end" >> $TEST_CAP cap -T # test:helloが追加されていることを確認 ※cap installしたディレクトリで実行 cap staging test:hello
- 実行結果
[deploy@ip-10-120-75-224 capistrano]$ cap staging test:hello INFO [58d84244] Running /usr/bin/env echo Hello on 127.0.0.1 DEBUG [58d84244] Command: /usr/bin/env echo Hello DEBUG [58d84244] Hello INFO [58d84244] Finished in 0.168 seconds with exit status 0 (successful). INFO [feb736f2] Running /usr/bin/env pwd on 127.0.0.1 DEBUG [feb736f2] Command: /usr/bin/env pwd DEBUG [feb736f2] /home/deploy INFO [feb736f2] Finished in 0.037 seconds with exit status 0 (successful).
SVNアップデート
チェックアウトを事前に行っておく。
- config/deploy/staging.rb
set :stage, :staging role :app, %w{deploy@127.0.0.1}"
- config/deploy.rb
set :svn_checkout_path, '<SVNチェックアウトパス>'
- lib/capistrano/tasks/svn.cap
namespace :deploy do desc 'svn checkout with target path' task :svn_up do on roles :app do within fetch(:svn_checkout_path) do execute :svn, :up end end end end
特定の環境のみ実行
task :XXX do if fetch(:stage).to_s == "staging" ~~~ end end