Render無料枠からCloud Run + Cloud Schedulerへ移行した理由と手順
個人開発や小規模プロジェクトでよく使われるRenderの無料枠。デプロイが簡単で、GitHubと連携するだけでCI/CDが動くのは非常に便利です。
しかし、 定期実行ジョブが多いプロダクト では、Renderの無料枠には致命的な制約があります。
今回、FastAPIで構築したバックエンドを Cloud Run + Cloud Scheduler の構成に移行したので、その理由と具体的な手順を共有します。
1. なぜRender無料枠から移行したのか
Renderの無料枠の制約
Renderの無料枠(Free Instance)には以下の制約があります。
| 制約 | 内容 |
|---|---|
| スリープ | 15分間リクエストがないとインスタンスが停止 |
| コールドスタート | 再起動に10〜30秒かかる |
| 月間稼働時間 | 750時間(複数サービスで共有) |
今回のプロダクトの特性
私のプロダクトは、AI技術ニュースを自動収集・処理するメディアサイトのバックエンドです。以下のような定期実行ジョブが動いています。
| ジョブ | 実行間隔 |
|---|---|
| ニュース収集 | 1時間ごと |
| 論文収集 | 8時間ごと |
| トレンド情報収集 | 24時間ごと |
| 事前スコアリング | 30分ごと |
| AI処理 | 30分ごと |
| 記事生成 | 60分ごと |
| トレンドスコア更新 | 6時間ごと |
問題点: これらはPythonの APScheduler で実装していました。しかし、Renderの無料枠では15分でスリープするため、 スケジューラーが止まってしまう のです。
以前のプロダクトとの違い
以前のプロダクトでは、Render無料枠でも問題ありませんでした。
- 実行頻度が低い(1日1回など)
- 実行後にフロントエンドからのリクエストが続くので起きている時間が長い
- 手動トリガーが中心
今回のプロダクトは 定期実行の頻度が高い (30分〜1時間ごと)のが特徴です。ただし、 常時起動している必要はありません 。定期実行のタイミングで確実に起動し、処理が終わったら停止して構わないのです。
2. 移行先の検討
候補の比較
今回の要件は以下の通りです:
- 定期実行の頻度が高い (30分〜1時間ごと)
- 常時起動は不要 (処理が終わったら停止してOK)
- 起動後のレスポンスが速い (日本リージョン希望)
| プラットフォーム | 月額 | 日本リージョン | 特徴 |
|---|---|---|---|
| Render (有料) | $7〜 | シンガポール | 常時起動前提、移行コスト最小 |
| Railway | $5〜10 | なし(米国) | 開発者体験◎ |
| Fly.io | $2〜5 | 東京 | エッジ配置、最安 |
| Cloud Run | $0〜5 | 東京 | 従量課金、スケーラブル |
| Compute Engine | $0〜6 | 東京 | 無料枠あり(米国のみ) |
Cloud Runを選んだ理由
最終的に Cloud Run + Cloud Scheduler を選択しました。
選定理由:
- 東京リージョン があり、起動後のレスポンスが速い(5-15ms)
- 従量課金 で無駄がない(リクエストがなければほぼ無料)
- min-instances=0 で常時起動不要、コスト最適化
- Cloud Scheduler との連携でスケジューラーをマネージド化
APSchedulerの問題
Cloud Runには1つ注意点があります。
Cloud Runはリクエストがないとコンテナが停止する ため、コンテナ内で動くAPSchedulerは機能しません。
これを解決するには2つの方法があります。
| 方法 | 月額 | 説明 |
|---|---|---|
| min-instances=1 | $10〜20 | 常時起動でAPSchedulerを維持 |
| Cloud Scheduler | $0〜5 | スケジューラーを外出しにする |
今回は Cloud Scheduler でスケジューラーを外出しにする方法を採用しました。
3. アーキテクチャの変更
Before: Render + APScheduler
flowchart TB
subgraph Render["Render (Free Instance)"]
subgraph FastAPI["FastAPI"]
API["API Endpoints"]
subgraph Scheduler["APScheduler"]
News["ニュース収集 (1h)"]
AI["AI処理 (30min)"]
Other["..."]
end
end
Warning["⚠️ 15分でスリープ → ジョブ停止"]
end
After: Cloud Run + Cloud Scheduler
flowchart LR
subgraph Scheduler["Cloud Scheduler"]
S1["ニュース収集 (1h)"]
S2["AI処理 (30min)"]
S3["記事生成 (1h)"]
S4["..."]
SM["✅ マネージド<br/>✅ 確実に実行"]
end
subgraph CloudRun["Cloud Run (東京)"]
subgraph App["FastAPI"]
Endpoints["API Endpoints"]
end
CR["✅ min-instances=0<br/>✅ リクエスト時のみ課金"]
end
Scheduler -->|HTTP POST| CloudRun
4. 移行手順
ステップ1: 環境変数の変更
APSchedulerを無効化します。
# .env
ENABLE_SCHEDULER=false
コード側では、この環境変数を見てスケジューラーの起動をスキップします。
# app/services/scheduler.py
def start(self):
if not self.settings.enable_scheduler:
logger.info("Scheduler is disabled")
return
# ...
ステップ2: Dockerfileの確認
既存のDockerfileはそのまま使えます。Cloud Runは標準的なDockerイメージをサポートしています。
FROM python:3.11-slim
WORKDIR /app
COPY . .
RUN pip install pipenv && pipenv install --system --deploy
EXPOSE 8000
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
ステップ3: Cloud Runへのデプロイ
# GCPプロジェクトを設定
gcloud config set project YOUR_PROJECT_ID
# Cloud Runにデプロイ
gcloud run deploy media-backend \
--source ./backend \
--region asia-northeast1 \
--allow-unauthenticated \
--set-env-vars "ENABLE_SCHEDULER=false,SUPABASE_URL=xxx,..."
ステップ4: Cloud Schedulerの設定
各ジョブをCloud Schedulerに登録します。
# ニュース収集(毎時0分)
gcloud scheduler jobs create http news-collection \
--location asia-northeast1 \
--schedule "0 * * * *" \
--uri "https://your-backend-xxx.run.app/api/v1/collectors/news/collect" \
--http-method POST \
--attempt-deadline 10m
# AI処理(30分ごと)
gcloud scheduler jobs create http ai-processing \
--location asia-northeast1 \
--schedule "*/30 * * * *" \
--uri "https://your-backend-xxx.run.app/api/v1/articles/pipeline/process" \
--http-method POST \
--attempt-deadline 10m
# 記事生成(毎時30分)
gcloud scheduler jobs create http article-generation \
--location asia-northeast1 \
--schedule "30 * * * *" \
--uri "https://your-backend-xxx.run.app/api/v1/articles/pipeline/generate" \
--http-method POST \
--attempt-deadline 10m
ポイント:
--attempt-deadline: ジョブのタイムアウト。AI処理など時間がかかるジョブは長めに設定--schedule: cron形式。*/30 * * * *は「30分ごと」
全ジョブの登録例
#!/bin/bash
BACKEND_URL="https://your-backend-xxx.run.app"
LOCATION="asia-northeast1"
# ニュース収集(1時間ごと)
gcloud scheduler jobs create http news-collection \
--location $LOCATION \
--schedule "0 * * * *" \
--uri "$BACKEND_URL/api/v1/collectors/news/collect" \
--http-method POST
# 論文収集(8時間ごと: 0, 8, 16時)
gcloud scheduler jobs create http paper-collection \
--location $LOCATION \
--schedule "0 */8 * * *" \
--uri "$BACKEND_URL/api/v1/collectors/papers/collect" \
--http-method POST
# トレンド情報収集(1日1回: 6時)
gcloud scheduler jobs create http trends-collection \
--location $LOCATION \
--schedule "0 6 * * *" \
--uri "$BACKEND_URL/api/v1/collectors/trends/collect" \
--http-method POST
# 事前スコアリング(30分ごと)
gcloud scheduler jobs create http preliminary-scoring \
--location $LOCATION \
--schedule "*/30 * * * *" \
--uri "$BACKEND_URL/api/v1/articles/pipeline/score" \
--http-method POST
# AI処理(30分ごと)
gcloud scheduler jobs create http ai-processing \
--location $LOCATION \
--schedule "*/30 * * * *" \
--uri "$BACKEND_URL/api/v1/articles/pipeline/process" \
--http-method POST \
--attempt-deadline 10m
# 記事生成(1時間ごと)
gcloud scheduler jobs create http article-generation \
--location $LOCATION \
--schedule "30 * * * *" \
--uri "$BACKEND_URL/api/v1/articles/pipeline/generate" \
--http-method POST \
--attempt-deadline 10m
# トレンドスコア更新(6時間ごと)
gcloud scheduler jobs create http trend-score-update \
--location $LOCATION \
--schedule "0 */6 * * *" \
--uri "$BACKEND_URL/api/v1/trend-score/batch" \
--http-method POST
5. コスト比較
実際の月額コスト
| 項目 | Render (有料) | Cloud Run + Scheduler |
|---|---|---|
| コンピュート | $7/月〜 | $0〜5/月 |
| スケジューラー | 込み | $0.10/月(7ジョブ) |
| 合計 | $7/月〜 | $0〜5/月 |
Cloud Runは 従量課金 なので、リクエストがない時間は課金されません。
Cloud Run料金の内訳
CPU: $0.00002400/vCPU-秒
メモリ: $0.00000250/GiB-秒
リクエスト: 200万リクエスト/月まで無料
今回のユースケース(1日100リクエスト程度)では、 月額$1未満 に収まっています。
6. 移行後の所感
メリット
- コスト削減: 1〜2/月
- 確実な実行: スケジューラーがマネージドなので止まらない
- スケーラビリティ: トラフィック増加時に自動スケール
- 低レイテンシ: 東京リージョンで5-15ms
デメリット
- 初期設定: Cloud Schedulerの設定が手間(1-2時間)
- コールドスタート: min-instances=0だと5-10秒の遅延(管理画面用なので許容)
- GCPの学習コスト: 初めてのGCPだと戸惑う点あり
コールドスタートの許容
今回のバックエンドは 管理画面からのAPI呼び出し がメインです。一般ユーザーは直接APIを叩かず、フロントエンドのISRキャッシュから記事を取得します。
そのため、管理者が5-10秒待つことになっても問題ありません。
一般ユーザー → フロントエンド(Vercel) → ISRキャッシュ → 記事表示
管理者 → 管理画面 → バックエンドAPI(5-10秒待ち許容)
7. まとめ
| 観点 | Render無料枠 | Cloud Run + Scheduler |
|---|---|---|
| 定期実行 | ❌ 15分でスリープ | ✅ 確実に実行 |
| 常時起動 | 必要(有料$7〜) | 不要(min=0) |
| コスト | 無料 or $7〜 | $0〜5/月 |
| 日本リージョン | シンガポール | 東京 |
結論:
- 実行頻度が低い・手動トリガー中心 → Render無料枠で十分
- 定期実行が多いが常時起動は不要 → Cloud Run + Cloud Scheduler
今回の移行により、 月額1-2に削減 しつつ、 確実な定期実行 を実現できました。
ポイントは「常時起動が不要」という点です。 定期実行のタイミングでCloud Schedulerがコンテナを起動し、処理が終わったら停止する。この「オンデマンド起動」がCloud Run + Cloud Schedulerの強みです。
同じような課題を抱えている方の参考になれば幸いです。
関連記事
- Cloud Runデプロイで遭遇したIAM権限エラーと解決法 - 実際のデプロイで遭遇した権限エラーの解決方法
参考リソース
更新履歴
| 更新日 | 内容 |
|---|---|
| 2025-12-26 | 初版公開 |
