Rails5+Puma+Nginxな環境をCapistrano3でEC2にデプロイする(前編)

Rails5系で新しく開発しているウェブアプリケーションを、AWSのEC2で稼働している本番環境にCapistranoでデプロイすることになったときのお話です。これまで個人でなにかを開発する際にはRails4系でアプリケーションサーバにUnicornを採用していましたが、今回からはPumaを採用することにしたのでその手順です。前回の手順はこちらです。
[Rails4 & Unicorn & Nginx & EC2でサーバー構築](http://qiita.com/kappy0322/items/fcb195391d46dd3213e7)


## 前提
- Rails 5.0.0
- Ruby 2.4.0
- rbenv 0.4.0
- Puma
- Nginx
- Capistrano 3.7.0
- Amazon Linux AMI 2017.03.0 (HVM)


## 手順
- 1. 必要なGemを追加
- 2. Capistranoの設定を追加
- 3. デプロイ実行ユーザを作成
- 4. アプリケーション用のディレクトリを作成
- 5. 接続に必要な鍵を作成
- 6. AgentFowardを設定
- 7. 接続を確認
- 8. おもむろにデプロイしてみる


### 1. 必要なGemを追加

まずはデプロイに必要なGemを追加する。`capistrano-rbenv-vars` はPumaで環境変数を読むために使っている。

```Ruby:Gemfile
# Use Puma as the app server
gem 'puma', '~> 3.0'

group :development do
  gem 'capistrano', '3.7.0'
  gem 'capistrano-rails'
  gem 'capistrano-bundler'
  gem 'capistrano-rbenv'
  gem 'capistrano-rbenv-vars'
end
```

```
$ bundle exec bundle install
```


### 2. Capistranoの設定を追加

インストールのためのコマンドがあるので使う。コマンドを使わずに手動でファイルを作成してもよいが、今回はこちらのコマンドで生成する。

```bash
$ bundle exec cap install
```

以下のファイルがざっと生成される。 `deploy.rb` に共通の設定を書いておいて、ステージング環境と本番環境の固有の設定は `staging.rb` と `production.rb` にそれぞれ記述していく。

- Capfile
- config/deploy.rb
- config/deploy/staging.rb
- config/deploy/production.rb
- lib/capistrano/tasks

以下にデプロイを成功させるための最低限の設定を記載しておく。ほぼ初期設定のためおそらくこのままではアプリケーションはきっと動かないので、自身の環境に合わせてよきに追加、削除する。まずは **デプロイが正常に終了する** ことにゴールとして進める。

```ruby:Capfile
require "capistrano/setup"
require "capistrano/deploy"
require "capistrano/scm/git"

install_plugin Capistrano::SCM::Git

Dir.glob("lib/capistrano/tasks/*.rake").each { |r| import r }
```

```ruby:deploy.rb
lock "3.7.0"

set :application, "my-app-name"
set :repo_url, "git@github.com:kappy/my-app-name.git"

namespace :deploy do
  desc "Make sure local git is in sync with remote."
  task :confirm do
    on roles(:app) do
      puts "This stage is '#{fetch(:stage)}'. Deploying branch is '#{fetch(:branch)}'."
      puts 'Are you sure? [y/n]'
      ask :answer, 'n'
      if fetch(:answer) != 'y'
        puts 'deploy stopped'
        exit
      end
    end
  end

  desc 'Initial Deploy'
  task :initial do
    on roles(:app) do
      invoke 'deploy'
    end
  end

  before :starting, :confirm
end
```

```ruby:production.rb
server "xx.xxx.xxx.xxx", user: "deploy", roles: %w{app db web}

set :ssh_options, {
  keys: %w(~/.ssh/id_rsa),
  forward_agent: true,
  auth_methods: %w(publickey)
}
```


### 3. デプロイ実行ユーザを作成（EC2）

Capistranoでデプロイを行うために、デプロイ用のユーザを作成してデプロイ対象のEC2に対しSSHで接続できるように権限を与える。

まずは既存のユーザでログインする。

```
$ ssh -i "~/.ssh/xxxxx.pem" ec2-user@ec2-xx-xxx-xxx-xxx.ap-northeast-1.compute.amazonaws.com
```

デプロイ用のユーザを作成し、sudoコマンドが実行できるように権限を与える。 `/etc/sudoers` ファイルを編集すればよいのだが `$ sudo vi /etc/sudoers` では読み取り専用になってしまうため `$ sudo visudo` で行なっている。

```
$ sudo su -
$ useradd deploy
$ passwd deploy

$ sudo visudo
## Allow root to run any commands anywhere
root   ALL=(ALL) ALL
deploy ALL=(ALL) ALL ← この１行を追加

$ exit
```

exitしてec2-userに戻り作成したdeployユーザで入れるか確認する。

```
$ su - deploy
```


### 4. アプリケーション用のディレクトリを作成（EC2）

デプロイ対象となるアプリケーション用のディレクトリを準備する。Capistranoのデフォルト設定では `/var/www/my_app_name` となっているため、特に理由がなければこちらのパスに沿うようにする。`my_app_name` は自身のアプリ名で置き換えればよい。

```
$ pwd
/home/deploy

$ sudo mkdir /var/www && cd /var/www
$ sudo mkdir my_app_name
$ sudo chown deploy:deploy my_app_name

$ cd my_app_name && pwd
/var/www/my_app_name
```


### 5. 接続に必要な鍵を配置（EC2）

ローカルマシン（以下、ローカル）からEC2に公開鍵認証で接続できるように設定していく。ローカルで作成した公開鍵をEC2の.sshディレクトリ配下に `AuthorizedKeysFile` という名前で配置することで、公開鍵認証によってパスフレーズなしに接続できるようになる。

なお、こちらの　`AuthorizedKeysFile`　というファイル名の指定は　`/etc/ssh/sshd_config` にて設定されているが、変更する理由もないのでこちらに合わせる。

```
$ mkdir ~/.ssh
$ vi ~/.ssh/authorized_keys
```

SSHで接続された際にPermission Deniedにならないように、公開鍵のパーミッションを変更しておく。

```
$ chmod 700 ~/.ssh
$ chmod 600 ~/.ssh/authorized_keys
```

ローカルに戻り、公開鍵認証でEC2に接続できるか確認していく。


### 7. 接続を確認


鍵の登録が正しく行われていれば以下のようなコマンドで鍵の指定をせずに接続ができるようになる。パスフレーズを入力し公開鍵認証での接続が成功していることを確認する（パスフレーズを設定せず鍵の作成を行えばいいよと書いている記事があるがそれは間違い）。

```
$ ssh deploy@xx.xxx.xxx.xxx
```

Permissionの影響などで正しく接続できない場合は、接続先の `/var/log/secure` にログが落ちているはずなので確認する。

```
$ sudo tail -f /var/log/secure
```


### 8. おもむろにデプロイしてみる

ここまでで、とりあえずデプロイを実行してみる。きっと失敗するががんばってエラーを逐一解消していく。エラーログはちゃんと読みましょう。

```
$ bundle exec cap production deploy
```

以下のデプロイタスクが正常に完了すればCapistrano側の設定はひとまずおわり。

- git:wrapper
- git:check
- deploy:check:directories
- git:clone
- git:update
- git:create_release
- deploy:set_current_revision
- deploy:symlink:release
- deploy:log_revision

後半はこちらです。
[Rails5+Puma+Nginxな環境をCapistrano3でEC2にデプロイする(後編)
](http://qiita.com/kappy0322/items/3d4293b964255b774769)


## おまけ

エラーログをちゃんと読めばだいたいすぐに解決すると思うが、念のため想定されるエラーと解決方法を載せておく。

**1. 権限がなくscpできない**

```
cap aborted!
SSHKit::Runner::ExecuteError: Exception while executing as deploy@xx.xxx.xxx.xxx: scp: /tmp/git-ssh-xxx-xxx-production-xxx.sh: Permission denied
```

→ ローカルから本番環境にscpでデプロイ実行ファイルを配置しようとしたときにPermission deniedでこけてる。別のユーザ（ec2-user）などですでにscpでファイルがつくられいる状態で、デプロイユーザ（deploy）でscpしようとしたときなどに起こる。 `ls -la /tmp/` などでファイルを確認してすでに存在すれば削除する。

**2. gitが入っていない**

```
cap aborted!
SSHKit::Runner::ExecuteError: Exception while executing as deploy@xx.xxx.xxx.xxx: git exit status: 127
git stdout: Nothing written
git stderr: /usr/bin/env: git: No such file or directory
```

→ gitが入っていないよ。とおこられている。素直に入れる。
`$ sudo yum install git`

**3. 権限がなくGithubに接続できない**

```
cap aborted!
SSHKit::Runner::ExecuteError: Exception while executing as deploy@xx.xxx.xxx.xxx: git exit status: 128
git stdout: Nothing written
git stderr: Warning: Permanently added 'github.com,xxx.xx.xxx.xxx' (RSA) to the list of known hosts.
Permission denied (publickey).
fatal: Could not read from remote repository.
```

→ 権限がなくてリポジトリが見れないよ。とおこられている。正しい鍵が登録されているか、もしくは　`production.rb` のssh_optionsで設定しているAgentFowardの設定は正しいかといった点を確認する。

**4. 権限がなくディレクトリが作成できない**

```
cap aborted!
SSHKit::Runner::ExecuteError: Exception while executing as deploy@xx.xxx.xxx.xxx: mkdir exit status: 1
mkdir stdout: Nothing written
mkdir stderr: mkdir: cannot create directory ‘/var/www/my-app-name/shared’: Permission denied
mkdir: cannot create directory ‘/var/www/my-app-name/releases’: Permission denied
```

→ 権限がなくて `/var/www/my-app-name/` 配下に必要なディレクトリ（`/shared` と `/releases`）が作成できないよ。とおこられている。
`$ sudo chown deploy:deploy -R /var/www/my-app-name`

## 参考
- [インフラ初心者がNginx+PumaのRails5アプリケーションをCapistrano3でデプロイした話](http://qiita.com/nrk_baby/items/ce4780e800696beee332)
- [rails5 + puma + nginx で AWSテスト環境を構築してみる](http://sodex.hatenablog.com/entry/2017/02/05/141803)
- [EC2にSSH接続用のユーザーを作成する](http://qiita.com/nekonoprotocol/items/e1fc5c212bf6a7365f8f)
[EC2のSSH接続でPermission denied(publickey)と出た時の対処法](http://qiita.com/katsukii/items/c3df0653ac07d41fe03f#authorizedkeysfile)