はじめに

DX開発事業部の北村です。

GitHub上のMonorepo構成Next.jsアプリをCloud Build(第2世代)でCloud Runに自動デプロイする手順と、Monorepo構成やCloud Build第2世代を利用する際のポイントを解説します。

今回解説する手順は、以下の通りです。

  • Docker環境構築
  • Cloud Build構成ファイル作成
  • ソースリポジトリ(GitHub)とCloud Buildリポジトリの接続
  • Artifact Registry構築
  • サービスアカウント設定
  • Cloud Buildトリガー構築

Docker環境構築

今回デプロイ対象のプロジェクトは、以下のMonorepo構成となっています。

app(プロジェクトルート)/
├── frontend/
├── backend/
├── mobile/
└── infra/

このうち、フロントエンド部分であるfrontendディレクトリのコンテナイメージをビルドするDockerファイルを作成します。

app(プロジェクトルート)/
├── .devcontainer/
│ ├── frontend/
│ │ └── Dockerfile
│ └── backend/
├── frontend/
├── backend/
├── mobile/
└── infra/

Dockerfile

# ビルドステージ
FROM node:22-slim AS builder
WORKDIR /app
COPY frontend/package*.json ./
RUN npm ci
COPY frontend .
RUN npm run build

# 実行ステージ
FROM node:22-slim
WORKDIR /app
COPY --from=builder /app/public ./public
COPY --from=builder /app/.next ./.next
COPY --from=builder /app/package*.json ./
# devdependenciesを排除
RUN npm ci --omit=dev
EXPOSE 3000
CMD ["npm", "start"]

ℹ️ 解説
上記のようにビルドプロセスの段階を分けるマルチステージビルドを活用することで、最終的な実行イメージに必要な最小限のファイル(ビルド成果物、依存関係など)だけが含まれ、コンテナイメージのサイズを削減することができます。

Cloud Build構成ファイル作成

Cloud Buildのワークフローを定義する構成ファイルを作成します。

_AR_HOSTNAME,PROJECT_ID,_REPO_NAME,_SERVICE_NAME,_DEPLOY_REGIONは、Cloud Buildトリガーを作成時に定義する変数です。変数の内容はこちらを参照。

app(プロジェクトルート)/
├── .cloudbuild/
│ ├── frontend/
│ │ └── cloudbuild.yml
│ └── backend/
├── frontend/
├── backend/
├── mobile/
└── infra/
cloudbuild.yml
steps:
  # docker build
  - name: 'gcr.io/cloud-builders/docker'
    args: [
      'build', '--no-cache', '-t',
      '${_AR_HOSTNAME}/${PROJECT_ID}/${_REPO_NAME}/${_SERVICE_NAME}:latest',
      '-f', '.devcontainer/frontend/Dockerfile',
      '.'
    ]

  # DockerイメージをArtifact Registryへプッシュする
  - name: 'gcr.io/cloud-builders/docker'
    args: [
      'push',
      '${_AR_HOSTNAME}/${PROJECT_ID}/${_REPO_NAME}/${_SERVICE_NAME}:latest'
    ]

  # Cloud Runへデプロイ
  - name: 'google/cloud-sdk'
    args: [
      'gcloud', 'run', 'deploy', '${_SERVICE_NAME}',
      '--image=${_AR_HOSTNAME}/${PROJECT_ID}/${_REPO_NAME}/${_SERVICE_NAME}:latest',
      '--region', '${_DEPLOY_REGION}', '--platform', 'managed',
      # 本番環境で公開Webサイトではない場合、このオプションは使用せず、IAMロールなどでアクセス制御を行うことを推奨します
      '--allow-unauthenticated',
      '--port=3000'
    ]

# イメージをArtifact Registryへ保存する
images:
  - '${_AR_HOSTNAME}/${PROJECT_ID}/${_REPO_NAME}/${_SERVICE_NAME}:latest'

# ログをCloud Loggingにのみ送信する
options:
  logging: CLOUD_LOGGING_ONLY

ソースリポジトリ(GitHub)とCloud Buildリポジトリの接続

デプロイ対象のソースコードを管理しているGitHubとCloud Buildリポジトリを接続し、ソースコードへのアクション(PushやPull Request作成など)を検知できるようにします。

Google Cloudコンソールから「Cloud Build/リポジトリ/第2世代」へ移動し、「ホスト接続を作成」を押下します。

⚠️注意
今回は、第2世代を使用します。第2世代は、Terraformでの管理が可能となりました。IaCを考えている方は、第2世代を使用すると良いでしょう。第1世代と第2世代では、GitHubとの連携手順などが一部異なります。

接続の構成を入力し、「接続」を押下します。

GitHubとCloud Buildを接続するためには、GitHubでCloud Buildアプリが必要です。インストールしていない場合は、GitHub側でインストールし、既存のアプリがある場合は、既存のアプリを選択して接続を行います。(GitHubのCloud Buildアプリを通して認証を行います)

GitHubとCloud Buildの接続を許可します。

接続したGitHubのホストが表示されていることを確認します。

ℹ️ 解説
GitHubの認証情報は、Secret Managerに保存されます。

次に、作成したホストにGitHubのリポジトリをリンクさせます。「リポジトリをリンク」を押下します。

接続するリポジトリ情報を入力し、「リンク」を押下します。

対象のリポジトリがホストにリンクされていることを確認します。

以上で、GitHubとCloud Buildリポジトリの接続が完了しました。

Artifact Registry構築

次に、Artifact Regitryを構築し、コンテナイメージを保存できるようにします。

Google Cloudコンソールから「Artifact Registry」に移動し、「リポジトリの作成」を押下します。

リポジトリの構成を入力し、「作成」を押下します。

⚠️ 注意
Artifact Registryにコンテナイメージを保存する際に、脆弱性をチェックしセキュリティを高めたい場合、「脆弱性スキャン」を「有効」にしてください。今回は、「無効」を選択します。

リポジトリが作成されていることを確認します。

以上で、Artifact Registryの構築は完了です。

サービスアカウント設定

次に、サービスアカウントを設定し、Cloud Buildトリガーに付与する権限を設定します。

Google Cloudコンソールから「IAMと管理/サービスアカウント」に移動し、「サービスアカウントを作成」を押下します。

サービスアカウントの詳細を入力し、「完了」を押下します。

サービスアカウントが作成されていることを確認します。

次に、サービスアカウントにIAMロールの紐付けを行います。

「IAMと管理/IAM」に移動し、「アクセスを許可」を押下します。

前のステップで作成したサービスアカウントを選択後、Cloud Buildトリガーで必要な権限を付与し(最小権限の原則で付与)、「保存」を押下します。

ℹ️ 解説
ロール「Secret Managerのシークレット アクセサー」は、Secret Managerに保存されたGitHub認証情報を参照するために必要となります。

⚠️ 注意
権限エラーが出る場合は、特にCloud Run側でカスタムサービスアカウントを指定している場合は、Cloud Runのサービスアカウントにロール「サービスアカウントユーザー」を付与してください。

以上で、サービスアカウントの設定は完了し、Cloud Buildトリガーを構築する準備が整いました。

Cloud Buildトリガー構築

最後に、Cloud Buildのトリガーを構築し、GitHubの任意のブランチ(今回はmain)にPushされた時に自動デプロイが実行(実行されるワークフローは、cloudbuild.ymlで定義)されるようにします。

Google Cloudコンソールから「Cloud Build/トリガー」に移動し、「トリガーを作成」を押下します。

Cloud Buildトリガーの設定を入力し、「作成」を押下します。


⚠️ 注意
Monorepo構成の場合、「ソース」で変更を検知するディレクトリを指定する必要があります。今回は、フロントエンド部分をデプロイするので、frontend/**をフィルタとして指定します。フィルタを指定しない場合、backendやinfraなど対象外のディレクトリが変更された場合でもCloud Buildトリガーが発火してしまうので、注意しましょう。

ℹ️ 解説

「詳細」で定義する変数の値は、cloudbuild.ymlで記載した変数に代入されます。

  • _AR_HOSTNAME:Artifact Registryのホスト名(基本的にasia-northeast1-docker.pkg.dev)
  • _DEPLOY_REGION:デプロイするリージョン(基本的にasia-northeast1)
  • _PROJECT_ID:Google CloudプロジェクトのID
  • _REPO_NAME:Artifact Registryの名前
  • _SERVICE_NAME:Cloud Runのサービス名、コンテナイメージ名(任意の名前を入力)


⚠️ 注意
サービスアカウント設定」で作成したサービスアカウントを選択する。

Cloud Buildトリガーが作成されていることを確認します。

Cloud Runが作成され、デプロイされていることを確認します。

お疲れ様でした!以上で、Next.jsアプリをCloud BuildでCloud Runに自動デプロイする手順は完了となります!

最後に

Monorepo構成で対象のディレクトリだけをデプロイする方法やCloud Buildリポジトリ第2世代での設定方法、サービスアカウントの設定方法が特にポイントかと思います。

Next.jsアプリをCloud Runにデプロイする際は、今回の方法で自動デプロイを試していただければと思います!