Rails5系で新しく開発しているウェブアプリケーションを、AWSのEC2で稼働している本番環境にCapistranoでデプロイすることになったときのお話です。これまで個人でなにかを開発する際にはRails4系でアプリケーションサーバにUnicornを採用していましたが、今回からはPumaを採用することにしたのでその手順です。前回の手順はこちらです。
Rails4 & Unicorn & Nginx & EC2でサーバー構築
前提
- 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)
手順
-
- 必要なGemを追加
-
- Capistranoの設定を追加
-
- デプロイ実行ユーザを作成
-
- アプリケーション用のディレクトリを作成
-
- 接続に必要な鍵を作成
-
- AgentFowardを設定
-
- 接続を確認
-
- おもむろにデプロイしてみる
1. 必要なGemを追加
まずはデプロイに必要なGemを追加する。capistrano-rbenv-vars
はPumaで環境変数を読むために使っている。
# 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の設定を追加
インストールのためのコマンドがあるので使う。コマンドを使わずに手動でファイルを作成してもよいが、今回はこちらのコマンドで生成する。
$ 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
以下にデプロイを成功させるための最低限の設定を記載しておく。ほぼ初期設定のためおそらくこのままではアプリケーションはきっと動かないので、自身の環境に合わせてよきに追加、削除する。まずは デプロイが正常に終了する ことにゴールとして進める。
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 }
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
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 ← この1行を追加
$ 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にデプロイする(後編)
おまけ
エラーログをちゃんと読めばだいたいすぐに解決すると思うが、念のため想定されるエラーと解決方法を載せておく。
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