⚙️

Docker コンテナイメージのビルド効率化を目指して 〜後編〜

2022/04/21に公開

こんにちは。エンジニアチームの山岸 (@yamagishihrd) です。

本記事は、以下の記事の後編になります。前編では、当社における「Docker コンテナイメージのビルド効率化」の取り組みとして、"マルチフェーズビルド (MBP: Multi-Phase Build)" の概念についてご紹介しました。後編では、MBP を適用したビルド実行パイプラインの自動化について解説したいと思います。

https://zenn.dev/simpleform/articles/20220412-01-multi-phase-build

目次

  1. SimpleCheck のアーキテクチャ概要【前編】
  2. Docker イメージビルドに対する課題感 【前編】
  3. マルチフェーズビルド (MPB: Multi-Phase Build)【前編】
  4. マルチフェーズビルドの運用課題と方策
  5. AutoMPB の概要とアーキテクチャ

4. マルチフェーズビルドの運用課題と方策

手動運用に対する課題感

前記事でご説明した通り、マルチフェーズビルドは Docker コンテナイメージのビルドプロセスを、機能的な特性をもとに複数のフェーズに分割・抽象化するものでした。各サービスイメージの開発時には最新の特化イメージを指定するだけでよく、ライブラリなどの下回りを気にせずにロジック開発に集中できます。

MPB-Architecture

コアイメージにはプロダクトのコアライブラリが含まれており、度々更新が入ります。サービスイメージ側でコアライブラリの更新を反映するには、① まずコアイメージを再ビルドし、② サービスイメージが利用する特化イメージを再ビルドし、③ さらにその特化イメージを使用してサービスイメージを再ビルドする必要があります。この作業をコアライブラリ更新の度に手動で行っていくのは、運用コストや正確性の点で難しいものがあります。

自動化の要件

手動運用から開放されるべく、特化イメージ再ビルドの自動化に着手しました。すなわち、コアイメージのソースが更新された場合はコアイメージおよびすべての特化イメージを再ビルドします。また、特定の特化イメージのソースのみが更新される場合もあるため、その場合はその特化イメージのみを再ビルドします。

当社ではプロダクトのリポジトリ管理を GitHub で行っています。リポジトリの更新をビルドパイプライン実行のトリガとしたいわけですが、上記の要件を実現するにあたって、リポジトリ内のどのディレクトリに変更が入ったかを検知する必要があります。さらに、プロダクトには製品版 (main) と開発版 (dev) があるため、どのブランチに対する変更なのかも検知する必要があります。

Requirements

(※サービスイメージは開発者が任意のタイミングで切り替えられるよう、自動ビルドの対象から外しました)

5. AutoMPB の概要とアーキテクチャ

ビルドパイプライン

構築したコアイメージ用ビルドパイプラインは以下の通りです。サーバーレスなビルド実行環境として AWS CodeBuild を使用し、パイプラインを AWS Step Functions で構築しています。「自動化されたマルチフェーズビルド」ということで、"AutoMPB (Automated Multi-Phase Build)" と呼称しています。

処理シーケンスは以下のようになっています。

  • ステートマシン実行入力を処理し、設定を Lambda 関数で読み込む。
  • 読み込んだ設定を環境変数として渡し、CodeBuild プロジェクトを同期的に開始する。ビルド結果が「失敗」なら通知してパイプライン実行を終了し、「成功」なら処理を継続する。
  • スキップフラグが有効の場合、特化イメージのビルドは行わずにそのまま終了する。
  • 特化イメージ用の設定を JSON 形式で整形・生成する。
  • Map ステートで各特化イメージのビルドパイプライン実行を開始する。
  • すべてのビルドが成功したら「成功」としてパイプライン実行を終了する。

AutoMPB-Architecture

特化イメージ用ビルドパイプラインは別ステートマシンとして切り出し、コアイメージ用からネストで呼び出すことにしました。これにより、特定の特化イメージのみのビルドに対応するとともに、保守性を向上させています。

実行トリガ

コアイメージ用・特化イメージ用それぞれのビルドパイプラインを構築できました。ではパイプライン実行をどうトリガすれば良いでしょうか。

これには GitHub Actions を使用しました。例えば、以下のようにワークフローの on 句を定義することで、特定ブランチの特定ディレクトリに対する変更を検知し、jobs に定義されたジョブを開始できます。jobs の中で Step Functions ステートマシン実行を開始していますが、事前に OIDC 設定および実行権限をもつ IAM ロールの作成を実施しておく必要があります。(※詳細は こちら の記事などをご参照ください)

.github/workflows/run-autompb-dev-core.yml
name: Run Auto-MPB (core@develop)
on:
  push:
    branches: 
      - develop                             # develop ブランチに対する変更を検知
    paths: 
      - "simplecheck/core/**"               # simplecheck/core/ 配下のすべての変更を検知
jobs:
  build:
    runs-on: ubuntu-latest
    permissions: 
      id-token: write
      contents: read
    steps:
      - name: Checkout
        uses: actions/checkout@v2
      - name: Configure AWS Credentials
        uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-region: ap-northeast-1
          role-to-assume: arn:aws:iam::{AccountID}:role/{IAMRoleName}
      - name: Start SFN workflow
        run: >
          aws stepfunctions start-execution --state-machine-arn arn:aws:states:ap-northeast-1:{AccountID}:stateMachine:sfn-workflow-build-deco-core --input "{\"COMMIT_HASH\":\"$COMMIT_HASH\", \"IMAGE_NAME\":\"$IMAGE_NAME\", \"ENV\":\"$ENV\", \"BUILD_SPECIALIZED_FROM_CORE\":$BUILD_SPECIALIZED_FROM_CORE, \"SKIP_BUILD_SPECIALIZED\":$SKIP_BUILD_SPECIALIZED}"
        env: 
          ENV: dev                          # develop ブランチに対する変更のため "dev" を指定
          IMAGE_NAME: core                  # 再ビルド対象となるイメージを指定
          COMMIT_HASH: ${{ github.sha }}    # コミットハッシュ値の取得 (Docker イメージのタグバージョンに使用する)
          SKIP_BUILD_SPECIALIZED: 0         # 特化イメージのビルドをスキップするか否かを表すフラグ

以上で、リポジトリ内の特定ブランチ・特定ディレクトリに対する変更を検知し、これを実行トリガとしてコアイメージおよび特化イメージを自動的にビルドしてくれるパイプラインを構築することができました。

まとめ

前編・後編にかけて、当社における Docker コンテナイメージビルドの CI/CD について解説してみました。いかがでしたでしょうか。

プロダクト開発におけるアジリティを確保する上で、運用を継続的に見直し、無駄があれば自動化などによって省力化していく努力が必要です。一方で、全自動を目指して作り込みすぎると保守性や習得性が下がるため、ある程度の汎用性を持たせておくこともワークフローを設計する上では重要だと思います。シンプルかつ現実的な運用設計を心がけたいですね。

最後までご覧いただき、ありがとうございました。

参考

Discussion