概要
railsでアプリ作って、capistranoでAWSに自動デプロイして、circleCIでgithubへのpushごとにテストコードを走らせるようにしました。
ここまできたらやるっきゃないっしょ!ってことでcircleCIによるCD環境も構築してみました。
前回記事
この記事の続きです。事前に読んどいてね。
RailsにcircleCIを構築し、githubと連携してrubocopとrspecテストを走らせる
前提条件
・Rails5.2
・capistranoを使った自動デプロイを構築できている
・circleCIとgithubを連携させて自動テストまでは実装できている
circleCIのセッティングについては他フレームワークでも共通概念として使えたりしますが、Rails5.1(だったかな?)以降ではmaster.keyを使ったcredentials系の概念が絡んできたりするので一応Railsを前提条件に加えときました。
参考記事
CapistranoデプロイをCircleCIでやってみた
CircleCI に SSH 鍵を登録する
【CircleCI】Capistranoでデプロイする設定
手順
ざっくり手順としてはこんな感じ。
まとめていっぺんに設定すると問題の切り分けができずに地獄を見るのでこんな感じで進めました。
・circleCIにssh秘密鍵を登録して、AWSのEC2インスタンスにアクセスできるようにする
・.circleci/config.ymlを編集して、ssh接続できることを確認する
・githubへpushしたときにcapistranoデプロイを走らせる
・githubのブランチがmasterの時にだけcapistranoデプロイを走らせるように条件文を加える
circleCIでssh秘密鍵を登録する
まずcircleCIのsetting⇨projectsから自動デプロイの設定をしたいリポジトリで歯車マークをクリック
次の画面に遷移するので、左側のメニューから「SSH permission」を選んでAdd SSH Keyをクリック
HostnameとPrivateKeyを入力することになります。
次はPrivateKeyの取得です。
PrivateKeyの取得
もしかしたら僕は一番ここで迷ったかもしれません。
具体的になんのSSHキーを登録すればいいのか、どこにも書いてありませんでした。
多かったのではデプロイの時に使ったSSH Keyだよ!っていうやつ。
AWSアクセスキーか?本番環境でgithubと紐づけるために取得したやつか?どれだよってなりました・・
結論から言うと、EC2インスタンス作成時に取得したpemファイルが必要になります。
もしAWSでデプロイしているのであれば、EC2インスタンスを使用しているかと思います。
そのときにKeyPairの作成の際に取得したやつです。
そいつからPrivateKeyを取得することになります。
(デプロイしたいプロジェクトで使ってるEC2インスタンスに紐づいてるpemファイルを使ってね!)
僕の場合、このプロジェクト用のpemファイルはローカルの~/.ssh内部に格納していました。
PrivateKeyをコピーするコマンドは、ターミナルで下記を実行します。
pbcopy < ~/.ssh/hogehoge.pem
これをしてから貼り付けてみると、-----BEGIN RSA PRIVATE KEY-----と-----END RSA PRIVATE KEY-----に囲まれためちゃくちゃ長い暗号が貼り付けられるはずです。
これをcircleCIのページのPrivate Keyに貼り付けます。
Hostnameはドメイン名かIPを指定するそうです。
僕の場合は独自ドメインを取得しているのでhogehoge.comと記入しています。
成功するとcircleCIのsetting >> user >> projectname >> SSH permissionのページにFingerprintというカラムができたと思います。
ここに書いてある値をあとで使うことになります。
SSH認証がうまくいくか試してみよう
.circleci/config.ymlに以下の記述を追加してください。
fingerprintsには先ほどのページの値をいれましょう。
そして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と表示されているはずです。
- add_ssh_keys:
fingerprints:
- "XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX"
githubへpushしたときにcapistranoデプロイを走らせる
いよいよデプロイしてみます。
まず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
これでpushしてみましょう!
ちゃんとCapistranoは動こうとしてくれたでしょうか?
とりあえず動こうとしてくれれば成功です。
Rails5.2であれば、おそらくmaster.keyがないんですけどどうするんすかエラーが出るはずです。
なぜエラーが出るのか
master.keyがgithubリポジトリに存在していないからです。
おそらく、capistranoによるデプロイをセッティングしたとき、master.keyを本番環境にuploadするような記述をしたのではないでしょうか。
ですが、circleCIはgithubのソースコードベースにデプロイを行います。
しかしmaster.keyは秘匿しなければならないためgitignoreに記述している・・。
なのでcircleCI側ではmaster.keyを認知できずにどうするんすか?わかんないっすって言ってきます。
master.key対策をしよう
まず、僕の場合はcapistranoによるデプロイの際にmaster.keyを本番環境にアップロードする記述をしてました。
それをコメントアウトします。
(省略)
namespace :deploy do
task :restart do
invoke 'unicorn:restart'
end
desc 'upload master.key'
task :upload do
on roles(:app) do |_host|
execute "mkdir -p #{shared_path}/config" if test "[ ! -d #{shared_path}/config ]"
# upload!('config/master.key', "#{shared_path}/config/master.key")
end
end
before :starting, 'deploy:upload'
after :finishing, 'deploy:cleanup'
end
次にcircleCI側に環境変数を設定してあげましょう。
かなり驚いたんですが、circleCIではデプロイ時に必要になる環境変数をGUI上から設定して、config.ymlから変数を呼び出してあげれば機能するという超便利機能があるのです。
そしてrailsのmaster.key用の環境変数まで用意されているのです。作った人天才かと・・・。
定義済み環境変数もあるみたいなので軽く目を通しておきましょう
では、master.keyの設定をしていきます。
settings >> user >> project >> Environment Variablesを開きます。
ここでAdd Variableボタンを押してnameとvalueを埋めていきます。
nameはRAILS_MASTER_KEYとしてください。この名前で機能してくれます。
valueはmaster.keyをテキストエディタで開くと確認できると思います。そいつを貼り付けてください。
では、もう一度githubにpushしてみてください。
僕と似た感じでcapistranoによるデプロイを設定しているのであれば、うまくいくはずです!
githubのブランチがmasterの時にだけcapistranoデプロイを走らせるように条件文を加える
最後ですね。ここまでこればあとはもう簡単です。
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: |
if [ "${CIRCLE_BRANCH}" != "master" ]; then
exit 0
fi
bundle exec cap production deploy
これだけでブランチをきっている場合はデプロイが走らず、masterへのマージなどが関わってきた場合はデプロイが走るようになります
最終系のコード
参考までに僕のコードを載せておきます。
version: 2
jobs:
build:
docker:
- image: circleci/ruby:2.5.1-node-browsers
- image: circleci/mysql:5.6
environment:
- MYSQL_ALLOW_EMPTY_PASSWORD: 'true'
- MYSQL_ROOT_HOST: '%'
- RAILS_ENV: 'test'
- BUNDLER_VERSION: 2.0.2
working_directory: ~/repo
steps:
- checkout
# Download and cache dependencies
- restore_cache:
keys:
- v1-dependencies-{{ checksum "Gemfile.lock" }}
# fallback to using the latest cache if no exact match is found
- v1-dependencies-
- run:
name: install dependencies
command: |
gem install bundler -v 2.0.2
bundle install --jobs=4 --retry=3 --path vendor/bundle
- save_cache:
paths:
- ./vendor/bundle
key: v1-dependencies-{{ checksum "Gemfile.lock" }}
- run: mv config/database.yml.ci config/database.yml
# Database setup
- run: bundle exec rake db:create
- run: bundle exec rake db:schema:load
- run:
name: Rubocop
command: bundle exec rubocop
# run tests!
- run:
name: run tests
command: |
mkdir /tmp/test-results
TEST_FILES="$(circleci tests glob "spec/**/*_spec.rb" | \
circleci tests split --split-by=timings)"
bundle exec rspec \
--format progress \
--format RspecJunitFormatter \
--out /tmp/test-results/rspec.xml \
--format progress \
$TEST_FILES
# collect reports
- store_test_results:
path: /tmp/test-results
- store_artifacts:
path: /tmp/test-results
destination: test-results
- 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: |
if [ "${CIRCLE_BRANCH}" != "master" ]; then
exit 0
fi
bundle exec cap production deploy
# config valid only for current version of Capistrano
# capistranoのバージョンを記載。固定のバージョンを利用し続け、バージョン変更によるトラブルを防止する
lock '3.11.2'
# Capistranoのログの表示に利用する
set :application, 'hogehoge'
# どのリポジトリからアプリをpullするかを指定する
set :repo_url, 'git@github.com:piyopiyo/hogehoge.git'
# バージョンが変わっても共通で参照するディレクトリを指定
set :linked_dirs, fetch(:linked_dirs, []).push('log', 'tmp/pids', 'tmp/cache', 'tmp/sockets', 'vendor/bundle', 'public/system', 'public/uploads')
set :rbenv_type, :user
set :rbenv_ruby, '2.5.1'
# どの公開鍵を利用してデプロイするか
set :ssh_options, auth_methods: ['publickey'],
keys: ['~/.ssh/hogehoge.pem']
# プロセス番号を記載したファイルの場所
set :unicorn_pid, -> { "#{shared_path}/tmp/pids/unicorn.pid" }
# Unicornの設定ファイルの場所
set :unicorn_config_path, -> { "#{current_path}/config/unicorn.rb" }
set :keep_releases, 5
# master.key用のシンボリックリンクを追加
# set :linked_files, %w[config/master.key]
# デプロイ処理が終わった後、Unicornを再起動するための記述
after 'deploy:publishing', 'deploy:restart'
namespace :deploy do
task :restart do
invoke 'unicorn:restart'
end
desc 'upload master.key'
task :upload do
on roles(:app) do |_host|
execute "mkdir -p #{shared_path}/config" if test "[ ! -d #{shared_path}/config ]"
# upload!('config/master.key', "#{shared_path}/config/master.key")
end
end
before :starting, 'deploy:upload'
after :finishing, 'deploy:cleanup'
end
workflow
本当はworkflowというものがあって、テストの後にデプロイを走らせる、とか2つの処理を平行に走らせるとかできるみたいです。
で、色々試したんですが僕にはできなかったです・・・・。
誰か教えていただけると本当に助かります
公式のcapistranoの記述をみてみたし参考に動かしてみたんですが・・・。できない。
ていうか僕レベルでもworkflowsの記述がおかしいように見える。
自分で色々構築してみたんですが、executorと言うものを指定する必要があり、machineもしくはmacosというexecutorを指定すると有料?っぽいのです・・。