Cloud RunにデプロイしたLaravelアプリケーションをCloud Schedulerで定期実行

こんにちは、tomatsuriです。

Cloud Schedulerを触ってみたいという衝動に駆られ、Cloud RunにLaravelアプリケーションをデプロイし、Cloud Schedulerで定期実行するサンプルを作成してみました。

Laravelアプリケーションの作成

まず、Cloud Schedulerから定期的にアプリケーションが実行されていることを確認するために、/batchにGETリクエストをしたらログに"batch executed.“と出力するだけの簡単な機能を用意しておきます。

Cloud Runは標準出力に出力したログはCloud Loggingによって自動的に取得されるため1、Laravelから標準出力にログを出力するためにconfig/logging.phpのchannelsに以下を追加します。

'stdout' => [
    'driver' => 'monolog',
    'handler' => StreamHandler::class,
    'with' => [
        'stream' => 'php://stdout',
    ],
],

(Laravel8系を使用)

あとは環境変数のLOG_CHANNELをstdoutに書き換えればOKです。 環境変数はCloud Run上で設定が可能です。2

Dockerイメージの作成

以下のようなDockerfileをプロジェクトのルートに用意します。 Cloud RunにLaravelをデプロイする方法としてApacheを使用している例をいくつか見たので、 今回はnginxでやってみました。

FROM php:8.0-fpm-alpine

RUN apk add --no-cache nginx wget

RUN mkdir -p /run/nginx
COPY docker/nginx.conf /etc/nginx/nginx.conf

RUN mkdir -p /app
COPY . /app

RUN sh -c "wget http://getcomposer.org/composer.phar && chmod a+x composer.phar && mv composer.phar /usr/local/bin/composer"
RUN cd /app && \
    /usr/local/bin/composer install --no-dev

RUN chown -R www-data: /app

CMD sh /app/docker/startup.sh

nginx.confとstartup.shはこちら。 ドキュメント3に従い、PORT環境変数で定義されたポートをリッスンするように構成しています。

worker_processes  1;

events {
    worker_connections  1024;
}
http {
    include mime.types;
    keepalive_timeout 65;

    server {
        listen LISTEN_PORT default_server;
        server_name _;
        root /app/public;
        index index.php;
        charset utf-8;
        location / {
            try_files $uri $uri/ /index.php?$query_string;
        }

        location = /favicon.ico { access_log off; log_not_found off; }
        location = /robots.txt  { access_log off; log_not_found off; }
        access_log /dev/stdout;
        error_log /dev/stderr;

        client_max_body_size 100m;

        location ~ \.php$ {
            fastcgi_split_path_info ^(.+\.php)(/.+)$;
            fastcgi_pass 127.0.0.1:9000;
            fastcgi_index index.php;
            include fastcgi_params;
            fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
            fastcgi_param PATH_INFO $fastcgi_path_info;
        }
    }
}

daemon off;
#!/bin/sh

sed -i "s,LISTEN_PORT,$PORT,g" /etc/nginx/nginx.conf

php-fpm -D

nginx

startup.shはchmod a+x docker/startup.shでパーミッションを変更しておきます。

これらはプロジェクトのルートにdockerフォルダを作成し、その中に配置しています。

Dockerfile、nginx.conf、startup.shが用意できたらdocker build -t laravel-batch01 . を実行してビルドします。

ビルドが完了したら、docker run -e PORT=80 -p 8080:80 laravel-batch01で起動し、localhost:8080でアクセスして確認してみましょう。

Container Registryへイメージをpush

まずはビルドしたイメージにレジストリ名をタグ付けします。4

docker tag laravel-batch01 asia.gcr.io/[PROJECT-ID]/laravel-batch01

次に、Container Registryへアクセスするための認証を行います。今回は推奨とされているgcloud 認証情報ヘルパーを使用します。5

ユーザーまたはサービスアカウント(推奨)で認証を行い、gcloud auth configure-dockerでgcloudをDockerの認証ヘルパーとして設定します。

これでタグ付きイメージをContainer Registryにpushできるようになりました。

docker push asia.gcr.io/[PROJECT-ID]/laravel-batch01

でpushし、Cloud ConsoleのContainer Registry上で確認できればOKです。

Cloud Runを作成

laravel-batch01という名前のCloud Run サービスを作成します。

Cloud Console上でCloud Runのページにアクセスし、サービスの作成へ進みます。

Cloud Console Cloud Run 作成画面1

「サービスの最初のリビジョンの構成」のコンテナイメージのURLは、先程pushしたイメージを選択します。

Container Registryのイメージ詳細から直接作成することも可能です。

Cloud Console Cloud Run 作成画面2

最後の「このサービスをトリガーする方法の構成」では、 Ingressは「すべてのトラフィックを許可する」を選択します。

現時点(2021/05/14)ではCloud Schedulerから内部サービスに対してhttpリクエストすることはできません。6

認証は「認証を必要とする」を選択し、作成します。

Cloud Console Cloud Run 作成画面3

Cloud SchedulerからCloud Runを起動するためのサービスアカウントを作成

このままCloud SchedulerからCloud Runへhttpリクエストをしても、権限がなく失敗してしまいます。

そこで、作成したCloud Runを起動する権限を持つサービスアカウントを作成し、それを使用してCloud SchedulerからCloud Runへリクエストするように設定します。

ここではlaravel-batch01-invokerという名前のサービスアカウントを作成し、以下のコマンドで権限を付与します。

gcloud run services add-iam-policy-binding laravel-batch01 \
    --member=serviceAccount:[サービスアカウントのメールアドレス] \
    --role=roles/run.invoker

Cloud Run詳細ページの権限タブを開き、Cloud Run 起動元の権限を持つサービスアカウントが追加されていればOKです。

Cloud Schedulerの設定

「ジョブを定義する」で名前、頻度(unix-cron形式)、タイムゾーンを設定します。

「ジョブのターゲットを構成する」で、どこにどのようなリクエストを送信するのかを設定します。 今回は以下のようになります。

  • ターゲットタイプ:HTTP
  • URL:Cloud Runにデプロイしたアプリケーションのエンドポイント
  • HTTPメソッド:GET

Authヘッダーは「OIDC トークンを追加」を選択し、先程作成したサービスアカウント(laravel-batch01-invoker)のメールアドレスを入力します。

Cloud Console Cloud Scheduler 作成画面

「詳細設定を構成する」でリトライ回数などを設定できますが、今回はデフォルトのままにします。

ジョブを作成したら、ジョブが実行されるまで待ってもよいですし、「今すぐ実行」ボタンで任意のタイミングで実行することもできます。

実行結果

ジョブの結果が「成功」となり、Cloud Runのログにアプリケーションで実装したログのメッセージが出力されていれば完了です。

Cloud Console Cloud Run ログ画面

おわり

Cloud Run、Cloud Schedulerを使用して手軽にバッチ処理のサンプルを作成できました。

ちなみにCloud Scheduler料金は実行単位ではなくジョブ単位です。

請求アカウント毎(プロジェクト毎ではない)に3ジョブの無料枠があり、課金対象となってもジョブあたり$0.10/月とお手頃です。

今後はCloud Schedulerのhttp以外のターゲットを使ってみたり、他のサービスを使用したバッチ処理のアーキテクチャなど色々試していけたらなと思います。

脚注


  1. Cloud Run ドキュメント ログの記録と表示 コンテナログを作成する ↩︎

  2. Cloud Run ドキュメント 環境変数の使用 ↩︎

  3. Cloud Run ドキュメント コンテナポートの構成 ↩︎

  4. Container Registry ドキュメント ローカル イメージにレジストリ名でタグ付けする ↩︎

  5. Container Registry ドキュメント gcloud 認証情報ヘルパー ↩︎

  6. Cloud Run ドキュメント 内部サービスへのアクセス ↩︎