RoadMovie

write down memos or something I found about tech things

【入門】Capistrano3で自動デプロイ

※この記事はcapistrano3についてです。capistrano2.x系には対応していません。


ちょこちょこ新規開発しているのですが、毎回リモートサーバーにsshで入って
pullして手順見ながらbundleなんちゃらして・・・。
みたいなのが非常にめんどくさいので、capistranoを使ってみました。
一度覚えてしまうと楽チンなので損はないと思います!そんなに難しくないです!

今回はインストールから実際のデプロイまで順を追って説明していきたいと思います。

★やろうとしていること

  • Rails4のアプリをリモートサーバーにローカルからデプロイ
    • web, db, appサーバーはとりあえず同じサーバーで
    • git pullとかassets:precompileとかmigrationとか自動でやりたい
  • テスト的にvagrantで作った仮想環境にデプロイするまでを説明します。

★できてないこと

■環境

  • 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_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自体の導入はそれほど敷居は高くないので導入してみてはいかがでしょうか!(๑╹ڡ╹๑)