※この記事はcapistrano3についてです。capistrano2.x系には対応していません。
ちょこちょこ新規開発しているのですが、毎回リモートサーバーにsshで入ってpullして手順見ながらbundleなんちゃらして・・・。
みたいなのが非常にめんどくさいので、capistranoを使ってみました。
一度覚えてしまうと楽チンなので損はないと思います!そんなに難しくないです!
今回はインストールから実際のデプロイまで順を追って説明していきたいと思います。
★やろうとしていること
- Rails4のアプリをリモートサーバーにローカルからデプロイ
- web, db, appサーバーはとりあえず同じサーバーで
- git pullとかassets:precompileとかmigrationとか自動でやりたい
- テスト的にvagrantで作った仮想環境にデプロイするまでを説明します。
★できてないこと
- unicornのhot deploy
■環境
- Ruby2.1.1(あまり今回は関係ない)
- rbenvを使っている
- vagrant(CentOS6.4)
まずcapistranoの導入です。Gemfileに書くだけなので超簡単です。
# Gemfile
group :development do
gem 'capistrano'
gem 'capistrano-rails'
gem 'capistrano-bundler'
gem 'capistrano-rbenv'
end
そしてあとはいつもどおりインストール。
$ bundle install
さて、ここまでできたらcapistranoのデフォルトファイル群を用意します。
これもコマンドひとつでできます。
$ bundle exec cap install
mkdir -p config/deploy
create config/deploy.rb
create config/deploy/staging.rb
create config/deploy/production.rb
mkdir -p lib/capistrano/tasks
Capified
こんな感じでいくつかファイルが出来たと思います。
この時オプション指定でdevelopmentを作ったりもできます。
今回はstagingを使ってvagrantにテストデプロイしていきます。
Capfile
まずはここからいきましょう。とりあえずいろいろ書かれていますが全部消しちゃっていいです。
require 'capistrano/setup'
require 'capistrano/deploy'
require 'capistrano/rails'
require 'capistrano/rails/assets'
require 'capistrano/rails/migrations'
require 'capistrano/rbenv'
require 'capistrano/bundler'
# Loads custom tasks from `lib/capistrano/tasks' if you have any defined.
Dir.glob('lib/capistrano/tasks/*.rake').each { |r| import r }
使うものをrequireしているだけです。これでOK。
config/deploy/staging.rb
次にデプロイ先の環境を設定しましょう。今回はvagrantにデプロイします。
set :stage, :staging
role :app, %{vagrant@デプロイ先IP}
role :web, %{vagrant@デプロイ先IP}
role :db, %{vagrant@デプロイ先IP}
config/deploy.rb
これがメインとなるファイルです。ここにデプロイのtaskを書いていきます。
と、その前にデプロイ後どういうディレクトリ構造になるのかというのを知っておいたほうが理解しやすいと思うので下に図示しておきます。
[vagrant@localhost ~]$ tree アプリ名/ -L 2
アプリ名/
├── current -> /home/vagrant/アプリ名/releases/20140421075958
├── releases
│ ├── 20140421062631
│ ├── 20140421064312
│ ├── 20140421064620
│ ├── 20140421074708
│ └── 20140421075958
├── repo
│ ├── FETCH_HEAD
│ ├── HEAD
│ ├── branches
│ ├── config
│ ├── description
│ ├── hooks
│ ├── info
│ ├── objects
│ ├── packed-refs
│ └── refs
├── revisions.log
└── shared
├── bin
├── bundle
└── public
17 directories, 6 files
上のように、capistranoは[current, releases, repo, shared]という4つのディレクトリを作ります。
releasesにリリースごとのバージョンが管理されていき、currentはreleasesの最新へのシンボリックリンクになっているという感じです。
その他のディレクトリについてはまた調べてみてください。
では、config/deploy.rbに戻りましてとりあえず必要最低限のところから設定していきましょう。
# config valid only for Capistrano 3.1
lock '3.1.0'
set :application, 'アプリ名'
set :repo_url, 'cloneしてくるレポジトリのURL.git'
set :branch, 'master' # デフォルトがmasterなのでこの場合書かなくてもいいです。
set :deploy_to, "/home/vagrant/アプリルートディレクトリ"
set :scm, :git # capistrano3からgitオンリーになった気がするのでいらないかも?
set :format, :pretty
set :log_level, :debug # :info or :debug
set :keep_releases, 3 # 何世代前までリリースを残しておくか
set :rbenv_type, :user
set :rbenv_ruby, '2.1.1'
set :rbenv_path, '~/.rbenv'
set :rbenv_prefix, "RBENV_ROOT=#{fetch(:rbenv_path)} RBENV_VERSION=#{fetch(:rbenv_ruby)} #{fetch(:rbenv_path)}/bin/rbenv exec"
set :rbenv_map_bins, %w{rake gem bundle ruby rails}
set :rbenv_roles, :all # default value
こんな感じでしょうか。
set :key, :valuie
のような構造になっています。
さて、いよいよデプロイ時に走らすタスクを書いていくというフェーズなのですが、
実はcapistranoはもともとデフォルトで既にいくつかのタスクを用意しています。
$ bundle exec cap -T
とやるとタスクの一覧が見れるのでみてみましょう。
cap bundler:install # Install the current Bundler environment
cap bundler:map_bins # Maps all binaries to use `bundle exec` by default
cap deploy # Deploy a new release
cap deploy:check # Check required files and directories exist
cap deploy:check:directories # Check shared and release directories exist
cap deploy:check:linked_dirs # Check directories to be linked exist in shared
cap deploy:check:linked_files # Check files to be linked exist in shared
cap deploy:check:make_linked_dirs # Check directories of files to be linked exist in shared
cap deploy:cleanup # Clean up old releases
cap deploy:cleanup_assets # Cleanup expired assets
cap deploy:cleanup_rollback # Remove and archive rolled-back release
cap deploy:compile_assets # Compile assets
cap deploy:finished # Finished
cap deploy:finishing # Finish the deployment, clean up server(s)
cap deploy:finishing_rollback # Finish the rollback, clean up server(s)
cap deploy:log_revision # Log details of the deploy
cap deploy:migrate # Runs rake db:migrate if migrations are set
cap deploy:normalize_assets # Normalize asset timestamps
cap deploy:published # Published
cap deploy:publishing # Publish the release
cap deploy:restart # Restart application
cap deploy:revert_release # Revert to previous release timestamp
cap deploy:reverted # Reverted
cap deploy:reverting # Revert server(s) to previous release
cap deploy:rollback # Rollback to previous release
cap deploy:rollback_assets # Rollback assets
cap deploy:started # Started
cap deploy:starting # Start a deployment, make sure server(s) ready
cap deploy:symlink:linked_dirs # Symlink linked directories
cap deploy:symlink:linked_files # Symlink linked files
cap deploy:symlink:release # Symlink release to current
cap deploy:symlink:shared # Symlink files and directories from shared to release
cap deploy:updated # Updated
cap deploy:updating # Update server(s) by setting up a new release
cap install # Install Capistrano, cap install STAGES=staging,production
よく見てみると、すでにgit pullやrake db:migrateやassets:precompuleも用意されています。
なのでミニマムではタスクは何も書かなくてもよいでしょう。
でもせっかくなのでDBがデプロイ時になければ作成するというタスクを追加しておきましょう。
# デプロイ前に実行する必要がある。
desc 'execute before deploy'
task :db_create do
on roles(:db) do |host|
execute "mysql -uroot -e 'CREATE DATABASE IF NOT EXISTS データベース名;'"
end
end
これはnamespace :deployの外に書いて個別に実行すると良いと思います。
いくつかタスクについて説明すると、まずnamespaceで名前空間を切ってそれぞれを管理できます。
あとはrolesというのがポロポロ出てきていると思うのですが、これは例えばroles(:db)としておくと、dbサーバーに対してのみ実行します。(dbサーバーはconfig/deploy/以下で指定したものですね。)
taskの前にdescを書くことが出来て、タスクの説明なんかも残しておけます。
デプロイ
さて、ここまでくれば後は実際にデプロイするのみです。
ローカルからdeployコマンドをうってみましょう。
$ bundle exec cap db_create
$ bundle exec cap staging deploy
うまくできましたか?本番リリース時はstagingのところをproductionにしてデプロイして下さい。
余談
僕が導入しようと思った環境はsupervisor経由でunicornを動かしていて、リリースごとにsupervisorを再起動する必要があります。
なのでsupervisorの再起動中に503がでてしまって"ゔッ!"ってなるのでcapistrano経由でunicornのホットデプロイをできないかとも
考えていたのですが、そもそもsupervisorを使ったunicornのホットデプロイのベストプラクティスがわかっておらず、
(先輩が調べてくれたのですがなかなかやっかいそう…)
そこんとこまだ完全に自動化しきれていないなー、、というのが現状です。。
ともかくcapistrano自体の導入はそれほど敷居は高くないので導入してみてはいかがでしょうか!(๑╹ڡ╹๑)