この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
今のプロジェクトでは、リリース作業を毎週行っています。たまにデプロイやE2Eテストが失敗してやり直すことがあるのですが、CI/CDのワークフローが一本道なので手動デプロイせざるを得ない事象が発生しました。 そのため、対策としてCircleCIのワークフローを見直して並列にしてみました。
環境について
- 本番環境
- エンドユーザーが利用している
- プレ本番環境
- 関係者が本番さながらに利用している
- 本番環境リリース前の最後の砦
- ステージング環境
- いわゆる検証環境
- 動作確認を行う
- 開発環境
- 開発者が自由に使える
リリースについて
v1.2.3
のようなタグを付けてPushすれば、CircleCIのワークフローが動いてステージング環境・プレ本番環境・本番環境へのリリースが行われます。リリース作業自体はミスを防ぐために複数人で確認しながら行っています。
特徴としては、ステージング環境→プレ本番環境→本番環境と一本道になっており、各環境の間にapproveボタン(承認ボタン)
を仕込んでいます。
これによってジョブの実行を一時停止させており、人間が承認すると次の環境にデプロイするジョブが動くようになっています。
- ステージング環境にリリースする(タグを付けてPushする)
- 1週間待つ
- プレ本番環境にリリースする(承認ボタンを押す)
- 1週間待つ
- 本番環境にリリースする(承認ボタンを押す)
なお、デプロイ環境の準備ではPython仮想環境の構築や必要なライブラリのインストールを行い、それをCacheに保存しています。以降のジョブではCacheから拾って利用しています。
一本道の課題
プレ本番環境や本番環境でデプロイに失敗した場合やE2Eテストが失敗したとき、手動デプロイ(CircleCIを使わず、開発者がデプロイコマンドを実行)せざるを得ない課題があります。 しかし、手動デプロイは地味に手間ですし、普段はしない操作のためミスをする可能性があります。タグを付けてPushすれば終わりではありません。
- 環境変数の名前と値を調べて適応する
- 使用するデプロイコマンドを調べたり実行順序に気をつける
手動デプロイをしている理由は、CircleCIのワークフローが一本道なので、デプロイ不要な環境(ステージング・プレ本番環境)へのデプロイが行われてしまうからです。
一本道がダメなら、並列にすればいいじゃない
そんなわけでCircleCIのワークフローを見直して並列にしてみました。
これによって、特定環境だけにデプロイできるようになりました。 ただし、通常のリリース時に「あ! ステージング環境じゃなくていきなり本番デプロイしちゃった!!!」となる可能性が生まれます。 このあたりは承認時に複数人でチェックするなどで対処します。 今回の変更は手動デプロイせざるを得ない状況を回避する事が目的だからです。まずは試してみて、不都合があればまた見直していきます。
CircleCIの画面(参考)
ステージング環境にデプロイした様子
いきなり本番環境にリリースした様子
すべての環境にリリースした様子
CircleCIのConfigファイル(参考)
あくまでも参考ですが、下記がCircleCIのConfigファイルです。
.circleci/config.yml
version: 2.1
executors:
my-executor:
docker:
- image: circleci/python:3.7.2
environment:
PIPENV_VENV_IN_PROJECT: true
working_directory: ~/work
commands:
restore:
steps:
- restore_cache:
key: work-v1-{{ .Branch }}-{{ .Revision }}
save:
steps:
- save_cache:
paths:
- ".venv"
key: work-v1-{{ .Branch }}-{{ .Revision }}
deploy:
parameters:
env:
type: enum
enum: ["prod", "pre_prod", "staging"]
steps:
- checkout
- restore
- run:
name: deploy
command: |
source .venv/bin/activate
echo << parameters.env >>
echo deploy-command
jobs:
setup:
executor: my-executor
steps:
- checkout
- restore
- run:
name: install
command: |
sudo pip install pipenv
pipenv install
- save
lint_and_unittest:
executor: my-executor
steps:
- checkout
- restore
- run:
name: lint_and_unittest
command: |
source .venv/bin/activate
echo lint-command
echo unittest-command
deploy_staging:
executor: my-executor
steps:
- checkout
- restore
- deploy:
env: staging
deploy_pre_prod:
executor: my-executor
steps:
- checkout
- restore
- deploy:
env: pre_prod
deploy_prod:
executor: my-executor
steps:
- checkout
- restore
- deploy:
env: prod
workflows:
version: 2.1
release-workflow:
jobs:
- setup:
filters:
branches:
ignore: /.*/
tags:
only: /v([0-9]+\.){2}[0-9]+/
- lint_and_unittest:
requires:
- setup
filters:
branches:
ignore: /.*/
tags:
only: /v([0-9]+\.){2}[0-9]+/
- approve_for_staging:
type: approval
requires:
- lint_and_unittest
filters:
branches:
ignore: /.*/
tags:
only: /v([0-9]+\.){2}[0-9]+/
- approve_for_pre_prod:
type: approval
requires:
- lint_and_unittest
filters:
branches:
ignore: /.*/
tags:
only: /v([0-9]+\.){2}[0-9]+/
- approve_for_prod:
type: approval
requires:
- lint_and_unittest
filters:
branches:
ignore: /.*/
tags:
only: /v([0-9]+\.){2}[0-9]+/
- deploy_staging:
requires:
- approve_for_staging
filters:
branches:
ignore: /.*/
tags:
only: /v([0-9]+\.){2}[0-9]+/
- deploy_pre_prod:
requires:
- approve_for_pre_prod
filters:
branches:
ignore: /.*/
tags:
only: /v([0-9]+\.){2}[0-9]+/
- deploy_prod:
requires:
- approve_for_prod
filters:
branches:
ignore: /.*/
tags:
only: /v([0-9]+\.){2}[0-9]+/
さいごに
CircleCIのワークフローを見直して、一本道から並列に変更してみました。たまにはCI/CDのフローを見直してみると良いかもしれませんね。