💭

CI/CD環境開発を振り返り

2024/12/17に公開

2024年も残りわずかとなりました。
入社して1年近く経ったということで、今回は自社プロジェクトでのインフラ環境開発についてお話しさせていただきたいです。

インフラのCI/CD化をお願いします

入社後、初めての自社案件インフラ構築であり、初めてのCI/CD化です。
要件は次の通りです。

  • 現在レンタルサーバ環境にあるリソースをAWSに引っ越してほしい
  • AWS Codeシリーズを使って開発 -> デプロイを自動化してね
  • CI/CDパイプラインでどこかで失敗したらロールバックしてね
  • コードをデプロイ中にサービス止まらないようにしてね

また、CIについてはAWS CodeBuildではなくGitHub Actionsを使用するということになりました。
設計に関してお話しする前にまずはCI/CDパイプラインについておさらいです。

CI/CDって何?

当初は教科書的に、開発(Development)と運用(Operation)を1つのパイプラインにまとめてこまめに変更を加える事で変更によるリスクやコード管理・運用管理の負担を減らすこと!と覚えていたため
CI/CDが何か漠然と理解していたつもりでしたが、実際に作ってみるまではどのようなものかわかりませんでした。
詳細まで理解するのに時間を要しましたが、今回は自社製品の開発環境であったためテストの期間など定めや納期はなくアジャイル開発的に進めていきました。

今回は本番と開発環境の2ステージ構成となりました。
当初のイメージはこういう感じ、リポジトリが2つで環境ごとに独立したパイプラインがあることを想像していました。

実際はこのようになりました(見やすさのために簡略化しています)。
コードはモノリポで管理して、それぞれの環境用のブランチにGitHub Actionsのワークフローをそれぞれ作成しています。パイプラインは独立させます。
1つのリポジトリでコードを管理して、動作する環境は2つとなります。

モノリポに関しては詳しい記事がありますので下記を参照してください。
https://zenn.dev/burizae/articles/c811cae767965a

設計にあたって考えたこと

Blue/Green

まずは、環境にイメージをデプロイをすることによって環境が停止しないようにすることです。
こちらを実現するためにはロールバックを実装する必要があります。
具体的には下記のタイミングで判断します。

  • コードをビルド時に単体テストして失敗した場合
  • デプロイ前後に不具合が見つかった場合

デプロイ後のロールバックに関してはCode Pipelineを使用することで前のリビジョンへの切り替えが実行できますし、デプロイ前はCodeDeployのBlue/Greenデプロイメントを使用することでコンテナのルーティング切り替えが容易に実装できます。
最終的に、B/Gデプロイ構成は下記のようになりました。

B/Gデプロイ概要
  1. 開発者またはインフラ管理者によってa.またはb.いずれかの操作が実施される。
    a. ソースコードが開発者によってGithubにプッシュされる。
    b. コンテナデプロイ/起動用の設定ファイル(appspec.yml, taskdef.json)がCodeCommitにプッシュされる。
  2. 1-a. Github と 1-b. CodeCommitにプッシュされたのち、Codepipelineが成果物を検知する。
    a. Github ActionsがworkflowをもとにソースコードからイメージをビルドしたのちにECRにプッシュして、CodePipelineが検知する。
    b. Code commitにプッシュされた設定ファイルをCodePipelineが検知する。
  3. CodeDeployはappspec.ymlを参照してECSクラスターのサービスを更新し、taskdef.jsonを参照してタスクのサイズや環境変数、ネットワーク設定が読み込まれてGreenターゲットグループにタスクがデプロイされる。
  4. 本番環境のみ承認者がデプロイされたBlueのターゲットグループのアプリケーションをテストして、問題がなければ承認する。
  5. CodeDeployがGreenターゲットグループのタスクに対してヘルスチェックを実施して、問題がなければALBのターゲットグループをBlueからGreenに変更する。

マイクロサービス化

CI/CD環境では、各コンポーネントが疎結合化されているため、パイプラインが機能しなくなった際に、どの時点で不具合が発生しているかを追跡する必要があります。
これはマイクロサービス化においてよく見られる課題ですが、疎結合の結果、障害の原因が特定しにくくなったり、設計全体を把握するのが難しくなったりすることがあります。
そのため、各サービスのログを確実に記録することや、監視および通知を活用して障害原因を究明する手法を検討し、アーキテクチャを分解しながら設計を進めていきました。
また、各コンポーネントで扱うデータをどこに保持するかにも注意が必要です。特に、環境ごとにデータの一貫性を保つことを怠ると、設計ミスが発生しやすくなります。

苦労したこと

  • アプリケーションのDockerコンテナ化
    • Dockerに関するベースの知識が乏しかったので、ECS on Fargateでエラーなく立ち上げられるまでに何度も検証を繰り返しました。
  • CodeDeployの設定
    • CodeDeployは一度作成すると設定を変更できない項目が多かったため、何度も作り直ししました。のちにCloudFormationを使えばよいことに気が付きました。
  • taskdef.json
    • タスク定義はコンソール画面から作成できますが、CodePipelineを使用する場合は別に用意しておく必要があったので混乱しました。
  • 環境変数の設計
    • CI/CDでモノリポを採用する場合は環境変数に気を配る必要があります。また、変数をSSMパラメータストアなどに格納する場合はローカル検証テスト用の変数ファイルを作るなどの工夫が必要です。イメージのビルド、コンテナの起動時、アプリケーションの実行時それぞれどの時点で注入するべきなのか検証しながら進めていましたが、変数が足りないことで何度エラーを経験したかわかりません。
  • ビルド / デプロイ通知
    • ビルド・デプロイ時に結果の成否をTeamsのチャネルに対して通知をするように設定する必要がありました。CodeDeployはlambdaを使用したhookを、GitHub ActionsはNotify Teams Channelを使用しました。
    • Adoptive Card方式がうまくはまらず苦労し、結局作りこみせず極力シンプルに
    • Webhookが使えなくなりましたが、Power Automateに移行することでとりあえず落ち着きました。

CI/CD環境を構築して感じたこと

  • イメージは最小化する
    • アプリケーションをビルドする際に長い時間がかかると、あまりCIの恩恵が感じられません。また、イメージが重いと起動時のオーバーヘッドが大きくなりデプロイにも時間がかかります。不要なモジュールなどはインストールしない方が良さそうです。
  • インフラ担当者の負担
    • CI/CD環境とはいってもインフラの構築や保守が少なくなるわけではありませんし、各環境の動作検証することを考えると割と手間がかかります。CI/CDを導入する際の設計はアプリケーション側開発者と綿密な連携が必要ですし、チームのDevOps技量が問われることになります。
  • CI/CD環境を作ることが終着点ではない
    • 環境構築後は当然インフラの保守が必要なので、メンテナンスのことを考えておく必要があります。たとえば、アプリケーション側で必要になる環境変数はSSM Parameter Storeなどに配置すると変更が容易になります。

終わりに

CI/CDは生産性や品質の向上、検証の効率化などの大きなメリットがあります。
半面、このメリットを享受するためにはそれなりの工数や開発運用の連携が必要になりました。
DevOps領域にチャレンジできとても良い経験値になったことは確かですし、これからもこの分野に対してさらに理解を深めたいと思っています。

この記事が今後CI/CD導入を検討する方の参考になれば幸いです。
2024年もありがとうございました。2025年もどうぞよろしくお願いします。

株式会社トッカシステムズ

Discussion