Capistrano3でLaravelプロジェクトをデプロイしてみた

今更感はありますが、Capistrano3を使ったことがなかったので手持ちのLaravelプロジェクトで実際にリモートサーバにデプロイする仕組みを作って試してみました。

# 目次

- Capistrano3とは
- 環境構築
- Capistranoのデプロイの仕組みと設定方法
- デプロイ
- 最後に

# Capisitrano3とは
Capistrano3はRuby製のデプロイツールです。
gitで管理されたプロジェクトを本番サーバに配置する処理を簡略化してくれます。

デフォルトで基本的なデプロイ機能が備わっているため、<b>デプロイしたいレポジトリ</b>と<b>デプロイ先のサーバ情報</b>さえ組み込めば基本的な動作をするようになっています。

Capistrano3を使うことによって得られるメリットは下記の点だと思います。

1. 本番サーバへのアップロード処理をコマンド一発で完結させることができる
2. サーバ上でソースコードをリリース時の状態で階層ごとに保持し、現在アクティブな階層にシンボリックリンクを貼る仕組みを使ってるためデプロイのロールバックが容易にできる

踏み台サーバでgit pullしてrsyncを使ったシェルスクリプトを実行してデプロイ。みたいなことをしなくて済みますし、誤ってデプロイしてしまったコードをgitでrevertせずにもとに戻すことができます。

# 環境構築
まずは環境構築です。
Capistrano3を使うためにはgemをインストールする必要があります。一応プロジェクト単位で使うためにbundlerを入れます。

```
brew install bundler
```

適当に作業用ディレクトリを作りましょう。

次にCapitranoをインストールするためのGemfileを作ります。

```:Gemfile
source 'https://rubygems.org'

gem 'capistrano', '~> 3.11'
```

作成後ローカルインストールする。

```bash
bundle install --path vendor/bundle
```

```bash
bundle exec cap -v

Capistrano Version: 3.11.0 (Rake Version: 12.3.2)
```

これで使えるようになったので初期設定ファイルを生成します

```bash
bundle exec cap install
```

下記のログが出力されたらOK

```bash
mkdir -p config/deploy
create config/deploy.rb
create config/deploy/staging.rb
create config/deploy/production.rb
mkdir -p lib/capistrano/tasks
create Capfile
Capified
```

最終的にCapistranoの階層構造はこうなってます。（vendorは無視してる）

```
$ tree -I vendor                                                                                                                                         8.8s  日  4/21 16:54:11 2019
.
├── Capfile
├── Gemfile
├── Gemfile.lock
├── config
│   ├── deploy
│   │   ├── production.rb
│   │   └── staging.rb
│   └── deploy.rb
└── lib
    └── capistrano
        └── tasks
```

# Capistranoのデプロイの仕組みと設定方法
環境ができたので、デプロイを実現するための設定ファイルを見ていきます。
設定ファイルは大きく分けて3つあります。

- Capfile
- config以下のデプロイパラメータを設定するRubyファイル
- lib/capistrano/tasks以下のrakeファイル

それぞれについて説明します。

## Capfile
ここには使うgemとインポートするカスタムtaskをの設定を書きます。

基本的なデプロイを行うだけであれば特に修正は必要はない（はず）です。
デフォルトでlib/capistrano/tasks以下にあるrakeファイルはすべて読み込まれるよになっています。

最初から有効になっている設定はこれだけです。

```ruby:Capfile
# Load DSL and set up stages
require "capistrano/setup"

# Include default deployment tasks
require "capistrano/deploy"

require "capistrano/scm/git"
install_plugin Capistrano::SCM::Git

# Load custom tasks from `lib/capistrano/tasks` if you have any defined
Dir.glob("lib/capistrano/tasks/*.rake").each { |r| import r }
```

## config以下のデプロイパラメータを設定するRubyファイル
config以下にはデフォルトで3つのファイルがあります。

```bash
# 共通設定
config/deploy.rb

# ステージングだけの設定
config/deploy/staging.rb

# 本番だけの設定
config/deploy/production.rb
```

デプロイ処理に共通して必要な処理をcondig/deploy.rbに書き、環境毎に分ける必要がある情報はproduction.rbやstaging.rbに書きます。
config/deploy.rbの設定内容は環境毎の設定ファイルにより上書きが可能です。

共通設定ファイルの設定は下記のようにします。

```ruby:config/deploy.rb
lock "~> 3.11.0"

set :application, "your_app"
set :repo_url, "git@your_repository.git"
set :deploy_to, "/app_dir"

# Laravel特有
append :linked_files, ".env"
append :linked_dirs, "storage"
```

applicationにはデプロイ先のサーバ情報を入れる必要があります。
自分の環境では ~/.ssh/config に指定したHost名を記載しました。

```
Host your_app
    HostName your_ip_address
    User your_user
    Port 22
    IdentityFile ~/.ssh/your_key
```

また、linked_filesやlinked_dirsはgitで管理していないファイルやディレクトリを指定します。
一度デプロイを開始すると `/app_dir/shared`というディレクトリが作成されるので、そこに必要なものを配置しましょう。デプロイ処理の中でシンボリックリンクが作成されて利用可能になります。

環境毎のファイルが空だと何も実行されないので、下記のように設定します。

```
role :app, %w{your_app}

# ブランチを対話的に変更するなら
ask :branch, "master"
```

## lib/capistrano/tasks以下のrakeファイル
Capistranoは機能一つ一つをtaskと呼んでいて、基本的なtaskは最初から用意されています。
利用可能なtaskの一覧は下記コマンドで確認できます。

```bash
bundle exec cap -T
```

実行してみたら大量に出力されます。デフォルトではすべてを使う設定になっていないので必要に応じて呼び出すことになります。

ここで実際にデプロイした際に呼び出されるtaskを見てみます。

```bash
# デプロイ先のサーバの/tmp以下にシェルスクリプト配置
git:wrapper

# レポジトリの存在チェック
git:check

# デプロイに必要なディレクトリが無ければ作成
deploy:check:directories

# シンボリックリンクを貼るディレクトリが存在していなければつくる
deploy:check:linked_dirs

# 共通ファイルの置き場が存在していなければつくる
deploy:check:make_linked_dirs

# 指定したレポジトリをcloneする。すでに作られていたらしない
git:clone

# レポジトリの状態を最新にする
git:update

# リリース用のディレクトリを作成
# mkdir -p /app_dir/releases/20190421075955 <- リリース用のソースコードが入るディレクトリ
git:create_release

# 環境変数REVISIONに最新コミットのhashをセット
deploy:set_current_revision

# linked_filesに指定されたファイルにシンボリックリンクをはる
deploy:symlink:linked_files

# linked_dirsに指定されたディレクトリにシンボリックリンクをはる
deploy:symlink:linked_dirs

# リリース用のディレクトリにシンボリックリンクをはる(ここで公開される)
# ln -s /app_dir/releases/20190421075955 /app_dir/releases/current
# mv /app_dir/releases/current /app_dir
deploy:symlink:release

# 保持上限を超えた場合は過去のソースコード削除(rm -rf)
deploy:cleanup

# ログに書き込み
deploy:log_revision
```

これらの処理を通してCapistranoはデプロイを実現します。

毎回ソースコードを別ディレクトリに作り直すため、Laravelプロジェクトをデプロイする際には `composer install`を実行する必要があります。
そこでカスタムtaskを作成してデプロイ時に実行されるようにしました。

```ruby:lib/capistrano/tasks/before_release.rake
namespace :deploy do
  task :composer_install do
    on roles(:app) do
      execute "cd #{fetch(:release_path)};composer install"
    end
  end

  before "deploy:symlink:release", "composer_install"
end
```
このケースではtaskの実行タイミングを `deploy:symlink:release`の前に指定しています。
こうすることで必ずcomposer installを実行してからリリースされる様になります。

これでデプロイを1コマンドで実行できるようになりました。

# デプロイ
デプロイ先のサーバにssh接続できる状態にしておくとスムーズなので

```
eval `ssh-agent`
ssh-add ~/.ssh/your_key
```

としておくと良いです。

デプロイのコマンドはすごく簡単で、

```bash
# ステージング
bundle exec cap staging deploy

# 本番
bundle exec cap production deploy
```

これを実行すればOKです。

# 最後に
コマンド一発でデプロイできるうようになるとオペミスも発生しにくいので楽になる以上にも良い効果があると思います。ロールバックもできますし。
ただ、最初からこの仕組みを想定して作っておかないと、currentディレクトリ以下を後からnginxでrootに指定する必要があるので少し面倒になってしまうので、導入する時はいろいろ検討する必要がありそうです。

そもそもLaravelでやるならdeployerという選択肢がありますし、おそらくそっちのが良いとおもいます。今回は動かしてるrailsプロジェクトがなかったのでLaravelでやってみましたが、railsじゃなくても使える便利なツールだということはわかりました。