TerraformでCloud RunにNestJSバックエンドをデプロイしてみる - 実践編
本記事のサマリ
前回のTerraform入門に続き、実際にNestJS GraphQL APIをCloud Runにデプロイしてみました。AIにTerraformコードを生成させて、それを読み解きながら実装していく過程で、Apple SiliconでのDockerビルド問題や組織ポリシーによるIAM制限など、実際のつまずきポイントも含めて記録しています。
今回のコードは下記のリポジトリにあります。
前回の振り返り
前回の記事では、Terraformの基礎を学びました。
- HCL構文: provider、resource、variable、outputの基本的な書き方
- Google Cloud認証: gcloud CLIとApplication Default Credentialsの設定
- 初回リソース作成: GCSバケットを例にした基本的なリソース管理
- Terraformワークフロー: terraform init → plan → apply の流れ
- 状態管理: terraform.tfstateファイルの重要性
今回はこの知識を活用して、実際のアプリケーションをデプロイしてみます。
今回やること
今回は、NestJSで作ったGraphQL APIをCloud Runにデプロイします。プロジェクトID lab-202511-cloudrun-terraform、リージョン asia-northeast1で進めていきます。
具体的には:
- Artifact Registry: Dockerイメージ保存用リポジトリの作成
- Cloud Run: NestJS GraphQL APIの実行環境
- デプロイフロー: TerraformでのインフラからDockerイメージのデプロイまで
なぜCloud Runなのか
バックエンドAPIの選択肢は色々ありますが、Cloud Runを選んだ理由:
- スケールゼロ: リクエストがない時は0にスケールダウン
- コスト効率: AWSのFargateは最小1インスタンスだが、Cloud Runは完全に0まで下がる
- シンプル: コンテナを用意すれば動く、Kubernetesの知識不要
- 従量課金: 実際に処理している時間だけ課金
特に個人プロジェクトでは、このコスト効率の良さが魅力的でした!
全体構成
今回の構成は簡潔に下記の通りです。
NestJS GraphQL API → Artifact Registry → Cloud Run
- NestJS: Todo管理のGraphQL API
- Artifact Registry: Dockerイメージの保管場所
- Cloud Run: コンテナの実行環境
Terraformコードを書いてみる
まずは必要なリソースを定義していきます。前回学んだprovider、resource、variableの知識を使って書いてみました:
resource "google_artifact_registry_repository" "main" {
location = var.region
repository_id = "${var.service_name}-repo"
description = "Docker repository for ${var.service_name}"
format = "DOCKER"
}
resource "google_cloud_run_service" "main" {
name = var.service_name
location = var.region
template {
spec {
containers {
image = "${var.region}-docker.pkg.dev/${var.project_id}/${google_artifact_registry_repository.main.repository_id}/${var.service_name}:latest"
ports {
container_port = 3000
}
resources {
limits = {
cpu = "1000m"
memory = "512Mi"
}
}
}
}
}
traffic {
percent = 100
latest_revision = true
}
}
書いたコードの各オプションが何を意味するのか、公式ドキュメントを確認しながら理解を深めていきました。
Terraform Registryで設定オプションを調べる
Artifact Registry
ドキュメントを見ると、主なオプションは:
- format: DOCKER、MAVEN、NPMなどのリポジトリ形式
- cleanup_policies: 古いイメージの自動削除ルール
- labels: タグ付け
今回は format = "DOCKER"を指定して、Dockerイメージ用のリポジトリを作成します。
Cloud Run
オプションが驚くほど多い!重要なポイント:
-
resources: CPU
1000m(1コア)、メモリ512Mi(512MiB) - traffic: 新バージョンへの100%トラフィック配分
- image: Artifact Registryの完全パスを文字列テンプレートで生成
NestJS側の準備
Dockerfile作成
FROM node:18-alpine AS builder
WORKDIR /app
RUN apk add --no-cache yarn
COPY package.json yarn.lock ./
RUN yarn install
FROM node:18-alpine AS runner
WORKDIR /app
COPY --from=builder /app/node_modules ./node_modules
COPY . .
RUN mkdir -p /app/src && chown -R node:node /app
USER node
RUN yarn build
EXPOSE 3000
CMD ["yarn", "start:prod"]
PORT環境変数対応
Cloud Runは PORT環境変数でポートを指定するため、NestJSで対応が必要:
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const port = process.env.PORT || 3000;
await app.listen(port, '0.0.0.0');
}
実際にデプロイしてみる
Terraformでインフラ作成
terraform init
terraform plan
terraform apply
Dockerイメージのビルド
ここで最初のつまずき。Apple Silicon環境では:
# これは失敗する
docker build -t asia-northeast1-docker.pkg.dev/lab-202511-cloudrun-terraform/nestjs-todo-api-repo/nestjs-todo-api:latest ./app
# 正解:プラットフォーム指定が必要
docker build --platform linux/amd64 \
-t asia-northeast1-docker.pkg.dev/lab-202511-cloudrun-terraform/nestjs-todo-api-repo/nestjs-todo-api:latest \
./app
Apple SiliconではARMアーキテクチャでビルドされるため、Cloud Run用に明示的に linux/amd64を指定する必要がありました。
プッシュとデプロイ
docker push asia-northeast1-docker.pkg.dev/lab-202511-cloudrun-terraform/nestjs-todo-api-repo/nestjs-todo-api:latest
プッシュ完了後、Cloud Runが自動的に新しいリビジョンをデプロイしました👍
つまずいたポイント
1. アーキテクチャ不一致
問題: Apple Silicon環境でビルドしたイメージがCloud Runで動かない
解決: --platform linux/amd64フラグでx86_64アーキテクチャを明示
2. 組織ポリシー制限
問題: Terraformで allUsersのIAM設定時に「One or more users named in the policy do not belong to a permitted customer」エラー
解決: 認証付きアクセスで運用することに決定
認証付きでのテスト:
curl -H "Authorization: Bearer $(gcloud auth print-identity-token)" \
https://nestjs-todo-api-5szbrnrtva-an.a.run.app/graphql \
-H "Content-Type: application/json" \
-d '{"query":"{ todos { id title completed } }"}'
結果、GraphQL APIが正常に動作していることを確認できました!
この辺りは以前書いた、Google Cloudのゼロトラストの思想なのかも。
まとめ
前回学んだTerraformの基礎知識を活かして、実際にNestJS GraphQL APIをCloud Runにデプロイできました。Apple SiliconでのDockerビルド問題や組織ポリシー制限など、ドキュメントだけでは分からない実践的な課題も経験できました。
次のステップでは、Secret ManagerやCI/CDパイプラインの構築に挑戦してみたいと思います✨
株式会社StellarCreate(stellar-create.co.jp)のエンジニアブログです。 プロダクト指向のフルスタックエンジニアを目指す方募集中です! カジュアル面談で気軽に雑談しましょう!→ recruit.stellar-create.co.jp/
Discussion