【実践】Render無料枠からCloud Run + Cloud Schedulerへ移行した理由と手順
テクノロジー

【実践】Render無料枠からCloud Run + Cloud Schedulerへ移行した理由と手順

カジュアルモードは準備中です

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 を選択しました。

選定理由:

  1. 東京リージョン があり、起動後のレスポンスが速い(5-15ms)
  2. 従量課金 で無駄がない(リクエストがなければほぼ無料)
  3. min-instances=0 で常時起動不要、コスト最適化
  4. 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. コスト削減: 7/7/月 → 1〜2/月
  2. 確実な実行: スケジューラーがマネージドなので止まらない
  3. スケーラビリティ: トラフィック増加時に自動スケール
  4. 低レイテンシ: 東京リージョンで5-15ms

デメリット

  1. 初期設定: Cloud Schedulerの設定が手間(1-2時間)
  2. コールドスタート: min-instances=0だと5-10秒の遅延(管理画面用なので許容)
  3. 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

今回の移行により、 月額7から7から1-2に削減 しつつ、 確実な定期実行 を実現できました。

ポイントは「常時起動が不要」という点です。 定期実行のタイミングでCloud Schedulerがコンテナを起動し、処理が終わったら停止する。この「オンデマンド起動」がCloud Run + Cloud Schedulerの強みです。

同じような課題を抱えている方の参考になれば幸いです。


関連記事


参考リソース


更新履歴

更新日 内容
2025-12-26 初版公開
この記事をシェア