本シリーズ集
タイトル | |
---|---|
0 | 目標・やりたいこと |
1 | AWS編 |
2 | rails開発環境構築編 |
3 | Nginx・MySQL編 |
4 | Capistrano編 |
5 | CircleCI編 |
6 | 総集編 |
#CircleCIとは
CircleCI(version: 2.1)は、自動でテスト&デプロイを実行してくれる、所謂、『CI/CDパイプライン(継続的インテグレーション/デリバリー)』 を構築する際に使用するSaaS。
なお、利用する際には、YAML(.yml)ファイルで記述する。
また、本記事を書く際に特に参考にしたのは以下の2つ。
1. 公式サイト
https://circleci.com/docs/ja/2.0/configuration-reference/#section=configuration
2. いまさらだけどCircleCIに入門したので分かりやすくまとめてみた
他にもたくさんCircleCIの設定内容の記事が存在しているので、この記事では 登録方法・SSHの設定・設定の結果 の3段階の説明にしておきます。
CircleCIの登録
- circleciの公式サイトで『Sign up with Github』をクリック
- 『Authorize circleci』をクリック
- Githubのログイン画面が出てくるので、パスワードを入力
- ユーザ選択画面が出てくるので、自身のアカウントと結びつける。
- 自身のリポジトリ一覧が表示されるので、利用したいリポジトリの右側の『Set Up Project』をクリック
- 遷移した画面先で、『Add Config』をクリック。
- 下図のようなモーダルが開かれるので、『Proeed to New UX』をクリック
- 新しくブランチが作成されるので、master(デフォルト)ブランチにmergeする。
※ 初めはエラーが出てくるかもしれませんが無視で構わないです。
-> 登録終了
SSHの設定
まずは、circleci から AWSのEC2インスタンスへデプロイ可能にするためのSSHキー設定を行う。
設定方法は、二通り。
###① __Circleci__のプロジェクト画面から行う。
1. 下図のように、対象のProjectで 『Project Settings』をクリック
2. 『SSH Keys』をクリック
3. 下のほうにスクロールして、『Additional SSH Keys』の欄の『Add SSH Key』をクリックして、EC2インスタンスのアクセス用の公開鍵をコピペする。
###② .circleci/config.yml ファイルに記述する
こちらは、CircleCI の設定ファイルである、 .circleci/config.yml ファイルにsshのfingerprintを直接記述するパターンである。
jobs:
#... (something)
deploy:
steps:
#... (something)
- add_ssh_keys:
fingerprints: "XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX"
- run: #{デプロイ用のコマンド}
今回は、前者で設定を行ったが、セキュリティ的にはどちらでも良いと思われる。(なお、EC2のSSH鍵ファイルは必要ないので、ブランチに鍵ファイルを含めなくても良い)
#.circleci/config ファイルの編集
以下は、gitで __push or PR__が発生した際に、自動で rspec テストを行ってくれ、なおかつ対象ブランチが master であった時に、capistrano を使用したデプロイ処理を行ってくれる設定のファイルである。
version: 2.1
jobs:
build:
docker:
- image: circleci/ruby:2.7.1-node-browsers
steps:
- checkout
# 2回目以降高速に行うため、キャッシュに保存したものを取り出す。ただ、Gemfileを変更した際に前回使用したものと同じではエラーが生じるかもなので、
# ファイル名・ファイルの中身に応じてハッシュ値を算出する、 checksum 構文を利用している。
- restore_cache:
key: Sample_Cache_{{ checksum "Gemfile.lock" }}
# 初回 or Gemfile更新 時にはこのコマンドを使用して環境を整える。
- run:
name: install Gemfile
command: bundle install --path vendor/bundle
# 上記のコマンドにより生成されたディレクトリをキャッシュに保存する。ちなみに、キャッシュ名が既に有る場合は、実行されない。
- save_cache:
key: Sample_Cache_{{ checksum "Gemfile.lock" }}
paths:
- ./vendor/bundle
test:
docker:
- image: circleci/ruby:2.7.1-node-browsers
environment:
DB_HOST: 127.0.0.1
DB_PASSWORD: root
- image: circleci/mysql:5.7
environment:
MYSQL_USER: root
MYSQL_ROOT_PASSWORD: root
steps:
- checkout
- restore_cache:
key: Sample_Cache_{{ checksum "Gemfile.lock" }}
# このコマンドを実行しないと、"Your Yarn packages are out of date! Please ... " というエラーが出るので実行
- run:
name: if do not execute this, an error occurs.
command: yarn install --check-files
# DBの立ち上げを待つ。120秒経っても起動しない場合は、test ジョブを強制終了させる。
- run:
name: Wait for DB
command: dockerize -wait tcp://127.0.0.1:3306 -timeout 120s
# bundlerの path設定を vendor/bundle にする
- run:
name: setup bundler target path
command: bundle config --local path vendor/bundle
- run:
name: Set up DB
command: |
bundle exec rake db:create
bundle exec rails db:migrate
- run:
name: implement test
command: bundle exec rspec ./spec/
deploy:
docker:
- image: circleci/ruby:2.7.1-node-browsers
steps:
- checkout
- restore_cache:
key: Sample_Cache_{{ checksum "Gemfile.lock" }}
- run:
name: setup bundler target path
command: bundle config --local path vendor/bundle
- run:
name: deploy app by capistrano
command: bundle exec cap production deploy
workflows:
version: 2.1
test-deploy:
jobs:
- build
- test:
requires:
- build
- deploy:
requires:
- test
filters:
branches:
only: master
なお、他者が公開している executor / command 等 を使える orbs という機能がversion2から実装されたが、例えば(調べた限りでは)、rspec
が rspec_junit_formatter
というgemでなければならなかったりしたので、簡単に実装できるが、柔軟性があまりないため、今回は一から自分で実装した。
(例: https://circleci.com/developer/orbs/orb/circleci/ruby )
これで、全てのブランチの__push (commit) / PR__に対して、テストを行ってくれ、master ブランチが対象であった場合は、capistrano を使用したデプロイ作業を自動で行ってくれるインフラの構築が完了。
__この設定に合わせた`config/database.yml`ファイルの設定__
default: &default
adapter: mysql2
encoding: utf8
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
username: <%= ENV.fetch("DB_USER") { "root" } %>
password: <%= ENV.fetch("DB_PASSWORD") { "password" } %>
host: <%= ENV.fetch("DB_HOST") { "db" } %>
他にも__rubocop__ など、『コードが(インデントなどの)コーディング規約に則っているか』をテストしてくれるgemがあるので、実際の開発では、jobs: test: の欄に、
jobs:
test:
#(...something)
steps:
#(...something)
- run:
name: 'rubocop'
command: bundle exec rubocop #(合ってるかわからない)
と書くなどすると思われる。
デプロイ時にエラーが発生
※ Capistrano の `unless ...` 文をつける前だったので、つけたら解決。前回『必須』と説明したのはこのため。 いずれにせよ初回はローカルからデプロイすることは必須。
masterブランチにMergeされた時に、Capistranoのコマンドにより、デプロイされる設定を書いたが、エラーが発生していた。(database.ymlはgitに含まれてました笑)SSHKit::Runner::ExecuteError: Exception while executing as ec2-user@xx.xx.xx.xx: No such file or directory @ rb_file_s_stat - config/master.key
↓(再掲)原因箇所
# (...something)
on roles(:app) do
upload! "config/database.yml", "#{shared_path}/config/database-pro.yml" unless test "[ -f #{shared_path}/config/database-pro.yml"
upload! "config/master.key", "#{shared_path}/config/master-pro.key" unless test "[ -f #{shared_path}/config/master-pro.key ]"
end
end
# (...something)
Webhook設定方法
どのタイミング(commit,push 等)で、circleciが走るか(は、github側で設定できる。
対象リポジトリの『Settings』へ行き、サイドバーの『Webhooks』の蘭で、
https://circleci.com/hooks/github...
という項目ができているので、それをクリックすれば設定できる。
戸惑った点
① yaml の構文によるSyntax エラーの発生。(rubyでいう)ハッシュに対して、インデントが足りてなかった。
ダメな例
- run:
name: some name
command: some command
良い例
- run:
name: some name
command: some command
② executors 周り。 DRYに書こうとして、共通部分である
docker:
- image: circleci/ruby:2.7.1-node-browsers
environment:
DB_HOST: 127.0.0.1
DB_PASSWORD: root
を executors を使用してまとめようとしたが、結果、test
Job の mysqlのコンテナがプライマリとなり、
#!/bin/bash -eo pipefail
yarn install --check-files
/bin/bash: yarn: command not found
Exited with code exit status 127
CircleCI received exit code 127
というエラーが発生したため、実現は叶わなかった。(しかもexecutorに指定したrubyコンテナが反映されているかもわからなかった)
まとめ
今まで、会社の方では、用意されていたCircleCIを使用してテストしてもらっていたけど、今回実際に自分で構築してみて、(Capistrano と比較して)思ったほど難しくないと思った。