Cloud Functions (gen2) のウォームインスタンスを営業時間帯だけに制限してコスト削減してみた

Cloud Functions (gen2) のウォームインスタンスを営業時間帯だけに制限してコスト削減してみた

Cloud Functions(第2世代)の min-instances を Cloud Scheduler で営業時間帯だけ有効にし、約57%のコスト削減を実現しました。PATCH非対応の回避策や Cloud Run API の409エラーなど、ハマりどころと解決策を共有します。
2026.08.02

はじめに

Cloud Functions(第2世代)で運用しているチャットボットに min-instances=1 を設定し、コールドスタートを回避していました。しかし、夜間や休日はほぼ利用がないのに常時ウォームインスタンスを維持するのはもったいない。

「営業時間帯だけウォームにできないか?」と調べてみたところ、Cloud Schedulerで実現できました。ただし、いくつかハマりどころがあったので共有します。

やりたいこと

時間帯 min-instances 状態
平日 9:00〜18:00 1 ウォーム(即時応答)
上記以外(夜間・土日) 0 コールド(初回リクエスト時に起動)

Cloud Schedulerで2つのジョブを作成し、営業開始時に minInstanceCount=1、営業終了時に minInstanceCount=0 に切り替えます。

cloud-scheduler-min-instances-business-hours-architecture

前提環境

  • Cloud Functions 第2世代(Cloud Run ベース)
  • リージョン: asia-northeast1
  • ランタイム: Python 3.14
  • デフォルトコンピューティングサービスアカウントに roles/editor が付与済み

ハマりどころ1: Cloud SchedulerはPATCHメソッドを直接サポートしていない

Cloud Run / Cloud Functions の Admin API でリソースを部分更新するには PATCH メソッドが必要です。しかし、Cloud Schedulerの --http-method で指定できるのは DELETE, GET, HEAD, POST, PUT のみ。PATCH はありません。

ERROR: argument --http-method: Invalid choice: 'patch'.
Valid choices are [delete, get, head, post, put].

回避策: PUT + X-HTTP-Method-Override: PATCH ヘッダーを使います。Google Cloud の公式ドキュメントでもこの方法が紹介されています。

--http-method=PUT \
--headers="Content-Type=application/json,X-HTTP-Method-Override=PATCH"

ハマりどころ2: Cloud Run APIでは409エラーになる

最初は Cloud Run Admin API(run.googleapis.com)を使ってみました。

https://run.googleapis.com/v2/projects/YOUR_PROJECT/locations/REGION/services/YOUR_SERVICE?update_mask=scaling.min_instance_count

しかし、リクエストは常に HTTP 409 (Conflict) で失敗しました。

{
  "debugInfo": "URL_ERROR-ERROR_OTHER. Original HTTP response code number = 409",
  "status": "ABORTED"
}

原因: Cloud Functions(第2世代)は Cloud Run 上で動作しますが、Cloud Functions が管理するサービスに対して Cloud Run API で直接更新をかけると、管理レイヤーの競合が発生します。

解決策: Cloud Run API ではなく Cloud Functions v2 APIcloudfunctions.googleapis.com)を使います。

https://cloudfunctions.googleapis.com/v2/projects/YOUR_PROJECT/locations/REGION/functions/YOUR_FUNCTION?updateMask=serviceConfig.minInstanceCount

リクエストボディも Cloud Functions API の形式に合わせます:

{"serviceConfig": {"minInstanceCount": 1}}

Cloud Schedulerジョブの作成

上記2つのハマりどころを踏まえた最終的なコマンドです。

ウォーム開始ジョブ(営業時間開始)

gcloud scheduler jobs create http warm-bot-start \
  --location=asia-northeast1 \
  --schedule="0 9 * * 1-5" \
  --time-zone="Asia/Tokyo" \
  --uri="https://cloudfunctions.googleapis.com/v2/projects/YOUR_PROJECT/locations/REGION/functions/YOUR_FUNCTION?updateMask=serviceConfig.minInstanceCount" \
  --http-method=PUT \
  --headers="Content-Type=application/json,X-HTTP-Method-Override=PATCH" \
  --message-body='{"serviceConfig":{"minInstanceCount":1}}' \
  --oauth-service-account-email=YOUR_SA@developer.gserviceaccount.com

ウォーム停止ジョブ(営業時間終了)

gcloud scheduler jobs create http warm-bot-stop \
  --location=asia-northeast1 \
  --schedule="0 18 * * 1-5" \
  --time-zone="Asia/Tokyo" \
  --uri="https://cloudfunctions.googleapis.com/v2/projects/YOUR_PROJECT/locations/REGION/functions/YOUR_FUNCTION?updateMask=serviceConfig.minInstanceCount" \
  --http-method=PUT \
  --headers="Content-Type=application/json,X-HTTP-Method-Override=PATCH" \
  --message-body='{"serviceConfig":{"minInstanceCount":0}}' \
  --oauth-service-account-email=YOUR_SA@developer.gserviceaccount.com

ポイント:

  • --schedule="0 9 * * 1-5" は月〜金の9:00、"0 18 * * 1-5" は月〜金の18:00
  • --time-zone="Asia/Tokyo" でJSTを指定
  • --oauth-service-account-email にはCloud Functions の更新権限(cloudfunctions.functions.update)を持つサービスアカウントを指定。roles/editor があれば十分

動作確認

手動でジョブを実行して動作を確認します。

# ウォーム開始を手動トリガー
gcloud scheduler jobs run warm-bot-start --location=asia-northeast1

# ジョブのステータス確認(status: {} = 成功)
gcloud scheduler jobs describe warm-bot-start --location=asia-northeast1 \
  --format="yaml(status,lastAttemptTime)"

実際に min-instances が変わったことを確認:

# Cloud Functions API で確認
gcloud functions describe YOUR_FUNCTION --region=asia-northeast1 --gen2 \
  --format="value(serviceConfig.minInstanceCount)"

# Cloud Run のアノテーションでも確認可能
gcloud run services describe YOUR_FUNCTION --region=asia-northeast1 \
  --format="value(spec.template.metadata.annotations.'autoscaling.knative.dev/minScale')"

status: {} が返れば成功です。status.code: 10 が返る場合は HTTP 409 エラーなので、APIエンドポイントが正しいか確認してください。

コスト比較

構成 ウォーム時間/月 概算月額
常時 min-instances=1 約730時間 約¥8,500〜10,000
スケジュール制(月〜金 9-18時) 約195時間 約¥2,300〜2,700

約73%のコスト削減になります。

コールドスタートのトレードオフ

営業時間外(夜間・土日)に min-instances=0 になるため、その時間帯の最初のリクエストではコールドスタートが発生します。

具体的には:

  • コンテナの起動、Pythonランタイムの初期化、依存パッケージの読み込みが走る
  • 依存パッケージが重い場合(例: google-cloud-aiplatform など)、起動に30秒以上かかることがある
  • Google Chat のようにHTTPレスポンスにタイムアウトがあるサービスでは、タイムアウトエラーが表示される場合がある(処理自体は継続し、遅延して応答が返る)

startup-cpu-boost を有効にしておくと起動が速くなりますが、完全には解消できません。

# startup-cpu-boost の有効化(初回のみ)
gcloud run services update YOUR_FUNCTION --region=asia-northeast1 --startup-cpu-boost

営業時間外の利用頻度が低い場合は、このトレードオフは許容範囲でしょう。

まとめ

  • Cloud Scheduler + Cloud Functions v2 API で、営業時間帯だけ min-instances=1 を維持する仕組みを構築できた
  • Cloud Functions(gen2)管理下のサービスには Cloud Run API ではなく Cloud Functions v2 API を使う必要がある(409回避)
  • Cloud Scheduler は PATCH 非対応のため、PUT + X-HTTP-Method-Override: PATCH で回避する
  • コスト約73%削減(常時ウォーム比)。営業時間外はコールドスタートが発生するトレードオフあり

この記事をシェアする

関連記事