はじめに
今回は「n8nをCloud Runでミニマムに動かす」というのをテーマに紹介します。
- 無料枠がたくさんあるCloud Run
- GCSをデータの永続化 (Cloud SQLのDB代も使わずに済む)
- Cloud RunのIAPがPre-GAになったので、アクセス管理が簡単に設定できます。Pre-GAなので、個人や社内利用くらいまでが良さそうです。
Terraformの設定
Cloud Storage
GCSのBucket作成
resource "google_storage_bucket" "n8n_data" {
name = "naka-test-n8n-data"
location = var.region
force_destroy = false
uniform_bucket_level_access = true
}
Service Account
Service Accountの作成と上で作成したBucketへの権限を付与
# service account for cloud run
resource "google_service_account" "n8n" {
account_id = "n8n-service" # need to be between 6-30 characters long
display_name = "n8n"
}
# access to bucket
resource "google_storage_bucket_iam_member" "n8n_gcs_access" {
bucket = google_storage_bucket.n8n_data.name
role = "roles/storage.objectUser"
member = google_service_account.n8n.member
}
Cloud Run service
Cloud Run serviceとIAPの設定
# n8n Cloud Run service
resource "google_cloud_run_v2_service" "n8n" {
provider = google-beta
name = "n8n"
location = var.region
launch_stage = "BETA" // IAP is Pre-GA
iap_enabled = true // enable IAP
template {
containers {
image = "n8nio/n8n:1.93.0"
ports {
container_port = 5678
}
resources {
limits = {
cpu = "1"
memory = "512Mi"
}
}
env {
name = "DB_TYPE"
value = "sqlite"
}
env {
name = "N8N_USER_FOLDER"
value = "/home/node/.n8n"
}
env {
name = "N8N_WEBHOOK_URL"
value = "<cloud run のendpoint>"
}
env {
name = "N8N_PROTOCOL"
value = "HTTPS"
}
volume_mounts {
name = "n8n-data"
mount_path = "/home/node/.n8n"
}
}
service_account = google_service_account.n8n.email
# gcs volume
volumes {
name = "n8n-data"
gcs {
bucket = google_storage_bucket.n8n_data.name
}
}
scaling {
min_instance_count = 1
max_instance_count = 1
}
}
}
min_instance_countを1にしてスケジュールなどのBackgroundタスクも実行できるようにします。
スケジュールなど使わない場合には、min_instance_countを0にして、アクセスがない場合にはSleepさせてコストを抑えることも可能です
max_instance_countを1にして、複数インスタンスから同一ファイルへの書き込みロックはないので一つのインスタンスのみにしています。ここらへんが気になる場合は、DB_TYPEをpostgresdbなどのDatabaseにしたほうが良いでしょう。
Cloud Storage FUSE では、同じファイルへの複数書き込みの同時実行制御(ファイルのロック)は行いません。複数の書き込みによってファイルの置き換えが試みられた場合は、最後の書き込みが有効となり、それより前の書き込みはすべて失われます。 (ref)
IAPを使うには現時点では、google-beta providerの v6.30.0 以上が必要です。
IAPの権限付与
locals {
n8n_iap_access_members = [
"user:your@example.com",
]
}
resource "google_iap_web_cloud_run_service_iam_member" "n8n_iap_access" {
for_each = toset(local.n8n_iap_access_members)
project = google_cloud_run_v2_service.n8n.project
location = google_cloud_run_v2_service.n8n.location
cloud_run_service_name = google_cloud_run_v2_service.n8n.name
role = "roles/iap.httpsResourceAccessor"
member = each.value
}
google providerの v6.31.0 以上が必要です。
Service AccountからIAPの後ろにn8nを呼び出す
今回は n8nのWebhookのエンドポイントを叩くことにします。(ref: https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.webhook/)
GCPの公式ドキュメントは、こちらです。
まず叩くService AccountとCloud RunのEndpointを指定します。Cloud Schedulerから叩く予定であれば、n8n-scheduler@.iam.gserviceaccount.com など Cloud Runで使っているservice accountとは別のservice accountを指定する事ができます。
SERVICE_ACCOUNT=xxx@<project>.iam.gserviceaccount.com
URL=$(gcloud run services describe n8n --project <project> --region asia-northeast1 --format json | jq -r '.status.url')
次に、jwtを生成するためのclaimを作成します。
cat > claim.json << EOM
{
"iss": "$SERVICE_ACCOUNT",
"sub": "$SERVICE_ACCOUNT",
"aud": "$URL/webhook-test/test",
"iat": $(date +%s),
"exp": $((`date +%s` + 3600))
}
EOM
audには、$URL/webhook-test/test
を設定していますが、n8n上でwebhookを設定したときのtest用のエンドポイントです。本番は、$URL/webhook-test/test
を $URL/webhook/test
になります。/test
の部分は自分で好きなpathを決めることができます。
audienceの設定は、$URL
のみならず、完全なパスを指定する必要があるのでご注意ください。audienceが実際に叩くPathと異なる場合は、Invalid IAP credentials: Audience specified does not match requested endpoint
というエラーが返ります。
サインをしてjwtを発行します。
gcloud iam service-accounts sign-jwt --iam-account="$SERVICE_ACCOUNT" claim.json output.jwt
最後にendpointをたたいて見ます
curl -X POST -H "Authorization: Bearer $(cat output.jwt)" "$URL/webhook-test/test"
{"message":"Workflow was started"}
が見えたら叩かれたことを確かめられます。
Cloud SchedulerからIAPで設定したCloud Runのエンドポイントを叩く (できてない❌️)
Cloud SchedulerからCloud Runを叩く場合、IAPを設定していない場合には、以下のようにService Accountを指定することで実行ができます。
# Cloud Scheduler用のサービスアカウント
resource "google_service_account" "n8n_scheduler" {
account_id = "n8n-scheduler"
display_name = "Service Account for Cloud Scheduler to invoke n8n webhook"
}
# サービスアカウントにCloud Run起動権限を付与
resource "google_cloud_run_service_iam_member" "n8n_scheduler_is_run_invoker_of_n8n" {
project = google_cloud_run_v2_service.n8n.project
location = google_cloud_run_v2_service.n8n.location
service = google_cloud_run_v2_service.n8n.name
role = "roles/run.invoker"
member = google_service_account.n8n_scheduler.member
}
# Cloud Schedulerジョブ
resource "google_cloud_scheduler_job" "n8n_webhook_test" {
name = "n8n-webhook-test"
description = "Daily job to trigger n8n webhook at 9 AM JST"
schedule = "0 0 * * *" # 毎日 UTC 0:00 (JST 9:00)
time_zone = "Asia/Tokyo"
attempt_deadline = "120s" # タイムアウトを120秒に設定
http_target {
http_method = "POST"
uri = "${google_cloud_run_v2_service.n8n.uri}/webhook/test"
# IAPを使用するための設定
oidc_token {
service_account_email = google_service_account.n8n_scheduler.email
audience = google_cloud_run_v2_service.n8n.uri
}
}
depends_on = [
google_cloud_run_service_iam_member.n8n_scheduler_is_run_invoker_of_n8n,
google_iap_web_cloud_run_service_iam_member.n8n_iap_access,
]
}
IAPの場合には、User同様に roles/iap.httpsResourceAccessor
の権限を付与しましたが、
# サービスアカウントにIAPの権限を付与
resource "google_iap_web_cloud_run_service_iam_member" "n8n_scheduler_iap_access" {
project = google_cloud_run_v2_service.n8n.project
location = google_cloud_run_v2_service.n8n.location
cloud_run_service_name = google_cloud_run_v2_service.n8n.name
role = "roles/iap.httpsResourceAccessor"
member = google_service_account.n8n_scheduler.member
}
実行できませんでした。❌️
Error
{
"insertId": "18etbn1fgv7hvt",
"jsonPayload": {
"debugInfo": "URL_ERROR-ERROR_AUTHENTICATION. Original HTTP response code number = 401",
"status": "UNAUTHENTICATED",
"jobName": "projects/xxxx/locations/asia-northeast1/jobs/n8n-webhook-test",
"targetType": "HTTP",
"@type": "type.googleapis.com/google.cloud.scheduler.logging.AttemptFinished",
"url": "https://n8n-xxx.asia-northeast1.run.app/webhook/test"
},
"httpRequest": {
"status": 401
},
"resource": {
"type": "cloud_scheduler_job",
"labels": {
"project_id": "xxxx",
"location": "asia-northeast1",
"job_id": "n8n-webhook-test"
}
},
"timestamp": "2025-05-22T06:36:48.073228186Z",
"severity": "ERROR",
"logName": "projects/xxx/logs/cloudscheduler.googleapis.com%2Fexecutions",
"receiveTimestamp": "2025-05-22T06:36:48.073228186Z"
}
これはまだ解決できてないので、どなたかご存知の方いれば教えていただきたいです
まとめ
n8nを簡単にCloud RunでDeployしてアクセス管理をIAPを用いて行える設定を紹介しました。
これによって、Cloud Runで払い出されたエンドポイント https://n8n-<projectnumber>.<region>.run.app
からアクセスができるようになりました。
Cloud SQLを使うケースは、n8n on Cloud Run (ツール比較から選定まで) が参考になります。