はじめに
先日、Capistranoを使って自作のポートフォリオをAWSにデプロイしたので、CircleCIと組み合わせて自動デプロイしてみました。
筆者はプログラミング学習を始めて4ヶ月ぐらいですが、一週間ほどで実装できました。
**ポートフォリオにCircleCI/CDを組み込んでみたい!**という方の参考になれば嬉しいです。
ご指摘等あれば、コメントいただければ幸いです。
前提
- Railsアプリ作成済
- CIrcleCIによる自動テストを導入済
- Capistranoを使ってAWSにデプロイ済
CircleCIとCapistranoに関しては別の記事でまとめています。
CircleCIでSystemSpec(RSpec)とRubocopを走らせる)
Capistrano + AWS(EC2) + Rails で簡単デプロイ
手順
複数の設定をする必要があるので、順を追ってやっていきましょう。
- CircleCIにssh秘密鍵を登録して、AWSのEC2インスタンスにアクセスできるようにする
- CircleCIのコンソール上で環境変数を設定する
- .circleci/config.ymlを編集して、SSH接続できることを確認する
- GithubへpushしたときにCapistranoデプロイを走らせる
- Githubのブランチがmasterの時にだけCapistranoデプロイを走らせるように設定する
1.CircleCIにSSH秘密鍵を登録
CIrcleCIのGUI上で使用するプロジェクトを選択した後に"Project settings >> SSH KEYS >> Add SSH key" を選択してHost Name
とPrivate Key
を記入します。
Host Name
はドメイン、またはIPを記述します。
筆者は独自ドメインを取得していたのでappname.com
を記述しました。
Private Key
は秘密鍵の中身を記述します、
ただ、ここで注意点が2点があるので説明します。(筆者はここで2〜3日詰まりました...)
秘密鍵を登録する時の注意点
1. ローカルからEC2にログインする際に使用する秘密鍵の中身を記述する
自作アプリを作成してAWSにデプロイまでやっていると、AWSアクセスキーやGithubとの紐付けのための秘密鍵など、複数の秘密鍵があるはずです。
そのため、どの秘密鍵を使用すればいいのかが迷うかもしれません。(筆者は迷いました...)
必要な鍵はローカルからEC2にログインする際に使用する秘密鍵です。
もし、秘密鍵を~/.sshに格納している場合、秘密鍵は以下のコマンドで一覧が確認します。
[~]$ cd ~/.ssh
[.ssh]$ ls
筆者の場合はローカルからEC2にログインする際に以下のコマンドを使用します。
[~]$ ssh appname_rsa
その場合、ターミナル上で以下のコマンドを打ち、秘密鍵の中身をコピーできます。
[~]$ pbcopy < ~/.ssh/appname_rsa
コピーした中身をPrivate Key
にペーストしましょう。
そして、記述内容の冒頭が-----BEGIN RSA PRIVATE KEY-----
であればOK
です。
もし、-----BEGIN OPENSSH PRIVATE KEY-----
であれば次の注意点に進みましょう。
2. 秘密鍵のファイル形式はPEM形式でなければならない
SSH Keyのファイル形式はOPENSSH
とPEM
があり、CircleCIに設定するSSH Keyのファイル形式はPEM
に指定されています。
ファイル形式の見分け方は秘密鍵の中身の冒頭部分でわかります。
OPENSSH
の場合:-----BEGIN OPENSSH PRIVATE KEY-----
PEM
の場合:-----BEGIN RSA PRIVATE KEY-----
もし、ローカルからEC2にログインする際に使用する秘密鍵がOPENSSH
形式だった場合は、PEM
形式の秘密鍵を作成し、EC2にログインできるように設定する必要があります。
筆者はOPENSSH
形式で作成していたので、次の手順で秘密鍵を作成しなおしました。
PEM形式の秘密鍵の作成・ログイン設定方法
まずローカルで鍵の生成を行います。
[~]$ cd .ssh
[.ssh]$ ssh-keygen -m pem
(#公開鍵を作成)
-----------------------------
Enter file in which to save the key ():appname_rsa
(#ここでファイルの名前を記述して、エンター)
Enter passphrase (empty for no passphrase):
(#何もせずそのままエンター)
Enter same passphrase again:
(#何もせずそのままエンター)
-----------------------------
[.ssh]$ ls
#「appname_rsa」と「appname_rsa.pub」が生成されたことを確認
[.ssh]$ cat appname_rsa.pub
(#鍵の中身をターミナル上に出力→ssh-rsa~~~~localまでをコピーしておく)
次にサーバー側(EC2)で先ほど作成した公開鍵を設定します。
[yuki|~]$ mkdir .ssh
[yuki|~]$ chmod 700 .ssh
[yuki|~]$ cd .ssh
[yuki|.ssh]$ vim authorized_keys
(#vimが開く)
-----------------------------
ssh-rsa sdfjerijgviodsjcIKJKJSDFJWIRJGIUVSDJFKCNZKXVNJSKDNVMJKNSFUIEJSDFNCJSKDNVJKDSNVJNVJKDSNVJKNXCMXCNMXNVMDSXCKLMKDLSMVKSDLMVKDSLMVKLCA -------@--------no-MacBook-Air.local
(#先ほどコピーした鍵の中身を貼り付け)
-----------------------------
[yuki|.ssh]$ chmod 600 authorized_keys
[yuki|.ssh]$ exit
[ec2-user|~]$ exit
完了したら、ローカルに戻って鍵をどの通信の認証時に使用するか等を設定します。
[~]$ cd .ssh
[.ssh]$ vim config
(#Vimを起動し、設定ファイルを編集する)
# 以下を追記
Host appname_rsa
Hostname EC2のElastic IP (#自分の設定に合わせて)
Port 22
User yuki (#EC2のユーザー名)
IdentityFile ~/.ssh/appname_rsa (#秘密鍵の設定)
-----------------------------
これでPEM
形式の秘密鍵を使ったSSH通信が可能になります。
ローカルで下記コマンドを入力し、実際にログインできるか試してみましょう。
ssh appname_rsa
ログインできれば、設定完了です。
ここまでできたら、注意点1を参考にしてCircleCIのPrivate Key
に秘密鍵の中身を記述して登録しましょう。
2. CircleCIのコンソール上で環境変数を設定する
CircleCIはGithubのソースコードをベースにデプロイを行います。
そのため、gitignoreに記述されているようなGithub上にpushされていないファイルは認識できません。
そしてCircleCIではコンソール上で環境変数として設定することで、そういったファイルを管理する機能があるのでそれを利用します。
CircleCIのプロジェクトの設定からEnvironment Variables
のページへ行き、Add Variable
を選択します。
そして、二つの環境変数を設定します。
Name:'RAILS_MASTER_KEY' Value: ローカルにある'master.key'の中身を記述。
Name:'PRODUCTION_SSH_KEY' Value:'~/.ssh/appname_rsa_xxxxxxxxxxxxxxx~'
PRODUCTION_SSH_KEY
のValueのappname_rsa_
の後ろには、先ほど登録したSSH Key
のHost Nameの隣に記述されているFingerprintsの:抜きの文字列を記述してください。
次に本番環境でのCapistranoのSSH接続設定を行います。
ここでは、PRODUCTION_SSH_KEY
を使ってconfig/deploy/production.rbを記述しましょう。
server 'EC2のElastic IPを記述', user: 'yuki', roles: %w[app db web]
# CircleCIのGUIで設定した環境変数を使ってSSH接続
set :ssh_options, {
keys: [ENV.fetch('PRODUCTION_SSH_KEY').to_s],
forward_agent: true,
auth_methods: %w[publickey]
}
3. SSH通信できることを確認する
.circleci/config.yml
に以下の記述を追加しましょう。
Fingerprintsには先ほど登録したSSH Key
のHost Nameの隣に記述されているのでコピペしてください。
- add_ssh_keys:
fingerprints:
- "XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX"
そしてgithubへpushしてみましょう。
実行できていれば、CircleCI側のコンソールで、Installing additional ssh keys
という処理に対して
Installed key XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX
と表示されるはずです。
4. GithubへpushしたときにCapistranoを走らせる
.circleci/config.yml
に以下の記述をします。
- add_ssh_keys:
fingerprints:
- "XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX"
- deploy:
name: Capistrano deploy
command: bundle exec cap production deploy
Githubにpushしてみましょう。
自動デプロイできるはず!...です。
5. Githubのブランチがmasterの時にだけCapistranoデプロイを走らせるように設定する
circleci/config.yml
のworkflowsの最後にfilters
追記します。
# build,test,deployを記述。
・
・
・
workflows:
version: 2
build_accept_deploy:
jobs:
- build
- test:
requires:
- build
- deploy:
requires:
- test
filters:
branches:
only: master
これでGithubのブランチがmasterの時にだけCapistranoデプロイが走るようになります!
まとめ
筆者はSSH認証の部分でかなりつまずきました笑。
同じようにつまづいている方の手助けになれば嬉しいです☺️
最後にCircleCIとCapistranoのソースコードと参考記事を載せておくのでご参考までに。
ソースコード
version: 2.1
orbs:
ruby: circleci/ruby@1.1.0
jobs:
build:
docker:
- image: circleci/ruby:2.5.1-node-browsers
environment:
BUNDLER_VERSION: 2.1.4
steps:
- checkout
- ruby/install-deps
test:
parallelism: 3
docker:
- image: circleci/ruby:2.5.1-node-browsers
environment:
DB_HOST: 127.0.0.1
RAILS_ENV: test
BUNDLER_VERSION: 2.1.4
- image: circleci/mysql:8.0
command: --default-authentication-plugin=mysql_native_password
environment:
MYSQL_ALLOW_EMPTY_PASSWORD: 'true'
MYSQL_ROOT_HOST: '%'
steps:
- checkout
- ruby/install-deps
- run: mv config/database.yml.ci config/database.yml
- run:
name: Wait for DB
command: dockerize -wait tcp://localhost:3306 -timeout 1m
- run: bundle exec rake db:create
- run: bundle exec rake db:schema:load
# Run rspec in parallel
- ruby/rspec-test
- ruby/rubocop-check
deploy:
docker:
- image: circleci/ruby:2.5.1-node-browsers
environment:
BUNDLER_VERSION: 2.1.4
steps:
- checkout
- ruby/install-deps
- add_ssh_keys:
fingerprints: "XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX"
- deploy:
name: Capistrano deploy
command: bundle exec cap production deploy
workflows:
version: 2
build_accept_deploy:
jobs:
- build
- test:
requires:
- build
- deploy:
requires:
- test
filters:
branches:
only: master
# capistranoのバージョン固定
lock '3.14.1'
# デプロイするアプリケーション名
set :application, 'golfour'
# cloneするgitのレポジトリ
set :repo_url, 'git@github.com:xxxxxx/xxxxxx.git'
# deployするブランチ。デフォルトはmasterなのでなくても可。
set :branch, 'master'
# deploy先のディレクトリ。
set :deploy_to, '/var/www/rails/appname'
# secret_base_keyを読み込ませるため追記
set :linked_files, %w[config/master.key]
# シンボリックリンクをはるファイル。
set :linked_files, fetch(:linked_files, []).push('config/database.yml', 'config/settings.yml', '.env')
# シンボリックリンクをはるフォルダ。
set :linked_dirs, fetch(:linked_dirs, []).push('log', 'tmp/pids', 'tmp/cache', 'tmp/sockets', 'vendor/bundle', 'public/system')
# 保持するバージョンの個数。過去5つまで履歴を保存。
set :keep_releases, 5
# rubyのバージョン
set :rbenv_ruby, '2.5.1'
# 出力するログのレベル。
set :log_level, :debug
namespace :deploy do
desc 'Restart application'
task :restart do
invoke 'unicorn:restart'
end
desc 'Create database'
task :db_create do
on roles(:db) do |_host|
with rails_env: fetch(:rails_env) do
within current_path do
execute :bundle, :exec, :rake, 'db:create'
end
end
end
end
desc 'Run seed'
task :seed do
on roles(:app) do
with rails_env: fetch(:rails_env) do
within current_path do
execute :bundle, :exec, :rake, 'db:seed'
end
end
end
end
after :publishing, :restart
after :restart, :clear_cache do
on roles(:web), in: :groups, limit: 3, wait: 10 do
end
end
end
server 'EC2のElastic IP', user: 'yuki', roles: %w[app db web]
# CircleCIのGUIで設定した環境変数を使ってSSH接続
set :ssh_options, {
keys: [ENV.fetch('PRODUCTION_SSH_KEY').to_s],
forward_agent: true,
auth_methods: %w[publickey]
}
参考記事
【circleCI】rails5.2/Capistrano/CICD環境によるAWSへの自動デプロイ
CircleCIでデプロイを自動化