##達成したいもの
master、staging、developブランチがあるとして
masaterへマージ・・・・・自動テスト実行→本番環境へ自動デプロイ
staginへマージ・・・・・・自動テスト実行→検証環境へ自動デプロイ
develpへプッシュ・・・・・自動テスト実行
CircleCIでのCI/CDパイプライン化というものを行いたい。
##公式リファレンス
https://circleci.com/docs/2.0/configuration-reference/#checkout
こちらにあります。
##サンプルのconfig.yml
プロジェクト追加時に示されるsample.yml
基本的にこれにDBのimageを追加すればすぐさま自動テストが利用できます。
(ruby versionなどは変更してください。)
# Ruby CircleCI 2.0 configuration file
#
# Check https://circleci.com/docs/2.0/language-ruby/ for more details
#
version: 2
jobs:
build:
docker:
# specify the version you desire here
- image: circleci/ruby:2.4.1-node-browsers
# Specify service dependencies here if necessary
# CircleCI maintains a library of pre-built images
# documented at https://circleci.com/docs/2.0/circleci-images/
# - image: circleci/postgres:9.4
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: |
bundle install --jobs=4 --retry=3 --path vendor/bundle
- save_cache:
paths:
- ./vendor/bundle
key: v1-dependencies-{{ checksum "Gemfile.lock" }}
# Database setup
- run: bundle exec rake db:create
- run: bundle exec rake db:schema:load
# 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
ですが今回は設定を変えていくので基本的な構造から確認します。
##設定ファイルの基本的な構造
version: 2
jobs:
#job名
build:
#dockerイメージや実行コマンドを書いていく
workflows:
version: 2
#workflow名
build-deploy:
jobs:
#実行するジョブとスケジュールを書いていく
こちら超簡略化したconfig.yml
jobsでテストの実行内容を書いて
workflowsでテストの実行条件(スケジュール)を書きます。
##full exampleが参考になる
公式リファレンス内にfull exampleというものがあります。
これはステージング環境と本番環境へのデプロイの設定が想定されているので
これを元にRailsプロジェクトへと書き換えるのが良さそうです。
下記はfull exampleを簡略化したものです。
version: 2
jobs:
build:
docker:
- image: dockerイメージ名
environment:
FOO: bar
working_directory: ~/my-project
steps:
- checkout
- run:
name: ステップ名
command: 実行コマンド
deploy-stage:
docker:
- image: dockerイメージ名
working_directory: /tmp/my-project
steps:
- run:
name: ステップ名
command: 実行コマンド
deploy-prod:
docker:
- image: dockerイメージ名
working_directory: /tmp/my-project
steps:
- run:
name:
command:
workflows:
version: 2
build-deploy:
jobs:
- build:
filters:
branches:
ignore:
- develop
- /feature-.*/
- deploy-stage:
requires:
- build
filters:
branches:
only: staging
- deploy-prod:
requires:
- build
filters:
branches:
only: master
##テスト実行の流れ
jobs以下で実行内容を定義し、
workflows以下で実行タイミングを定義します。
またworkflowsを定義していない場合はデフォルトでjob -> buildが
エントリーポイントとなり、push時に実行されます。
またworkflowsには filters -> branches -> onlyまたはignoreで
トリガーbranchを設定します。
上記のfull exampleでは
・develop, featureブランチを除いたブランチへのpush時にbuildジョブを実行
・stagingブランチへのpush時にはbuildジョブ成功後にdeploy-stageジョブを実行
・masterブランチへのpush時にはbuildジョブ成功後にdeploy-prodジョブを実行
という設定になっています。
ステージング環境と本番環境でデプロイ先を変えたい場合は
deploy-stageジョブとdeploy-prodジョブの中で設定を行えば可能となります。
##cacheを活用する
テスト実行を効率化するためにcacheを使いましょう。
(デフォルトの設定ファイルですでに設定されています)
公式ページはhttps://circleci.com/docs/2.0/caching/
パッケージの依存関係が更新されていない限りはrestoreされたキャッシュを使用します。
設定方法
steps:
- restore_cache:
keys:
- v1-dependencies-{{ checksum "Gemfile.lock" }}
- v1-dependencies-
- run:
name: install dependencies
command: |
bundle install --jobs=4 --retry=3 --path vendor/bundle
- save_cache:
paths:
- ./vendor/bundle
key: v1-dependencies-{{ checksum "Gemfile.lock" }}
restore_cacheで利用できるキャッシュを探索
save_cacheでキャッシュを保存します。
依存関係が書かれているGemfile.loc:kのチェックサムが合致すればそれを利用し、
無ければ最新のキャッシュを利用します。
上記はgemをvendor/bundleにキャッシュしてGemfile.lockの変更をトリガーにして
キャッシュを保存していく、といった設定になります。
pathを.gitに変更してソースコード自体をキャッシュする方法もあるので
checkoutに時間がかかる場合はこちらを検討すると良いみたい。
##CircleCIからherokuへデプロイする
公式のリファレンスは
https://circleci.com/docs/2.0/deployment-integrations/#heroku
こちらです。
herokuでデプロイする場合には、heroku側のUIから設定可能ですが
パイプラインとしてcircleCIに組み込む場合は
config.ymlにgit pushコマンドを記載します。
また、CircleCIのUIから環境変数にAPIキーとAppNameを設定しておきましょう。
git push https://heroku:$HEROKU_API_KEY@git.heroku.com/$HEROKU_APP_NAME.git master
このコマンドをconfig.ymlのjobに組み込めば自動デプロイが可能ですね。
ステージングと本番環境を分けられるように環境変数も
HEROKU_APP_NAME_STAGE
HEROKU_APP_NAME_PROD
のようにして二つに分けておきましょう
##full_exampleを完成版にする
必要な情報は揃いましたがfull_exmapleがrubyとherokuでは無いため
CircleCIを使って
・Githubへプッシュすると自動テストが実行される
・stagingブランチへpushされた場合は検証環境(heroku)へデプロイ
・masterブランチへpushされた場合は本番環境(heroku)へデプロイ
書き換えて行きましょう
version: 2
jobs:
build:
docker:
# specify the version you desire here
- image: circleci/ruby:2.6.1-node-browsers
- image: circleci/postgres
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: |
bundle install --jobs=4 --retry=3 --path vendor/bundle
- save_cache:
paths:
- ./vendor/bundle
key: v1-dependencies-{{ checksum "Gemfile.lock" }}
# Database setup
- run: bundle exec rake db:create
- run: bundle exec rake db:schema:load
# 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
deploy_stage:
docker:
- image: circleci/ruby:2.6.1-node-browsers
working_directory: /tmp/repo
steps:
- checkout
- run:
name: 'Install Heroku CLI, if necessary'
command: |
if [[ $(command -v heroku) == "" ]]; then
curl https://cli-assets.heroku.com/install.sh | sh
else
echo "Heroku is already installed. No operation was performed."
fi
- run:
name: heroku maintenance on
command: heroku maintenance:on --app ${HEROKU_APP_NAME_STAGE}
- run:
name: Deploy to Heroku_Staging
command: |
git push https://heroku:$HEROKU_API_KEY@git.heroku.com/$HEROKU_APP_NAME_STAGE.git master
- run:
name: heroku maintenance off
command: heroku maintenance:on --app ${HEROKU_APP_NAME_STAGE}
deploy_prod:
docker:
- image: circleci/ruby:2.6.1-node-browsers
working_directory: /tmp/repo
steps:
- checkout
- run:
name: 'Install Heroku CLI, if necessary'
command: |
if [[ $(command -v heroku) == "" ]]; then
curl https://cli-assets.heroku.com/install.sh | sh
else
echo "Heroku is already installed. No operation was performed."
fi
- run:
name: heroku maintenance on
command: heroku maintenance:on --app ${HEROKU_APP_NAME_PROD}
- run:
name: Deploy to Heroku_Production
command: |
git push https://heroku:$HEROKU_API_KEY@git.heroku.com/$HEROKU_APP_NAME_PROD.git master
- run:
name: heroku maintenance off
command: heroku maintenance:off --app ${HEROKU_APP_NAME_PROD}
workflows:
version: 2
build-deploy:
jobs:
- build
- deploy_stage:
requires:
- build
filters:
branches:
only:
- staging
- deploy_prod:
requires:
- build
filters:
branches:
only:
- master
herokuメンテナンスモードを変えるためherokuCLIが必要なので
インストールコマンドだけ追加しています。
herokuとgithubを連携させてテスト完了後にデプロイという設定もできますが
CircleCI上でパイプライン化するとどこで異常が発生したか
一目瞭然で便利ですね。
##master,stagingへのプッシュを禁止する
このCI/CDパイプラインは
feature- → develop → staging(デプロイ) → master(デプロイ)*
という想定なのでこれらのブランチには直接プッシュできない状態にするのが安全。
そのため
https://qiita.com/starfish/items/f55b7cadeff5df5cca46
https://qiita.com/january108/items/05ab9e2f1d713f6efae3
こちらの記事にあるようにpre-pushを設定しておくと心理的にも安心安全、、、!