はじめに

マイクロサービスアーキテクチャを採用したプロジェクトでは、複数のサービスが独立して開発・デプロイされます。しかし、すべてのサービスが同じリポジトリで管理されている場合(モノレポ構成)、コードの変更があった特定のサービスだけをデプロイする効率的な方法が必要です。

この記事では、GitHub Actionsを使用して、変更があったサービスだけを自動的に検出し、デプロイする方法を紹介します。

前提条件

  • GitHubリポジトリにマイクロサービスが構成されている
  • AWS CDKを使用したデプロイ
  • GitHub Actionsの基本的な知識

ワークフローの概要

このワークフローは以下のステップで構成されています:

  1. コードの変更を検出する
  2. 変更があったサービスのみをビルド
  3. AWS認証を設定
  4. 変更があったサービスのみをデプロイ

ワークフロー実装

ワークフロー定義

name: 差分サービスデプロイ

on:
  push:
    branches:
      - release/develop

env:
  AWS_REGION: ${{ vars.AWS_REGION }}
  AWS_ACCOUNT_ID: ${{ secrets.AWS_ACCOUNT_ID }}
  AWS_ROLE_ARN: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/GitHubActionsRole

jobs:
  deploy:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      id-token: write
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0  # 履歴を取得するために必要

変更検出ロジック

      - name: 変更ディレクトリの確認
        id: check-changes
        run: |
          # 最新の2つのコミット間の差分を確認
          PREVIOUS_COMMIT=$(git rev-parse HEAD^)
          CURRENT_COMMIT=$(git rev-parse HEAD)

          # 各サービスの変更を確認
          echo "service_a=$(git diff --name-only $PREVIOUS_COMMIT $CURRENT_COMMIT -- path/to/service_a/ | grep . > /dev/null && echo 'true' || echo 'false')" >> $GITHUB_OUTPUT
          echo "service_b=$(git diff --name-only $PREVIOUS_COMMIT $CURRENT_COMMIT -- path/to/service_b/ | grep . > /dev/null && echo 'true' || echo 'false')" >> $GITHUB_OUTPUT
          echo "service_c=$(git diff --name-only $PREVIOUS_COMMIT $CURRENT_COMMIT -- path/to/service_c/ | grep . > /dev/null && echo 'true' || echo 'false')" >> $GITHUB_OUTPUT
          echo "service_d=$(git diff --name-only $PREVIOUS_COMMIT $CURRENT_COMMIT -- path/to/service_d/ | grep . > /dev/null && echo 'true' || echo 'false')" >> $GITHUB_OUTPUT

条件付きビルドステップ

      - name: サービスAのビルド
        if: ${{ steps.check-changes.outputs.service_a }} == 'true'
        run: |
          cd path/to/service_a
          npm run build
          cd ../../

      - name: サービスBのビルド
        if: ${{ steps.check-changes.outputs.service_b }} == 'true'
        run: |
          cd path/to/service_b
          npm run build
          cd ../../

      - name: サービスCのビルド
        if: ${{ steps.check-changes.outputs.service_c }} == 'true'
        run: |
          cd path/to/service_c
          npm run build
          cd ../../

      - name: サービスDのビルド
        if: ${{ steps.check-changes.outputs.service_d }} == 'true'
        run: |
          cd path/to/service_d
          npm run build
          cd ../../

AWS認証設定

      - name: AWS認証情報の設定
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-region: ${{ env.AWS_REGION }}
          role-to-assume: ${{ env.AWS_ROLE_ARN }}

      - name: CDKブートストラップ
        run: |
          npx cdk bootstrap aws://${{ env.AWS_ACCOUNT_ID }}/${{ env.AWS_REGION }}

条件付きデプロイステップ

      - name: サービスAのデプロイ
        if: ${{ steps.check-changes.outputs.service_a }} == 'true'
        run: |
          npx cdk deploy ServiceA-dev --app "npx ts-node --prefer-ts-exts bin/app.ts" --context stage=dev --require-approval never

      - name: サービスBのデプロイ
        if: ${{ steps.check-changes.outputs.service_b }} == 'true'
        run: |
          npx cdk deploy ServiceB-dev --app "npx ts-node --prefer-ts-exts bin/app.ts" --context stage=dev --require-approval never

      - name: サービスCのデプロイ
        if: ${{ steps.check-changes.outputs.service_c == 'true' }}
        run: |
          npx cdk deploy ServiceD-dev --app "npx ts-node --prefer-ts-exts bin/app.ts" --context stage=dev --require-approval never

      - name: サービスDのデプロイ
        if: ${{ steps.check-changes.outputs.service_d == 'true' || steps.check-changes.outputs.service_a == 'true' }}
        run: |
          npx cdk deploy ServiceD-dev --app "npx ts-node --prefer-ts-exts bin/app.ts" --context stage=dev --require-approval never

重要なポイント

1. 変更検出の仕組み

このワークフローの核心は、git diffコマンドを使用して、前回のコミットと現在のコミットの間で変更があったディレクトリを特定する部分です。

git diff --name-only $PREVIOUS_COMMIT $CURRENT_COMMIT -- path/to/service/

このコマンドは、指定されたパス内で変更があったファイルの一覧を返します。結果が空でなければ(grep . > /dev/nullで確認)、そのサービスに変更があったと判断します。

2. 条件付き実行

各ビルドステップとデプロイステップは、対応するサービスに変更があった場合にのみ実行されます:

if: ${{ steps.check-changes.outputs.service_a }} == 'true'

3. サービス間の依存関係

サービス間に依存関係がある場合は、条件を組み合わせることで対応できます。例えばサービスDがサービスAに依存していた場合:

if: ${{ steps.check-changes.outputs.service_d == 'true' || steps.check-changes.outputs.service_a == 'true' }}

この例では、サービスDはサービスAに依存しているため、サービスAに変更があった場合もサービスDをデプロイします。

まとめ

GitHub Actionsを使用して、変更があったマイクロサービスだけを選択的にデプロイする方法を紹介しました。このアプローチにより、以下のメリットが得られます:

  • デプロイ時間の短縮
  • リソース使用量の削減
  • 変更のないサービスへの影響リスクの低減

マイクロサービスアーキテクチャを採用している場合、このような差分デプロイの仕組みは、CI/CDパイプラインの効率化に大きく貢献します。