【Rails】デプロイツール「Capistrano」の基本情報と実装方法

はじめに

アプリを本番環境にアップロードして誰でもアクセスできる状態にすることをデプロイと言います。デプロイで行うべきことは多岐にわたります。Railsアプリの場合で言えば、本番環境にアップロードすることはもちろんですが、Gemのインストールやマイグレーション、SprocketsやWebpackerのコンパイルなど多くのことを行う必要があります。これらの作業を毎回すべて手作業で行うのは骨が折れますし、いつもヒューマンエラーを起こさないとも限りません。Capistranoというデプロイツールを使えば面倒なデプロイ作業を自動化することができます。

本記事では、デプロイツール「Capistrano」の使い方をまとめています。

Capistranoの基本

Capistranoについて

Capistrano(カピストラーノ)とは、アプリのデプロイ作業を自動化するツールです。Rubyで作られていますが、Rubyで作られたアプリ(Railsアプリなど)以外のデプロイ作業でも使うことができます(Rubyの実行環境は必要)。

Capistranoは学習コストが高いと言われており、初学者には少し敷居が高いかもしれません。確かに、Capistranoの設定は複雑で大変な作業です。しかし、一度設定してしまえばその後の面倒なデプロイ作業がすべて自動化できるので、無理してでも導入すべきではないかと思います。

Capistranoの構成

Capistranoは大まかに分けると3つの要素で構成されています。

要素 説明
設定 アプリの情報やデプロイ先サーバーの情報などを設定します。共通のデプロイ設定と、デプロイ環境(本番環境/ステージング環境)ごとの設定があります。
タスク 前述の設定に基づいてデプロイ先サーバーで行うタスクを定義します。なお、フレームワーク固有の基本タスク(Gemのインストールやマイグレーションなど)は後述するライブラリに含まれているので、開発者が定義すべきアプリ固有のカスタムタスクはそれほど多くありません。
ライブラリ 各種セットアップやデプロイに係る基本タスク(サーバー接続やリポジトリからの取得など)、フレームワーク固有の基本タスクが含まれています。必要に応じてライブラリを追加することでデプロイ作業に組み込むことができます。

Capistranoのインストール

Gemfileに以下を追記し、bundle installを行います。Capistrano本体以外にも追加のライブラリをインストールしています。

Gemfile

group :development do
  # 以下を追記
  gem 'capistrano'
  gem 'capistrano-rbenv'
  gem 'capistrano-bundler'
  gem 'capistrano-rails'
end

なお、後述する「Capistranoの設定」セクションでCapistranoのバージョン指定を行っています。Capistranoのバージョンが上がるたびにその設定も変更する必要があるので、それが嫌だという場合はバージョンを指定してインストールしてください。

Gemfileの書き方については以下の記事を参照してください。

以下のコマンドを実行してCapistranoをインストールします。

$ bundle exec cap install

上記のコマンドを実行すると以下のファイルが作成されます。これらの設定やタスクについては「Capistranoの設定」セクションで説明します。

/
|-config/
|  |-deploy/            # 環境ごとのデプロイ設定
|  |  |-production.rb   # 本番環境のデプロイ設定
|  |  |-staging.rb      # ステージング環境のデプロイ設定
|  |
|  |-deploy.rb          # 共通のデプロイ設定
|
|-lib/
|  |-capistrano/
|     |-tasks/          # カスタムタスク
|
|-Capfile               # Capistranoの基本設定

Capistranoの実装

Capistranoはカスタマイズ性の高いデプロイツールのため、好みによっていろいろなデプロイ方法を設定することができます。今回は例として、概ね以下の流れでデプロイを行うようCapistranoを構成していきます。なお、前提としてバージョン管理システムへのアップロードは手動で行っておく必要があります。

  1. デプロイ先サーバーへSSH接続
  2. デプロイ先サーバーからバージョン管理システムへ接続
  3. バージョン管理システムから最新版のアプリケーションをクローン
  4. フレームワーク固有の基本タスク(マイグレーションなど)を実行
  5. アプリケーションサーバーの起動または再起動

Capistranoの基本設定

Capistranoの基本設定はCapfileで行います。

Capfile

# DSLの読み込みと環境ごとの設定
require "capistrano/setup"

# デフォルトタスクの読み込み
require "capistrano/deploy"

# 追加ライブラリ(Git)の読み込み
require "capistrano/scm/git"
install_plugin Capistrano::SCM::Git

# 追加ライブラリ(その他)の読み込み
require "capistrano/rbenv"
require "capistrano/bundler"
require "capistrano/rails/assets"
require "capistrano/rails/migrations"

# カスタムタスクのインポート
Dir.glob("lib/capistrano/tasks/*.rake").each { |r| import r }

基本的にCapistranoをインストールした直後の状態から変更する必要はありませんが、「Capistranoのインストール」セクションで追加のライブラリをインストールしている場合はここで読み込む設定を行います。

共通のデプロイ設定

共通のデプロイ設定はconfig/ディレクトリ配下のdeploy.rbで行います。

deploy.rb

# Capistranoのバージョン
lock "~> 3.16.0"

# アプリケーション固有の設定
set :application, "my_app_name"
set :repo_url, "git@example.com:me/my_repo.git"
set :branch, 'master'
set :deploy_to, '/var/www/my_app_name'
set :linked_files, %w(config/master.key)
set :linked_dirs, %w(log tmp/pids tmp/cache tmp/sockets vendor/bundle public/system)
set :keep_releases, 5
set :rbenv_ruby, '2.6.3'
set :log_level, :debug
set :default_env, {
  rbenv_root: "/home/user/.rbenv",
  path: "/home/user/.rbenv/bin:$PATH"
}

lockでCapistranoのバージョンを固定します。インストールされているCapistranoのバージョンはGemfile.lockで確認できます。バージョン指定にはGemfileと同じ方法を使うことができます。

setで設定した値はfetchで取得します(タスクで使用)。今回設定している項目は以下の通りです。

設定 説明
:application デプロイするアプリの名称。
:repo_url バージョン管理システムのリポジトリURL。
:branch リポジトリのブランチ。デフォルトはmaster
:deploy_to デプロイ先のディレクトリ。
:linked_files シンポリックリンクを貼るファイル。
:linked_dirs シンポリックリンクを貼るディレクトリ。
:keep_releases, 保持するリリースバージョンの数。
:rbenv_ruby デプロイ先サーバーにインストールされているRubyバージョン。
:log_level 出力するログレベル。
:default_env デプロイ先サーバーに設定する環境変数。

:linked_filesでは、バージョン管理システムへはアップロードされないがデプロイ先サーバーには必要なファイルを設定します。今回はCredentialsを使用しているのでconfig/master.keyを設定しています。

環境ごとの設定

環境ごとの設定はconfig/deploy/ディレクトリ配下のファイルで行います。Capistranoのインストール時に本番環境とステージング環境の設定ファイルが作成されますが、今回はステージング環境にデプロイは行わないのでstaging.rbは削除します。

production.rb

server 'xxx.xxx.xxx.xxx',
  user: 'xxxxx',
  roles: %w{app db web},
  port: xxxxx,
  ssh_options: {
    keys: '~/.ssh/id_rsa'
  }

rolesはタスクを実行するための権限を設定します。タスクには実行権限を設定することができます。ここで設定した権限以外がタスクに設定されている場合、そのタスクは実行されません。

サーバーへの接続にSSHを使用している場合、ssh_optionsでローカルにある鍵ファイルのパスを設定します。また、SSHで使用するポート番号を変更している場合はportで設定します(デフォルトは22)。

カスタムタスク

追加ライブラリに含まれるフレームワーク固有の基本タスク以外に実行すべきタスクがある場合はカスタムタスクを作成します。lib/capistrano/tasks/ディレクトリ配下にcustom.rakeを作成し、以下を記述します。

custom.rake

after 'deploy:publishing', 'deploy:restart'

namespace :deploy do
  desc 'Restart unicorn server'
  task :restart do
    on roles(:app) do
      invoke 'unicorn:restart'
    end
  end
end

一行目のafterはタスクが実行される順番を設定しています。deploy:publishingが実行されてからdeploy:restartを実行するようにしています。ちなみにdeploy:publishingはライブラリに含まれる基本タスクのひとつです。beforeafterは名前空間ブロックの中で書くこともでき、その場合名前空間を省略してタスクを指定することができます。

namespace :deploy do
  after 'publishing', 'restart'
end

on rolesでタスクの実行権限を設定しています。deploy:restart:appという権限を設定していなければ実行されません。

invokeunicorn:restartunicornという名前空間にあるrestartというタスク)を呼び出しています。このタスクはまだ未定義なので、これから作成していきます。lib/capistrano/tasks/ディレクトリ配下にunicorn.rakeを作成し、以下を記述します。

unicorn.rake

namespace :unicorn do
  task :environment do
    set :unicorn_pid,    "#{current_path}/tmp/pids/unicorn.pid"
    set :unicorn_config, "#{current_path}/config/unicorn.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 restart_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)} ]")
        restart_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

deploy:restartから呼び出されたunicorn:restartでは、Unicornの起動状態を調べ、起動している場合は再起動し、起動していない場合は起動します。その他のタスク(Unicornの開始や停止)について詳しくは以下の記事を参照してください。

Capistranoの実行

Capistranoの各種設定やタスク定義ができたら、以下のコマンドを実行してデプロイを行います(注意:本記事の内容だけではUnicornの設定を行っていないのでコマンドは失敗します)。

$ bundle exec cap production deploy [--dry-run]

capコマンドの第1引数はデプロイ環境を指定します。config/deploy/ディレクトリ配下にある設定ファイルの名前(拡張子を除く)を指定します。

第2引数は名前空間を指定します。指定した名前空間に含まれるすべてのタスクが実行されます。デフォルトタスクの名前空間はdeployなので、通常はこれを指定します。また、特定のタスクのみを実行することもできます。以下はデプロイ先サーバーでUnicornの再起動を実行しています。

$ bundle exec cap production unicorn:restart

以下のコマンドを実行すると、実行可能なタスクの一覧を確認することができます。

bundle exec cap -T

--dry-runオプションをつけると実際のデプロイは行われず、各種設定やタスクが正常に動作するかを確認できます。

今回はデプロイ先サーバーへの接続にSSHを使用しているので、上記のコマンドを実行した後にSSH接続のパスワードの入力を求められます。パスワードを入力するとデプロイタスクが続々と行われていきます。

Capistranoの完全な実行ログはlog/ディレクトリ配下のcapistrano.logで確認できます。デプロイ中にエラーが起こった場合はまずこのファイルを見てエラー内容を把握しましょう。

まとめ

Capistranoはよく学習コストが高いと言われます。しかし、デプロイに必要なタスクはライブラリで用意されていることが多いので、自分で定義するタスクの量はそれほど多くありません。記述すべきファイルが多くて大変そうだと思うかもしれませんが、実は設定もタスクもひとつのファイルにまとめてしまうこともできます。ただし、その場合はメンテナンス性が落ちるので、ファイルはわかりやすい単位で分けることをおすすめします。

本記事を参考にして、Capistranoの使い方を覚えていただければと思います。

関連記事

【Rails】Paranoiaを使用した論理削除(ソフトデリート)
# はじめに Paranoiaは、Railsアプリケーションで論理削除(ソフトデリート)を実現するためのGemです。 論理削除は、データベースのレコードを物理的に削除するのではなく、削除フラグを設定することで「削除済み」とみなす方法です。こ [...]
2024年7月20日 21:33
【Rails】activerecord-multi-tenantを使用したマルチテナントアプリケーションの作成
# はじめに マルチテナントアプリケーションでは、複数の顧客(テナント)が同じアプリケーションを利用するため、データの分離が必要です。 activerecord-multi-tenantは、このようなマルチテナント環境をサポートするための便 [...]
2024年7月18日 16:50
【Rails】RubyとRailsにおけるattr_reader, attr_writer, attr_accessorの概念と使用方法
# はじめに RubyとRailsの開発において、`attr_reader`,`attr_writer`,`attr_accessor`は非常に便利なメソッドです。これらは、クラス内でインスタンス変数に対するゲッターおよびセッターメソッドを簡単に [...]
2024年7月17日 18:11
【Rails】RubyとRailsにおけるyieldの概念と使用方法
# はじめに RubyとRailsにおける`yield`は、メソッドやテンプレートの中で動的にコードブロックを実行する能力を提供し、これによってコードの再利用性と拡張性が大幅に向上します。本記事では、RubyとRailsにおける`yield`の概 [...]
2024年7月17日 13:15
【Rails】AASMを使用してオブジェクトの状態遷移を効率的に管理
# はじめに Railsアプリケーションにおいて、オブジェクトの状態管理は重要な課題の一つです。AASM (Acts As State Machine) gemは、複雑な状態遷移を効率的に管理します。本記事では、AASMの基本的な使い方を解説して [...]
2024年7月16日 18:00
【Rails】RSpec + Swagger + rswagでアプリケーションのAPIをテストおよびドキュメント化する方法
# はじめに Railsアプリケーションの開発において、APIのテストとドキュメント化は重要な要素です。 RSpecはテストフレームワークとして広く利用されており、SwaggerはAPIの設計とドキュメント化を支援します。これらを統合するr [...]
2024年7月16日 14:27
【Rails】mailcatcherを使用して開発環境でメール送信をテストする方法
# はじめに mailcatcherは、開発環境でのメール送信をキャプチャするためのツールです。ローカルで送信されたメールをブラウザ上で簡単に確認できるようにします。mailcatcherをRailsアプリケーションで使用する方法について説明しま [...]
2024年7月15日 16:37
【Rails】impressionistを使用してページビューやクリック数を追跡する方法
# はじめに impressionist Gemを使用してRailsアプリケーションでページビューやクリック数を追跡する方法について説明します。 # 実装方法 ## impressionist Gemのインストール まず、impre [...]
2024年7月15日 14:18