☁️

Cloud Run ジョブ + Djangoで処理をバッググラウンド化してみる

2025/01/29に公開

初めに

業務で Django + CloudRun の環境を使用しており、時間がかかる処理をCloudRunジョブに移行することで、効率的にバックグラウンド処理を行えるようにしました。本記事では、その構築手順を解説します。CloudRunジョブを初めて触る方でも、この記事を参考に実装できるように説明しています。

対象読者

  • CloudRunサービスは知っているけど、CloudRunジョブは触ったことがない方
  • Django(Python)からCloudRunジョブを実行する方法を知りたい方

記事で触れること

  • Django(Python)からCloudRunジョブを実行する
  • ジョブ実行時のコマンド引数をカスタムして実行する

記事で触れないこと

  • Python、Djangoの基本操作
  • デプロイ周りの詳細


CloudRunジョブについて

CloudRunはサービスとジョブの2種類に分かれています。ジョブは一連のタスクを実行する非同期処理の機能で、サービスとは異なりHTTPリクエストをリッスンしません。任意のタイミングやスケジュールに沿った実行が可能で、特に長時間実行する処理やバッチタスクに適しています。

詳細については公式ドキュメントをご覧ください
ジョブを作成する  |  Cloud Run Documentation  |  Google Cloud


システム要件

システム要件は以下とします

  • エンドポイントjob/{id}でCloudRunジョブを実行することができる
    • idは任意の数値を指定可能。idをCloudRunジョブに引数として渡す
  • CloudRunジョブは、渡された引数idをログ出力する


実装

カスタムコマンドを作成

CloudRunジョブで実行するDjangoのカスタムコマンドを作成します。

  • app/management/commands/job.py

    from django.core.management.base import BaseCommand, CommandError
    
    class Command(BaseCommand):
      def add_arguments(self, parser):
        parser.add_argument('--id', type=int, required=True)
    
      def handle(self, **kwargs):
        id = kwargs['id']
        print(f'ジョブを実行しています。引数id: {id}が渡されました')
    
  • このカスタムコマンドは、以下のように実行できます。

    python manage.py job —-id={id}
    

CloudRunジョブを実行するエンドポイントを用意

DjangoアプリケーションからCloudRunジョブを実行するエンドポイントを用意します

  • urls.py

    from django.urls import path
    from app import views
    
    urlpatterns = [
        path('job/<int:id>', views.run_job) 
    ]
    
  • views.py

    from django.http import HttpResponse
    from django.conf import settings
    
    from google.cloud import run_v2
    
    def run_job(self, id):
      overrides = run_v2.types.RunJobRequest.Overrides(
          container_overrides=[
              run_v2.types.RunJobRequest.Overrides.ContainerOverride(
                  args=['manage.py', 'job', f'--id={id}'],
              )
          ]
      )
      request = run_v2.RunJobRequest(
          name=f'projects/{settings.PROJECT_ID}/locations/asia-northeast1/jobs/{settings.JOB_NAME}',
          overrides=overrides
      )
      client = run_v2.JobsClient()
      client.run_job(request=request)
    
      return HttpResponse('CloudRunジョブを実行しました。')
    
    • google-cloud-runライブラリのインストール
    • ポイント
      • RunJobRequestでジョブ名と引数を指定
      • client.run_jobでジョブの実行をトリガー
    詳細
    • client.run_job
      ジョブ実行をトリガーできます。この時、ジョブ実行を作成するためのパラメーター requestを引数に取ります
    • request(run_v2.RunJobRequest)
      • name
        CloudRunJobのジョブ名を指定します。フォーマットはprojects/{project}/locations/{location}/jobs/{job}になります
      • overrides(run_v2.types.RunJobRequest.Overrides)
        • ジョブの仕様を上書きします。
        • container_overrides(シーケンス[run_v2.types.RunJobRequest.Overrides.ContainerOverride])
          - コンテナごとのオーバーライド指定になります。今回はargsにて、リクエストから受け取ったidで上書きする構成にしています。
          他にenvclear_argsを用いて環境変数を上書きしたり、元々設定されている引数を消去できます。詳細は以下をご覧ください
          Class ContainerOverride (0.10.14)  |  Python client library  |  Google Cloud

Dockerイメージを作成・保存

ArtifactRegistryにイメージを保存します。

  1. Dockerfileの準備

  2. ビルドと保存

    gcloud builds submit --region={region-name} --tag gcr.io/{PROJECT_ID}/{IMAGE_NAME} .
    

CloudRunジョブを作成

Google Cloud ConsoleからCloudRunジョブを作成します。保存したDockerイメージを使用し、デフォルトコマンド引数として先ほど作成したカスタムコマンドを登録します

作成後、ジョブを実行して、ログで成功メッセージを確認します。

ジョブを実行しています。引数id: 1が渡されました

検証

  • CloudRunジョブと同様の手順でサービスもデプロイします。
  • デプロイ後、サービスのエンドポイント/job/{id}にリクエストを送信し、CloudRunジョブが正しく実行されていることを確認します。

例: /job/2を実行した場合、ログに以下のメッセージが出力されることを確認できます。

ジョブを実行しています。引数id: 2が渡されました

補足

バックグラウンド処理を行う他の選択肢として、GCPのCloud Functionsがありますが、以下の違いがあります。

  • CloudRunジョブ
    • 最大実行時間: 24時間
    • バッチ処理や重たいタスクに適している
  • Cloud Functions
    • 最大実行時間: 9分(HTTPトリガーは最大60分)
    • 軽量なイベント駆動型の処理に適している


終わりに

CloudRunジョブを活用することで、処理のバックグラウンド化を比較的簡単に実現できることが分かりました。特に、長時間実行が必要な重たい処理には非常に適しており、他の選択肢よりも柔軟性があります。

この記事が、Djangoアプリでのバックグラウンド化において少しでも参考になれば幸いです。

最後までお読みいただき、ありがとうございました!


参考

https://cloud.google.com/run/docs/create-jobs?hl=ja

https://cloud.google.com/run/docs/execute/jobs?hl=ja

https://cloud.google.com/run/docs/configuring/jobs/containers?hl=ja

Discussion