デプロイ失敗時にCI/CDで再デプロイしたいので、CircleCIのワークフローを見直して並列にした話
今のプロジェクトでは、リリース作業を毎週行っています。たまにデプロイや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ファイルです。
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のフローを見直してみると良いかもしれませんね。