AI個人開発の技術スタック

Next.jsをGoogle Cloud(Cloud Run)で動かす

生成AI関連の需要拡大もあり、Google Cloudはあらためて注目を集めています。Cloud Runには期限のない無料枠がありますが、組み合わせるデータベースの選び方で費用が大きく変わる点は知っておく必要があります。

コンテナをそのまま動かせるサーバーレス

Cloud RunはDockerコンテナをそのままデプロイできるサーバーレスの実行環境です。Next.jsをDockerfileでビルドできる状態にしておけば、アプリ側のコードをCloud Run向けに書き換える必要はほとんどありません。この手引の自前サーバー構成(Next.jsの基礎、Caddyの基礎、デプロイの基礎)とアプリの作り方は共通のまま、公開先だけをマネージドなクラウドに置き換えるイメージです。

Cloud Runには期限のない無料枠がある

Cloud Runの無料枠は、期限も商用利用の制限もなく毎月リセットされます。GitHubリポジトリに変更を送ると、GitHub Actionsからコンテナをビルドし、Cloud Runへデプロイする流れが基本です。デプロイ自体はGitHub Actions側からgcloud run deployを実行する数行のワークフローで済みます。

無料枠に収まりやすい構成:最小インスタンス数を0に設定しておくと、アクセスがない時間帯は課金が発生しません。ただし次のリクエストまでにコンテナの起動待ち(コールドスタート)が生じるため、常時アクセスがあるサービスでは最小インスタンス数を1以上にする判断も必要です。

データベースはCloud SQLとNeon、どちらを選ぶか

Cloud SQLはGoogle Cloudが提供するマネージドPostgreSQL/MySQLですが、Cloud Runと違って期限のない無料枠がありません。新規アカウント向けの無料トライアルやクレジットを使い切った後は、最小構成でも固定費がかかり続けます。

これに対してNeonはサーバーレスのPostgresで、使っていない時間は自動的に計算リソースがゼロへスケールします。個人開発やアクセスが少ない検証段階のサービスであれば、無料枠の範囲内で運用し続けられる場合があります。DrizzleなどのORMと組み合わせる場合は、サーバーレス環境向けのHTTP接続ドライバーを使う必要がある点には注意します。

Neonのコールドスタートに注意:直近にアクセスがない状態から接続すると、計算リソースの起動待ちで最初の応答が遅くなります。常時アクセスのある本番サービスでは、有料プランでの常時起動や、Cloud SQLへの切り替えを検討します。
機密情報を扱うならCloud SQLを選ぶ:個人情報や認証情報のような機密性の高いデータを保存する場合は、無料プランのNeonではなくCloud SQLを選びます。Cloud SQLは同じGoogle Cloudのプロジェクト内でIAMやVPC、閉域接続によるアクセス制御をそのまま適用できるのに対し、Neonの無料プランは共有インフラ上で動く別サービスであり、アクセス制御や監査の仕組みもGoogle Cloud側とは別に管理する必要があります。この判断基準は、この手引のGoのAPI(モバイルアプリのAPIにGoを選ぶ理由)でも触れています。

Cloud Runの「箱」はTerraformで管理する

Cloud Runサービス自体、サービスアカウント、IAM権限、コンテナイメージを置くArtifact Registry、Neonの接続文字列を保管するSecret Managerは、Terraformのgoogleプロバイダーで宣言的に管理できます。実際にアプリのコンテナイメージを差し替えて反映するのはCI(GitHub Actions)の仕事で、Terraformは「サービスの入れ物」がある状態を保証する役割です。

resource "google_artifact_registry_repository" "app" {
  location      = "asia-northeast1"
  repository_id = "my-app"
  format        = "DOCKER"
}

resource "google_service_account" "run_sa" {
  account_id   = "my-app-run"
  display_name = "Cloud Run runtime SA"
}

resource "google_secret_manager_secret" "database_url" {
  secret_id = "my-app-database-url"
  replication {
    auto {}
  }
}

resource "google_cloud_run_v2_service" "app" {
  name     = "my-app"
  location = "asia-northeast1"

  template {
    service_account = google_service_account.run_sa.email
    containers {
      image = "asia-northeast1-docker.pkg.dev/PROJECT_ID/my-app/app:latest"
      env {
        name = "DATABASE_URL"
        value_source {
          secret_key_ref {
            secret  = google_secret_manager_secret.database_url.secret_id
            version = "latest"
          }
        }
      }
    }
    scaling {
      min_instance_count = 0
      max_instance_count = 3
    }
  }
}

resource "google_cloud_run_v2_service_iam_member" "public" {
  name     = google_cloud_run_v2_service.app.name
  location = google_cloud_run_v2_service.app.location
  role     = "roles/run.invoker"
  member   = "allUsers"
}

コンテナイメージのタグ(:latestの部分)は、GitHub Actions側がビルドごとに書き換えてgcloud run deployで反映する運用が一般的です。Terraformの適用と、CIによるイメージ差し替えを同じパイプラインに混在させると競合しやすいため、Cloudflare編と同様にジョブを分けておきます。

Neonの接続文字列はTerraformに書かない:Secret Managerの「箱」自体はTerraformで作りますが、中身の値(Neonの接続文字列)はGitHub Actionsやgcloudコマンドから別途投入し、.tfファイルや状態ファイルに平文で残さないようにします。

Terraformのstateファイルは、同じGoogle Cloud上のCloud Storageバケットに置きます。gcsバックエンドはTerraformが標準で対応しているため、Cloudflare編のR2のようなS3互換の読み替えは不要です。

terraform {
  backend "gcs" {
    bucket = "my-app-tfstate"
    prefix = "cloud-run"
  }
}

バケット自体も、他のリソースと同じくTerraform管理下に置けます(初回のbootstrapだけは手動、もしくは別の最小構成で作成します)。

resource "google_storage_bucket" "tfstate" {
  name                        = "my-app-tfstate"
  location                    = "asia-northeast1"
  uniform_bucket_level_access = true
  public_access_prevention    = "enforced"

  versioning {
    enabled = true
  }
}
公開防止とバージョニングを必ず設定:public_access_prevention = "enforced"でバケットが公開設定されることを構造上防ぎ、versioningを有効にして、state破損時に過去の版へ戻せるようにします。アクセス権限はプロジェクトの編集者権限などを使い回さず、Terraformを実行するサービスアカウントに対して、このバケットへの読み書きだけを個別に付与します。

無料枠と制限を先に把握する

  • Cloud Runのリクエスト数:月200万リクエストまで無料(商用利用可、期限なし)
  • Cloud RunのCPU時間:月24万vCPU秒まで無料
  • Cloud Runのメモリ:月45万GiB秒まで無料
  • Cloud SQL:期限のない無料枠はなし。最小構成でも固定費が発生
  • Neonのストレージ:プロジェクトあたり0.5GBまで無料
  • Neonの計算時間:月100CU時間まで無料(プロジェクトは最大100個まで作成可)

Cloud Run側は無料枠だけで長く運用できる設計ですが、DB側は選ぶサービスによって費用の発生タイミングがまったく異なります。

Cloud SQLとNeon、どちらを選ぶか

Cloud Run+Neonが向く

個人開発、検証段階のサービス、データベースも含めてまず無料で始めたい場合。Postgres本来の機能をそのまま使えます。

Cloud Run+Cloud SQLが向く

常時アクセスのある本番サービス、個人情報や認証情報など機密性の高いデータを扱う場合、同一ネットワーク内での閉域接続や他のGoogle Cloudサービスとの統合を重視する場合。

どちらのDBを選んでも、Cloud Run側の認証設定やシークレット管理、GitHub Actionsによるデプロイの考え方は共通です。

構成の選定から相談したい方へ

Google Cloud、Cloudflare Workers、自前サーバーのいずれでも、要件に合わせて構築・運用まで対応します。

運用について相談する