LoginSignup
32
33

More than 3 years have passed since last update.

CircleCI + Capistrano + AWS(EC2) + Railsで自動デプロイしてみた

Posted at

はじめに

先日、Capistranoを使って自作のポートフォリオをAWSにデプロイしたので、CircleCIと組み合わせて自動デプロイしてみました。

筆者はプログラミング学習を始めて4ヶ月ぐらいですが、一週間ほどで実装できました。

ポートフォリオにCircleCI/CDを組み込んでみたい!という方の参考になれば嬉しいです。

ご指摘等あれば、コメントいただければ幸いです。

前提

  • Railsアプリ作成済
  • CIrcleCIによる自動テストを導入済
  • Capistranoを使ってAWSにデプロイ済

CircleCIとCapistranoに関しては別の記事でまとめています。

CircleCIでSystemSpec(RSpec)とRubocopを走らせる)
Capistrano + AWS(EC2) + Rails で簡単デプロイ

手順

複数の設定をする必要があるので、順を追ってやっていきましょう。

  1. CircleCIにssh秘密鍵を登録して、AWSのEC2インスタンスにアクセスできるようにする
  2. CircleCIのコンソール上で環境変数を設定する
  3. .circleci/config.ymlを編集して、SSH接続できることを確認する
  4. GithubへpushしたときにCapistranoデプロイを走らせる
  5. Githubのブランチがmasterの時にだけCapistranoデプロイを走らせるように設定する

1.CircleCIにSSH秘密鍵を登録

CIrcleCIのGUI上で使用するプロジェクトを選択した後に"Project settings >> SSH KEYS >> Add SSH key" を選択してHost NamePrivate Keyを記入します。

Host Nameはドメイン、またはIPを記述します。
筆者は独自ドメインを取得していたのでappname.comを記述しました。

Private Keyは秘密鍵の中身を記述します、
ただ、ここで注意点が2点があるので説明します。(筆者はここで2〜3日詰まりました...)

秘密鍵を登録する時の注意点

1. ローカルからEC2にログインする際に使用する秘密鍵の中身を記述する

自作アプリを作成してAWSにデプロイまでやっていると、AWSアクセスキーやGithubとの紐付けのための秘密鍵など、複数の秘密鍵があるはずです。
そのため、どの秘密鍵を使用すればいいのかが迷うかもしれません。(筆者は迷いました...)

必要な鍵はローカルからEC2にログインする際に使用する秘密鍵です。

もし、秘密鍵を~/.sshに格納している場合、秘密鍵は以下のコマンドで一覧が確認します。

(local)
[~]$ cd ~/.ssh
[.ssh]$ ls

筆者の場合はローカルからEC2にログインする際に以下のコマンドを使用します。

(local)
[~]$ ssh appname_rsa

その場合、ターミナル上で以下のコマンドを打ち、秘密鍵の中身をコピーできます。

(local)
[~]$ pbcopy < ~/.ssh/appname_rsa

コピーした中身をPrivate Keyにペーストしましょう。

そして、記述内容の冒頭が-----BEGIN RSA PRIVATE KEY-----であればOK
です。

もし、-----BEGIN OPENSSH PRIVATE KEY-----であれば次の注意点に進みましょう。

2. 秘密鍵のファイル形式はPEM形式でなければならない

SSH Keyのファイル形式はOPENSSHPEMがあり、CircleCIに設定するSSH Keyのファイル形式はPEMに指定されています。

ファイル形式の見分け方は秘密鍵の中身の冒頭部分でわかります。
OPENSSHの場合:-----BEGIN OPENSSH PRIVATE KEY-----
PEMの場合:-----BEGIN RSA PRIVATE KEY-----

もし、ローカルからEC2にログインする際に使用する秘密鍵がOPENSSH形式だった場合は、PEM形式の秘密鍵を作成し、EC2にログインできるように設定する必要があります。

筆者はOPENSSH形式で作成していたので、次の手順で秘密鍵を作成しなおしました。

PEM形式の秘密鍵の作成・ログイン設定方法

まずローカルで鍵の生成を行います。

(local)
[~]$ 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)で先ほど作成した公開鍵を設定します。

(server)
[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

完了したら、ローカルに戻って鍵をどの通信の認証時に使用するか等を設定します。

(local)
[~]$ cd .ssh
[.ssh]$ vim config
(#Vimを起動し、設定ファイルを編集する)

# 以下を追記
Host appname_rsa
  Hostname EC2のElastic IP (#自分の設定に合わせて)
  Port 22
  User yuki (#EC2のユーザー名)
  IdentityFile ~/.ssh/appname_rsa (#秘密鍵の設定)
-----------------------------

これでPEM形式の秘密鍵を使ったSSH通信が可能になります。
ローカルで下記コマンドを入力し、実際にログインできるか試してみましょう。

(local)
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を記述しましょう。

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の隣に記述されているのでコピペしてください。

.circleci/config.yml
     - 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に以下の記述をします。

.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追記します。

.circleci/config.yml
# 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のソースコードと参考記事を載せておくのでご参考までに。

ソースコード

.circleci/config.yml
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
config/deploy.rb
# 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
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]
}

参考記事

【circleCI】rails5.2/Capistrano/CICD環境によるAWSへの自動デプロイ
CircleCIでデプロイを自動化

32
33
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
32
33