Help us understand the problem. What is going on with this article?

Capistrano 3によるRails 5.2 + puma + nginxのデプロイをステップバイステップで学ぶ

このドキュメントを書いた理由

qiita内も含め、同じような事例は数多く公開されているが、残念ながらイチから学ぶ上ではほとんど参考にならなかった。

なぜなら重要なのは、最終的な作業内容や設定ファイルの中身ではないからだ。それらはソフトウェアの構成やバージョンが変われば容易に変化する。

本当に重要なのは、デプロイ作業がどんなステップで成り立っていて、各ステップで何を目的とし、そのために最低限どんな設定が必要なのか、理解することだ。

そういうわけで、自分で考えて調べて実行したので、結果をまとめることにした。

方針

以下の6ステップに分割して作業を進めた。概ね、各ステップがCapistranoの一プラグインに対応している。つまり一つずつプラグインを追加していくイメージである。

  1. ssh/gitによるファイルの配置
  2. rbenvの動作確認
  3. bundlerによるgemインストール
  4. Railsの設定
  5. pumaの起動
  6. nginx と pumaの連携

可能な限り、次の2つの指針をもって進めた。

  • デフォルト値が妥当な場合は、設定を書かない
  • 1回きりとわかっている手順は、手動で行う

デフォルト値が妥当な場合は、設定を書かない

そのステップで本当に必要な設定が、一目でわかるようにするためである。

もちろん、人によって構成によって必要な設定は異なる。異なるからこそ、他人の設定ファイルをコピペするのではなく、公式ドキュメントを読んで一つずつ妥当性を判断すべきである。全部まとめてやるのは大変だが、一度に1プラグインなら難しくない。

1回きりとわかっている手順は手動で行う

Capistranoは独自のタスクを定義できるから、ついカッコよく自動化してみたくなる。しかしそれは「早すぎる最適化」というものだ。結局今回は、カスタムタスクは作らなかった。

環境情報とドキュメント化の範囲

サーバOS: Debian 9 stretch
クライアントOS: Ubuntu 18.04.2 LTS on WSL 1

  • Ruby 2.6.3 on rbenv
  • Rails 5.2.3
  • puma 3.12.1
  • Capistrano 3.11.0
  • SQLite

各ソフトウェアのインストールには触れない。ユーザを追加したりSSH鍵をセットアップしたりといった手順にも触れない。またプラグインを追加した後のbundle installなど、自明な手順はしばしば省略する。

使用するサーバはweb/app/dbを兼ねる1台のみ、productionオンリー、しかもDBはsqlite。オモチャのようなアプリであるが、手順を学ぶ題材にはちょうど良かった。

デプロイ手順の説明

前置きが長くなった。具体的な作業に入る。Capstranoのインストールから。

Gemfile
# Use Capistrano for deployment
group :development do
  gem 'capistrano'
end

bundle installの後、cap install を実行して設定ファイルを作る。今回はproductionの分しか作らない。

bundle exec cap install STAGES=production

Capistranoの設定は、Railsアプリと共通のGemfileconfig/以下に書き込んでいく。しかし事実上、「独立したデプロイ用のプログラムを作る」と考えた方がいい。

例えばCapistrano関係の設定を変更しても、その都度 git commit / push する必要はない。Capistranoは実行時のRailsアプリとは無関係だ。

1. ssh/gitによるファイルの配置

目的

  • 素のCapistranoで、ssh/gitを用いたファイル配置のみを行う。

前提条件

  • サーバにSSH接続できること
  • 接続したユーザで配置先のディレクトリに書き込めること
  • サーバからGitリポジトリに接続できること

方針通り、最低限の設定のみ記す。必要なのはまずアプリケーションの名前と、gitリポジトリのURLと、サーバ上での配置先。

config/deploy.rb
set :application, 'myapp'
set :repo_url, 'git@github.com:myapp.git'
set :deploy_to, "/var/www/apps/myapp"

それからサーバのアドレスとユーザ名。Capistranoは実行ユーザの ~/.ssh/config を(完全ではないが)認識するので、sshコマンドで普通に接続できるなら、改めてSSH関連の設定はしなくていい。

config/deploy/production.rb
server "myserver", user: "tkyk", roles: %w{app db web}

デプロイ実行。

bundle exec cap production deploy

指定した配置先にCapistranoが管理するディレクトリ構造が作られ、gitからファイルがチェックアウトされる。サーバにログインして確認しておこう。

2. rbenvの動作確認

目的

  • 使用したいバージョンのRubyがrbenv経由で使える(Capistranoが認識する)ことを確認する。

前提条件

  • rbenvおよび使用するバージョンのRubyがインストールされていること

capistrano-rbenvプラグインを追加・有効化する。

Gemfile
group :development do
  #...
  gem 'capistrano-rbenv'
end
Capfile
require "capistrano/rbenv"

今回、rbenvはシステムレベルで(/usr/local/rbenv に)インストールしており、Rubyのバージョンは.ruby-version で指定する。この場合、設定は次のようになる。

config/deploy.rb
set :rbenv_type, :system
set :rbenv_ruby, File.read('.ruby-version').strip
set :rbenv_prefix, "RBENV_ROOT=#{fetch(:rbenv_path)} #{fetch(:rbenv_path)}/bin/rbenv exec"

rbenvの構成違いは、

  • rbenv_type
  • rbenv_ruby
  • rbenv_prefix

という3つの設定を組み合わせて対応する。たとえばrbenvをユーザレベルでインストールして、かつ.ruby-versionを使用しないなら、次のような設定になるはず(動作確認はしてない)。

config/deploy.rb
set :rbenv_type, :user
set :rbenv_ruby, '2.6.3'
set :rbenv_prefix, "RBENV_ROOT=#{fetch(:rbenv_path)} RBENV_VERSION=#{fetch(:rbenv_ruby)} #{fetch(:rbenv_path)}/bin/rbenv exec"

deployの実行。

bundle exec cap production deploy

この段階では実質的に何も実行しないが、設定を間違えたりして必要なバージョンが見つからない場合、警告が出てデプロイが中止される。

3. bundlerによるgemのインストール

目的

  • Rails他、アプリケーションが必要とするgemをbundlerでインストールする

前提条件

  • 使用するバージョンのRubyにおいてbundlerがインストールされていること

capistrano-bundlerプラグインを追加。

Gemfile
group :development do
  #...
  gem 'capistrano-bundler'
end
Capfile
require "capistrano/bundler

アプリケーションで使用するgemはリリース間で共有したいので、プラグインの標準の規約に従い、.bundle をlinked_dirsに追加する。ここに追加したディレクトリは shared ディレクトリ下に配置され、各リリースからはシンボリックリンクで参照される。

config/deploy.rb
append :linked_dirs, '.bundle'

他の設定はデフォルト値で問題なかったが、並列数だけはサーバスペックに合わせて設定すると良いと思う。デフォルトは4。

config/deploy.rb
set :bundle_jobs, 2

デプロイ実行。bundle installが走るので初回はかなり時間がかかるだろう。

bundle exec cap production deploy

4. Railsの設定

このステップはやや複雑で、おそらく構成による差異も大きいはず。

目的

  • Railsがリリース間で共有するリソースを定義する
  • データベースのmigrationを行う
  • assetコンパイルを行う

前提条件

  • 特になし

まずはcapistrano-railsプラグインを追加し、migrationおよびassetコンパイルを有効化する。

Gemfile
group :development do
  #...
  gem 'capistrano-rails'
end
Capfile
require "capistrano/rails/assets"
require "capistrano/rails/migrations"

第1の目的。railsがリリース間で共有するリソースを定義する。次のファイル・ディレクトリは、基本的にどんなRailsアプリでも該当するだろう。

config/deploy.rb
append :linked_files, "config/master.key"
append :linked_dirs, "log", "tmp/pids", "tmp/cache", "tmp/sockets"

他にも tmp/storage とか public/uploads とか、アプリケーションによって必要なものを追加する。

こうしたリソースのうち、ディレクトリは自動で作られるが、ファイルはそうではない。よってconfig/master.key は予めサーバのshared以下にコピーしておこう。

scp config/master.key myserver:/var/www/apps/myapp/shared/config/

第2の目的。データベースのmigration。

独立したデータベースサーバがあるなら、database.ymlは普通、リポジトリに追加しないはず。その場合はlinked_filesにdatabase.ymlを追加し、何らかの手段で shared/config/database.yml を設置する。

一方、今回の私のアプリではsqliteを使うため、データベースファイル *.sqlite3 自体をリリース間で共有したい。そのため、database.ymlを次のように書き換え、1段階ディレクトリ階層を深くして……

config/database.yml
production:
  <<: *default
  database: db/production/production.sqlite3

このディレクトリを共有リソースとした(database.ymlはリポジトリ内で管理する)。

config/deploy.rb
append :linked_dirs, 'db/production'

第3の目的、assetコンパイルについては、特に設定する箇所がなかったのでデフォルトのまま。

デプロイ実行。migration, assetコンパイルが実行され、諸々シンボリックが作られる。

bundle exec cap production deploy

5. pumaの起動

目的

  • pumaを起動できるようにする
  • pumaプロセスのステータスをローカルから確認できるようにする
  • デプロイ完了時にpumaプロセスを再起動させる

前提条件

  • 特になし

capistrano-pumaプラグインのインストールと有効化。もちろんpuma自体まだインストールしていないなら、それも。

Gemfile
# Rails 5.2.3でrails newしたら最初から入っていた
gem 'puma', '~> 3.11'

group :development do
  #...
  gem 'capistrano3-puma'
end
Capfile
require 'capistrano/puma'
install_plugin Capistrano::Puma

とりあえず動かしてみたいなら、プラグインに設定ファイルを自動生成してもらうのが簡単である。

bundle exec cap production puma:config

このコマンドで、サーバ上の shared/puma.rb に設定ファイルが作られる。

(すでにサーバ上にpuma gemをインストール済みなら)以下のようなコマンドで、起動や終了、ステータスを確認できる。

bundle exec cap puma:start
bundle exec cap puma:stop
bundle exec cap puma:status

デプロイ実行。完了時に再起動されることを確認しよう。

bundle exec cap production deploy

6. nginx と puma の連携

目的

  • nginxからUNIXドメインソケットを通してpumaにアクセスできるようにする

前提条件

  • nginxがインストールされていること

capistrano-pumaのnginx用プラグインを有効化。

Capfile
install_plugin Capistrano::Puma::Nginx

これまた、とりあえず動かしたいなら、設定ファイルは自動生成すると良い。

bundle exec cap production puma:nginx_config

これで /etc/nginx/sites-available/myapp_pruduction のようなパスに設定ファイルが作られ、sites-enabled からはシンボリックリンクが作られる(当然、ディレクトリへの書き込み権限が必要。もし書き込み権限を与えたくないなら rails g capistrano:nginx_puma:config でローカルに設定ファイルを生成できる)。

あとは必要に応じて設定ファイルを修整、例えばnginxのデフォルトサイト(sites-enabled/default)を削除したりして、nginxをリロードすれば完了。

sudo nginx -t && sudo systemctl reload nginx

前ステップでpumaが起動しているなら再デプロイは不要なはず。httpアクセスして動作確認しよう。

終わりに

pumaプラグインが設定ファイルを自動生成してくれるおかげで、動作させるだけならとても簡単だった。もちろん実運用においては、それら設定ファイルの管理ポリシーが必要だが。

一連の過程において唯一引っかかったのは、以下の問題だった。

capistrano3-pumaのアップデートで起きたバグ解決

結局これも、デフォルトが妥当なら上書きしない、という方針で回避できた。

(とはいえcapistrano-rbenvの場合、デフォルト値が何なのか公式ドキュメントに書いてないのが困りものである。例示されている設定はデフォルト値ではない)

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away