スマートキャンプ、エンジニアの入山です。
前回のブログで、弊社プロダクトのインフラをEC2基盤からECS/Fargate基盤へ移行した話を紹介しました。
上記プロジェクトは大規模なインフラの刷新だったこともあり、CI/CDについても従来の仕組みからECS/Fargateの構成に合わせて変更しています。
CI/CDは、安定したプロダクト開発には必須且つ長期に渡って継続的に利用するものなので、いかにストレス少なく効率的に出来るかが重要だと考えています。
また、CI/CDは一度構築してしまうと放置されがちですが、日々の開発チーム全体の生産性にも大きな影響を与えるため、こういった数少ない再構築のタイミングではコストを掛ける価値があるのではないでしょうか。
今回は、弊社のインフラ移行時に実施したCI/CDの改善について紹介したいと思います。
従来のCI/CD構成と課題
インフラ移行前(EC2運用時)のCI/CDは、CircleCI + Jenkinsの構成で実施していました。
CIはCircleCIでの自動テスト、CDはJenkinsでのデプロイと明確に分かれている構成です。
CircleCI
- バックエンドテスト
- RSpec
- RuboCop
- フロントエンドテスト
- Jest
- ESLint
- Storybook
- Buildテスト
- バックエンドテスト
Jenkins
- デプロイ(EC2)
- Railsのassetsプリコンパイル
- webpackによるビルド
- EC2へのコード反映
- マイグレーション実行
- デプロイ(EC2)
CIについては、GitHubへのPushをトリガーに自動で実行されます。
基本的にコードをPushする度に必ず実行されますが、フロントエンドとバックエンドでテストフローを分割しており、各コードに差分がない場合はテストをスキップして時間短縮&コスト削減するように少し工夫しています。
ちなみにこのテストスキップ処理は、数ヶ月毎に取り組んでいる開発改善の中で実施したものです。
CDについては、Jenkinsのワークフローでassetsプリコンパイルやwebpackによるビルド、各EC2へのコード反映を実施する仕組みとなっていました。
各EC2へのコード反映は、ALBを活用してEC2を2グループに分け、1グループずつ最新のコードを反映して切り替えることで実施していました。
Jenkinsによるデプロイは、全てワークフローやスクリプトで作り込んでいたため、拡張性や柔軟性の面を中心に、以下のような課題がありました。
- デプロイ時間が長い
- 約45分 / Staging, Pre, Productionまでの1リリース
- 自動ロールバック不可
- ロールバックは可能だが、手動でのイレギュラー対応が必要
- スケーリングし辛い
- 予め用意しておいた予備のEC2しか自動デプロイ出来ない
- Jenkinsサーバーがデプロイ中に落ちる(たまに)
- デプロイ環境依存のエラー(たまに)
新しいCI/CD構成
インフラ移行後のCI/CDは、GitHub Actions + CircleCI + AWS CodeDeployの構成に変更しました。
CircleCIが従来から実施していた自動テストに加えてデプロイに関する作業の一部分を担うようになり、AWS CodeDeployが最終的なECS/Fargateへのデプロイを管理する構成です。 CircleCIの自動テストの部分については、今回特に変更してないため割愛し、デプロイ作業に関する部分のみを説明します。
GitHub Actions
- デプロイ開始トリガー(Gitタグ発行)
- Production : masterブランチ固定
- Staging : 任意のブランチをデプロイ可能
- デプロイ開始トリガー(Gitタグ発行)
CircleCI
- デプロイ(ECS/Fargate)
- Dockerイメージビルド
- DockerイメージのECRプッシュ
- Railsのassetsプリコンパイル
- webpackによるビルド
- AWS CodeDeployのデプロイ開始
- デプロイ(ECS/Fargate)
AWS CodeDeploy
- デプロイ(ECS/Fargate)
- Blue/Greenデプロイ
- マイグレーション実行
- デプロイ(ECS/Fargate)
従来のJenkins単一でのデプロイ構成と比べて、デプロイに必要な作業がCircleCIによって並列処理可能となったことや、デプロイ部分をCodeDeploy管理としたことで柔軟なデプロイ方式を簡単に選択できるようになりました。
今回のデプロイ構成では、CircleCIのデプロイトリガーをGitタグにすることで、デプロイのタイミングやStagingに反映するブランチを柔軟に選択出来るようにしています。(Productionはmasterブランチ固定)
また、トリガーのGitタグのPushやGitHub上でリリースタグを発行する作業も手動だと手間なため、GitHub Actionsのworkflow_dispatchを使ってリリースタグを自動生成できるようにしています。
つまり、GitHub Actionsでデプロイのワークフローを実行すれば、(Stagingであれば指定したブランチで)リリース作業が開始出来るような仕組みになっています。
改善点・工夫点
新しいCI/CDの構築により改善された点や構築にあたって工夫した点を簡単に紹介します。 今回のCI/CDの再構築によって、従来の構成で主な課題となっていた部分は全て解消することができました。
CI/CD全体
- デプロイ時間短縮(約15分短縮)
- マネージドサービスの利用による環境管理コストやエラー率低減
GitHub Actions
- 定常的に行う手動作業(Gitタグ発行)を自動化
- 同様のトリガーを一箇所に集約することで管理コスト削減
CircleCI
- Gitタグをトリガーにした柔軟なデプロイ運用
- デプロイに必要な作業を並列にすることで効率化
- キャッシュ機能(ファイルキャッシュ、Dockerレイヤーキャッシュ)による時間短縮
CodeDeploy
- Blue/Greenデプロイによる自動での即時ロールバックに対応
- カナリアやリニア方式などの柔軟なリリース方式が選択可能
- スケールを含むデプロイも簡単に対応可能
- 検証したコンテナがそのまま本番へ昇格するため切り替えが速い
- CodeDeployの承認フローをChatOpsにして利便性向上(以前のブログで紹介)
まとめ
今回は、弊社のインフラ移行時に実施したCI/CDの改善について紹介しました!
色々細かい改善も実施しましたが、個人的にはBlue/Greenで即時ロールバックが可能になった事がリリース作業時の心の余裕に繋がっており、何よりも導入してよかったと思っているポイントです。
CI/CDは開発チームが常に付き合っていくものであり、上手く構築することでチーム全体における日々の開発効率を向上させることが出来るものです。弊社では今後も少しずつ改善を実施し、更に快適なCI/CDにしていこうと考えております!
今回ご紹介した構成は数多あるパターンのうちの一つですが、何かの参考になれば嬉しいです。
また、読んでいただいた方々が構築しているCI/CDで「これは最高だ!」というものがあれば、ぜひ教えていただきたいです!