Capistrano とは
Ruby 製の自動デプロイ (& サーバー操作) ツール。
複雑な Rails 製アプリケーションのデプロイ作業を
コマンド数行でさくっと片付けられるようになる。
Capistrano 自体の説明は以下の資料によくまとまっていた。
本記事では Vagrant を利用して、 Rails 4.2 系 + Unicorn + Nginx 環境で
Capistrano を利用してデプロイ作業をさくっとデプロイできるようになるまでの流れを
(主に自分用に) まとめた。
前提条件
- 用意する開発・本番環境
- development (開発用と同一サーバー), production (開発用と別サーバー)
- アプリケーションサーバー
- Unicorn
- Ruby のバージョン管理
- RVM
- Webサーバー
- Nginx
- 使用するデータベース
- MySQL
- git のリポジトリ管理
- GitHub
環境構築
下準備は Chef-Solo + Berkshelf で Rails 4 の開発環境を構築 を参考に。
2台分 (開発用・本番用) をVagrant で用意しておく。
プライベートIPは他の環境と別のものを割り当てる。
今回は
- 開発用サーバー (dev_server): 192.168.33.20
- 本番用サーバー (pro_server): 192.168.33.21
を割り当てた。
以下 Vagrantfile
の例
# -*- mode: ruby -*-
VAGRANTFILE_API_VERSION = "2"
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
# 全マシンで共通の設定
config.vm.box = "centos6.5"
config.vm.box_url = "https://github.com/2creatives/vagrant-centos/releases/download/v6.5.3/centos65-x86_64-20140116.box"
config.vm.provider "virtualbox" do |vb|
vb.customize ["modifyvm", :id, "--memory", "2048"]
end
# development サーバーの設定
config.vm.define :dev_server do |dev_server|
dev_server.vm.network "private_network", ip: "192.168.33.20"
end
# production サーバーの設定
config.vm.define :pro_server do |pro_server|
pro_server.vm.network "private_network", ip: "192.168.33.21"
end
end
仮想環境を2台分作成した後、Chef-Solo + Berkshelf で Rails 4 の開発環境を構築 の
手順に従い、2台それぞれに knife-solo コマンドを適用し必要なアプリケーションをインストールする。
また、本番環境に開発用環境から、また開発環境からそのマシン自身に
SSHでログイン出来るように設定する。
まずは vagrant の鍵ファイルを本番環境に転送しておく。次のコマンドをホストのマシンで実行。
$ scp -i ~/.vagrant.d/insecure_private_key ~/.vagrant.d/insecure_private_key vagrant@192.168.33.20:/home/vagrant/.ssh/
次に開発用環境にvagrant ssh dev_server で
ログインし、~/.ssh/config
を次のように設定する。
~/.ssh/config
が無ければ新規作成。
Host 192.168.33.20
HostName 192.168.33.20
IdentityFile ~/.ssh/insecure_private_key
User vagrant
Host localhost
HostName localhost
IdentityFile ~/.ssh/insecure_private_key
User vagrant
Host 192.168.33.21
HostName 192.168.33.21
IdentityFile ~/.ssh/insecure_private_key
その後、~/.ssh/config
の権限を適切な状態になるように設定する。
$ chmod 600 ~/.ssh/config
Nginx の設定
開発用、本番用のどちらも同じ作業を行う。
$ sudo mkdir -p /var/www/nginx/capistrano_sample
$ sudo chown vagrant /var/www/nginx/capistrano_sample
$ sudo cp /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/local.conf
$ sudo mv /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf.bk
$ sudo emacs /etc/nginx/conf.d/local.conf
設定ファイルを下記のように書き換える。
upstream unicorn {
server unix:/var/www/nginx/capistrano_sample/shared/tmp/sockets/unicorn.sock;
}
server {
listen 80 default_server;
server_name [server_name];
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
root /var/www/nginx/capistrano_sample;
client_max_body_size 100m;
error_page 404 /404.html;
error_page 500 502 503 504 /500.html;
try_files $uri/index.html $uri @unicorn;
location @unicorn {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_pass http://unicorn;
}
}
Nginxを再起動。
$ sudo service nginx restart
データベースの準備
本番用のサーバーに capistrano_sample, capistrano_sample_dev データベースを作成しておく。
本番用サーバーで以下の作業を行う。
$ mysql -u root -pvagrant
mysql> GRANT ALL PRIVILEGES ON client_data.* TO root@"%" IDENTIFIED BY 'vagrant' WITH GRANT OPTION;
mysql> CREATE DATABASE capistrano_sample;
mysql> CREATE DATABASE capistrano_sample_dev;
mysql> GRANT ALL ON capistrano_sample.* TO root;
mysql> GRANT ALL ON capistrano_sample_dev.* TO root;
mysql> SHOW databases;
+-----------------------+
| Database |
+-----------------------+
| information_schema |
| capistrano_sample |
| capistrano_sample_dev |
| mysql |
| performance_schema |
+-----------------------+
5 rows in set (0.00 sec)
Rails プロジェクト作成
開発用の環境にログインし、以下のコマンドを実行していく。
$ mkdir -p ~/projects/capistrano_sample
$ cd ~/projects/capistrano_sample
$ bundle init
作成されたGemfileを以下のように書き換える。
source 'https://rubygems.org'
gem 'rails', '4.2.0'
以下のコマンドを実行。
$ bundle install --path vendor/bundle
$ bundle exec rails new .
...
Overwrite /home/vagrant/projects/capistrano_sample/Gemfile? (enter "h" for help) [Ynaqdh] Y
...
Gemfile を下記のように書き換える。
source 'https://rubygems.org'
gem 'rails', '4.2.0'
gem 'mysql2', '~> 0.3.11'
gem 'sqlite3'
gem 'sass-rails', '~> 5.0'
gem 'uglifier', '>= 1.3.0'
gem 'coffee-rails', '~> 4.1.0'
gem 'therubyracer', platforms: :ruby
gem 'jquery-rails'
gem 'turbolinks'
gem 'jbuilder', '~> 2.0'
gem 'sdoc', '~> 0.4.0', group: :doc
gem 'bcrypt', '~> 3.1.7'
gem 'unicorn'
gem 'rb-readline'
group :development, :test do
gem 'byebug'
gem 'web-console', '~> 2.0'
gem 'spring'
end
group :deployment do
gem 'capistrano', '~> 3.2.1'
gem 'capistrano-rails'
gem 'rvm-capistrano'
gem 'capistrano-bundler'
gem 'capistrano3-unicorn'
end
下記コマンドを実行。
$ bundle install --path vendor/bundle
テスト用アプリ作成
config/database.yml
を下記のように編集。
default: &default
pool: 5
timeout: 5000
development:
<<: *default
adapter: mysql2
database: capistrano_sample_dev
username: root
password: vagrant
host: 192.168.33.21
encoding: utf8
production:
<<: *default
adapter: mysql2
database: capistrano_sample
username: root
password: vagrant
host: 192.168.33.21
encoding: utf8
test:
<<: *default
adapter: sqlite3
database: db/development.sqlite3
開発用サーバーで以下のコマンドを実行していく。
$ bundle exec rails generate scaffold board title:string text:string
$ bundle exec rake db:migrate RAILS_ENV=development
$ bundle exec rake db:migrate RAILS_ENV=production
config/routes.rb
を下記のように編集
# -*- coding: utf-8 -*-
Rails.application.routes.draw do
resources :boards
root "boards#index"
end
下記コマンドでサーバーを起動し、ローカルマシンのブラウザから
http://192.168.33.20:3000
が正しく表示されるかどうか確認しておく。
$ bundle exec rails server -b 0.0.0.0
Unicorn の設定
開発用サーバーで Rails のルートディレクトリ以下に、
config/unicorn/production.rb
-
config/unicorn/development.rb
を作成する。今回はどちらの環境も同じ内容で良い。
$ mkdir config/unicorn
$ emacs config/unicorn/production.rb
$ emacs config/unicorn/development.rb
# -*- coding: utf-8 -*-
worker_processes Integer(ENV["WEB_CONCURRENCY"] || 3)
timeout 15
preload_app true # 更新時ダウンタイム無し
app_path = '/var/www/nginx/capistrano_sample'
app_shared_path = "#{app_path}/shared"
working_directory "#{app_path}/current/"
listen "#{app_shared_path}/tmp/sockets/unicorn.sock"
stdout_path "#{app_shared_path}/log/unicorn.stdout.log"
stderr_path "#{app_shared_path}/log/unicorn.stderr.log"
pid "#{app_shared_path}/tmp/pids/unicorn.pid"
# ログの出力
stderr_path File.expand_path('log/unicorn.log', ENV['RAILS_ROOT'])
stdout_path File.expand_path('log/unicorn.log', ENV['RAILS_ROOT'])
before_fork do |server, worker|
Signal.trap 'TERM' do
puts 'Unicorn master intercepting TERM and sending myself QUIT instead'
Process.kill 'QUIT', Process.pid
end
defined?(ActiveRecord::Base) and
ActiveRecord::Base.connection.disconnect!
end
after_fork do |server, worker|
Signal.trap 'TERM' do
puts 'Unicorn worker intercepting TERM and doing nothing. Wait for master to send QUIT'
end
defined?(ActiveRecord::Base) and
ActiveRecord::Base.establish_connection
end
Capistrano の設定
設定ファイル作成
引き続き開発用環境の Rails の root ディレクトリで下記コマンド実行。
$ bundle exec cap install STAGES=development,production
以下のようなディレクトリ構成になる。
[rails root dir]
├─ Capfile
├─ config
│ ├─ deploy
│ │ ├─production.rb
│ │ └─staging.rb
│ └─deploy.rb
└─ lib
└─capistrano
└─tasks
Capfile の記述
以下のように記述する。
require 'capistrano/setup'
require 'capistrano/deploy'
require 'capistrano/rails'
require 'capistrano/rails/assets'
require 'capistrano/rails/migrations'
require 'rvm/capistrano'
require 'capistrano/bundler'
require 'capistrano3/unicorn'
Dir.glob('lib/capistrano/tasks/*.rake').each { |r| import r }
デプロイ設定
共通部分
共通部分の設定を config/deploy.rb
に記述する。
:repo_url の部分は、後ほど作成するGitのリポジトリURLと合わせておく。
# -*- coding: utf-8 -*-
require 'rvm/capistrano'
lock '3.2.1'
set :application, 'capistrano_sample'
set :repo_url, 'git@github.com:Salinger/capistrano_sample.git'
set :deploy_to, '/var/www/nginx/capistrano_sample'
set :default_stage, "development"
set :scm, :git
set :deploy_via, :remote_cache
set :log_level, :debug
set :pty, true # sudo に必要
# Shared に入るものを指定
set :linked_files, %w{config/database.yml config/secrets.yml}
set :linked_dirs, %w{bin log tmp/pids tmp/cache tmp/sockets bundle public/system public/assets}
# RVM
set :rvm_type, :system
set :rvm1_ruby_version, '2.1'
# Unicorn
set :unicorn_pid, "#{shared_path}/tmp/pids/unicorn.pid"
# 5回分のreleasesを保持する
set :keep_releases, 5
after 'deploy:publishing', 'deploy:restart'
namespace :deploy do
# アプリの再起動を行うタスク
desc 'Restart application'
task :restart do
on roles(:app), in: :sequence, wait: 5 do
execute :mkdir, '-p', release_path.join('tmp')
execute :touch, release_path.join('tmp/restart.txt')
end
end
# linked_files で使用するファイルをアップロードするタスク
# deployが行われる前に実行する必要がある。
desc 'upload important files'
task :upload do
on roles(:app) do |host|
execute :mkdir, '-p', "#{shared_path}/config"
upload!('config/database.yml',"#{shared_path}/config/database.yml")
upload!('config/secrets.yml',"#{shared_path}/config/secrets.yml")
end
end
# webサーバー再起動時にキャッシュを削除する
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 :rm, '-rf', release_path.join('tmp/cache')
end
end
end
# Flow の before, after のタイミングで上記タスクを実行
before :started, 'deploy:upload'
after :finishing, 'deploy:cleanup'
# Unicorn 再起動タスク
desc 'Restart application'
task :restart do
invoke 'unicorn:restart' # lib/capustrano/tasks/unicorn.cap 内処理を実行
end
end
環境別
config/deploy/development.rb
, config/deploy/production.rb
に
各環境の設定を記述する。
set :stage, :development
set :branch, 'development'
role :app, %w{vagrant@localhost}
role :web, %w{vagrant@localhost}
role :db, %w{vagrant@localhost}
set :ssh_options, {
keys: [File.expand_path('~/.ssh/insecure_private_key')],
forward_agent: true
}
set :stage, :production
set :branch, 'master'
role :app, %w{vagrant@192.168.33.21}
role :web, %w{vagrant@192.168.33.21}
role :db, %w{vagrant@192.168.33.21}
set :ssh_options, {
keys: [File.expand_path('~/.ssh/insecure_private_key')],
forward_agent: true
}
Unicorn の起動等の設定をlib/capistrano/tasks/unicorn.cap
に記述する。
namespace :unicorn do
task :environment do
set :unicorn_pid, "#{shared_path}/tmp/pids/unicorn.pid"
set :unicorn_config, "#{current_path}/config/unicorn/#{fetch(:rails_env)}.rb"
end
def start_unicorn
within current_path do
execute :bundle, :exec, :unicorn, "-c #{fetch(:unicorn_config)} -E #{fetch(:rails_env)} -D"
end
end
def stop_unicorn
execute :kill, "-s QUIT $(< #{fetch(:unicorn_pid)})"
end
def reload_unicorn
execute :kill, "-s USR2 $(< #{fetch(:unicorn_pid)})"
end
def force_stop_unicorn
execute :kill, "$(< #{fetch(:unicorn_pid)})"
end
desc "Start unicorn server"
task :start => :environment do
on roles(:app) do
start_unicorn
end
end
desc "Stop unicorn server gracefully"
task :stop => :environment do
on roles(:app) do
stop_unicorn
end
end
desc "Restart unicorn server gracefully"
task :restart => :environment do
on roles(:app) do
if test("[ -f #{fetch(:unicorn_pid)} ]")
reload_unicorn
else
start_unicorn
end
end
end
desc "Stop unicorn server immediately"
task :force_stop => :environment do
on roles(:app) do
force_stop_unicorn
end
end
end
secrets.yml の設定
下記コマンドを実行。
$ bundle exec rake secret
出力された文字列を保存しておき、secrets.yml の
production: -> secret_key_base: XXXX
の部分をその値で書き換える。
本番用サーバーの環境変数に仕込んでも良いが、今回は手間を省く。
Git 周り
ここまでで、一度 GitHub に Push しておく。
両方のサーバーでSSH公開鍵を新規に作成し GitHub に登録後、
capistrano_sample リポジトリを GitHub 側に作成する。
master と development リポジトリを作成して Push。
$ ssh-keygen -t rsa
(Enter 連打)
$ cat ~/.ssh/id_rsa.pub
XXXXX (<- これをGitHubに登録する)
以下、開発用サーバーの Rails のルートディレクトリ以下で作業。
.gitignore
ファイルを下記のように編集する。
# Ignore bundler files.
/.bundle
vendor/bundle
db/*.sqlite3
/log/*
/tmp/*
# Important configures
config/database.yml
config/config/secrets.yml
# For emacs
*~
その後、下記コマンドを実行。
$ git init
$ git add .
$ git commit -m "Initial commit."
$ git remote add origin git@github.com:[user name]/capistrano_sample.git
$ git push -u origin master
$ git checkout -b development
$ git push -u origin development
※ 注意
config/database.yml
config/config/secrets.yml
は Git で管理されないので、別途セキュアな方法・場所で管理する。
デプロイ
development 環境のデプロイ
$ export RAILS_ENV=development
$ bundle exec cap development deploy:upload # database.yml のアップロード
$ bundle exec cap development deploy:check # deploy可能か(権限のチェック等)
$ bundle exec cap development deploy
http://192.168.33.20/ にアクセスして、以下の画面が見えればOK。
production 環境のデプロイ
$ export RAILS_ENV=production
$ bundle exec cap production deploy:upload
$ bundle exec cap production deploy:check
$ bundle exec cap production deploy
http://192.168.33.21/ にアクセスして、以下の画面が見えればOK。
おわりに
- ローカルの開発環境で git clone して開発
- ローカルでデプロイして確認しつつ開発
- 動作確認取れたら本番環境のWebアプリケーションサーバにデプロイ
という流れがスムーズに行える環境が整った。
Chef-Solo + Berkshelf で Rails 4 の開発環境簡単につくれるようにしておいたため、
Unicorn & Capistrano & Nginx 周りのテストが非常に捗った。
参考資料
Capistrano3でrailsをdeployしてみる
Capistrano 3系でRails4.1のデプロイ[rbenv][rvm][ruby2.1]