散々出尽くした感はあるけど、細切れの情報を継ぎ合わせて遠回りをしてしまった自分のような初心者向けに、ハマったポイントと手順をまとめてみました。(夏休みの宿題的に)
Capistranoとは
Rails(に限らず?)のデプロイを、ローカルマシンからのコマンド一発で実現してくれるツールです。
それまではFTPでアップして、サーバ上でもろもろコマンド叩いて再起動!なんて前時代的な事をやってました。
Capistranoはバージョン2と3で色々変わってしまったらしいので、情報を検索する際は"Capistrano3"で検索することをオススメ。
公式サイト
http://capistranorb.com/
環境、バージョン
- クライアント
- MacOSX Yosemite
- Ruby 2.1.5 p273
- Rails 4.1.1
- Capistrano3
- サーバ(vagrant上に立ててます)
- CentOS 6.5
- Ruby 2.1.1 p76(合わせろよ。。。)
- Rails 4.1.1
- Apache + Passenger
前提
既にクライアント、サーバ共にRailsアプリケーションが動作している環境に対し、Capistranoによるコマンド一発デプロイを実現するところまでを対象とします。
また、githubでソースが管理されていることを前提とします。
Capistrano3のインストール
Gemfileを以下のように追加します。インストール先はクライアントのみです。
group :development do
gem 'capistrano', '~> 3.1.0'
gem 'capistrano-rails', '~> 1.1'
gem 'capistrano-bundler', '~> 1.1.2'
end
インストールします。
$ bundle install
$ bundle exec cap install
デフォルトでは、以下の様なファイルが新規作成されます。
{rails_root}
┣Capfile
┣config
┃┣deploy
┃┃┣production.rb
┃┃┗staging.rb
┃┗deploy.rb
┗━lib
┗capistrano
┗tasks
今回、最低限変更が必要だったのは以下の3ファイル。
- Capfile
- config/deploy.rb
- config/deploy/staging.rb
まずはCapfileを変更します。(コメント行は省略します)
require 'capistrano/setup'
require 'capistrano/deploy'
require 'capistrano/rails' #この辺がRails特有タスク
require 'capistrano/bundler'
require 'capistrano/rails/assets'
require 'capistrano/rails/migrations'
Dir.glob('lib/capistrano/tasks/*.cap').each { |r| import r }
次にdeploy.rbを変更します。
lock '3.1.0'
set :application, 'RailsApp'
set :repo_url, 'git@github.com:hogehoge/RailsApp.git'
set :branch, 'modify'
set :deploy_to, '/var/www/RailsApp' # ★ハマりポイント1 後述します
set :scm, :git
set :log_level, :debug
set :linked_files, %w{config/database.yml config/secrets.yml}
set :linked_dirs, %w{bin log tmp/pids tmp/cache tmp/sockets vendor/bundle public/system}
set :default_stage, "staging"
set :assets_roles, [:web, :app]
namespace :deploy do
before :starting, :upload
desc "Upload database.yml"
task :upload do
on roles(:app) do |host|
if test "[ ! -d #{shared_path}/config ]"
execute "mkdir -p #{shared_path}/config"
end
# githubに上げたくないファイルを個別に更新(★ハマりポイント2 後述します)
upload!('config/database.yml', "#{shared_path}/config/database.yml")
end
end
desc 'Restart application'
task :restart do
on roles(:app), in: :sequence, wait: 5 do
# Passenger再起動のため、/tmp/restart.txtのタイムスタンプを更新
execute :touch, release_path.join('tmp/restart.txt')
end
end
after :publishing, :restart
after :restart, :clear_cache do
on roles(:web), in: :groups, limit: 3, wait: 10 do
# Here we can do anything such as:
# within release_path do
# execute :rake, 'cache:clear'
# end
end
end
end
各設定の中身は生成された初期ファイルのコメントともっと詳しい方の解説に委ねるとして、私がハマったポイントについて詳しく説明します。
ハマりポイント1:Railsのアプリケーションルートについて
元々のアプリケーションのルートが
/var/www/RailsApp/
だったとします。
前提に書いた通り、既に動いているアプリケーションをターゲットとしているので、:deploy_toにはこのアプリケーションルートを設定したくなると思います。
ここがちょっとした罠で、Capistranoでデプロイすると、アプリケーションルートは以下の様な構成になります。
/var/www/RailsApp
├─current << ここがアプリケーションルート(rails_root)
├─releases
│ ├─20150812143523 << releases以下にファイルの実体がバージョン管理される
│ └─20150812143524
├─repo
├─revisions.log
└─shared << githubで管理したくないファイルを置いておく
├─bin :linked_dirsで指定したフォルダ達
├─bundle
├─config
│ ├─database.yml :linked_filesで指定したファイル達
│ └─secrets.yml
├─log
├─public
├─tmp
└─vendor
アプリケーションルート(rails_root)が"current"に変わります。それに合わせて、ApacheのDocumentRootも修正する必要があります。
特にRailsの標準と重複するフォルダは無いため、app, bin, config等の元々存在したフォルダを削除する必要は無さそうですが、邪魔だったので私は削除してしまいました。
既に運用中だったため、フォルダ構成を変更する必要があることになかなか気が付きませんでした。Webで見つかる参考情報でも、この辺を説明しているものはほとんどありませんでした。
最初にこれを理解しておけば、その後の理解がスムーズかと思います。
ハマりポイント2:githubに公開したくないファイルの取扱い
こちらは同様の情報がWebで色々公開されているので詳しい説明は省略したいのですが、ハマりポイント1で紹介したフォルダ構成をきちんと理解しておかないと、Webの情報が理解できませんでした。
Capistranoを利用すると、実体ファイルはreleases/yyyymmddhhmiss のようなタイムスタンプ付きのフォルダに保存されるのですが、currentにシンボリックリンクが貼られるので常に最新のソースが参照されるようになっています。うまく出来てます。
Capistranoではgithubから最新のリポジトリを取得してサーバにデプロイするのが基本のようです。(他の使い方もあるんだと思いますが試していません)
その際、データベースの接続情報など、ちょっと公開できないファイルの取り扱いが問題になるかと思います。
- database.yml
- secrets.yml
また、capistranoでデプロイすると、ファイルの実体は新規フォルダに作成されますので、
- log/
- public/system/
などは一箇所で集中管理したいところです。
※public/system/ フォルダはpaperclipで画像ファイルをアップロードする際のデフォルト保存先です。別のフォルダに保存している場合は個別に設定するようにして下さい。
これを解決するための仕組みが、:linked_dirs, :linked_filesです。ここで指定したファイル、フォルダは、shared/ の下で一元管理されます。(その代わり、githubからは取得できないので、別途scpなりでサーバに保存するようにして下さい)
上のサンプルでは、uploadタスクでconfig/database.ymlだけは最新版をアップロードするようにしています。
大分脱線してしまいましたが、上記2ポイントがWebで情報を集めていてもなかなか理解できなかったポイントです。
残りの手順は追ってまとめたいと思いますが、ひとまず力尽きたので次回にしたいと思います。
認識間違ってる部分がありましたら、ご指摘頂けると嬉しいです。