laravel8のscheduleで定期実行する方法

laravel

laravel8でコマンドなどを定期的に実行する方法について。要するにcronのlaravelバージョン。

プロジェクトの作成

まずは確認用のプロジェクトを作成する。作業ディレクトリのベースは/home/ubuntu

$ composer create-project laravel/laravel sandbox --prefer-dist

設定ファイルの編集

環境に関する設定(local, staging, production等)とメールを送るのでメールに関連する設定を変更する。環境については今回はstagingとして動作させてみる。

$ cd sandbox
$ vi .env
:
APP_ENV=staging
:
MAIL_MAILER=sendmail
MAIL_HOST=mailhog
MAIL_PORT=1025
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS=no-reply@ukkari-san.net
MAIL_FROM_NAME="うっかりさん"
:

cronの変更

cronへlaravelスケジュールを起動するように設定する。

$ sudo vi /etc/crontab
#通常はこちらを使用
#* * * * * ubuntu cd /home/ubuntu/sandbox && php artisan schedule:run >> /dev/null 2>&1
#今回は起動ログを確認するためにこちらを使用
* * * * * ubuntu cd /home/ubuntu/sandbox && php artisan schedule:run >> /home/ubuntu/sandbox/storage/logs/schedule.log 2>&1

コマンドクラスの作成

スケジューリング対象とするコマンドのクラスを生成する。

$ php artisan make:command SampleScheduleBatch
$ vi app/Console/Commands/SampleScheduleBatch.php 

簡単に、現環境をログと画面に出力するクラスを作成。

<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Support\Facades\Log;

class SampleScheduleBatch extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'batch:sample-schedule';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Command description';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return int
     */
    public function handle()
    {
        // 環境を取得
        $environment = \App::environment();
        // ログに出力
        Log::info($environment);
        // 画面に出力
        echo $environment;
        //sleep(180);
        return 0;
    }
}

一度この状態でコマンドクラスが動くか動作確認を行う。

$ php artisan batch:sample-schedule

ログファイルが出力されていればOK。

$ tail storage/logs/laravel.log
[2021-03-07 23:36:01] staging.INFO: staging 

スケジュールクラスの編集

スケジュールのクラスファイルを変更して、指定時間に実行されるように変更する。

<?php

namespace App\Console;

use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
use Illuminate\Support\Stringable;
use \Artisan;

class Kernel extends ConsoleKernel
{
    /**
     * The Artisan commands provided by your application.
     *
     * @var array
     */
    protected $commands = [
        //
    ];

    /**
     * Define the application's command schedule.
     *
     * @param  \Illuminate\Console\Scheduling\Schedule  $schedule
     * @return void
     */
    protected function schedule(Schedule $schedule)
    {
        // $schedule->command('inspire')->hourly();

        $schedule->command('batch:sample-schedule')
            // 2分間隔で実行
            ->cron('*/2 * * * *')
            // 環境(APP_ENV)がstagingかproductionの場合に実行
            ->environments(['staging', 'production'])
            // バックグラウンド実行
            ->runInBackground()
            // 多重起動を禁止し、最高10分まで待つ
            ->withoutOverlapping(10)
            // 結果をファイルに上書き出力する。
            ->sendOutputTo('/home/ubuntu/sandbox/storage/logs/sample-schedule.log')
            // 結果をファイルに追加出力する。
            //->appendOutputTo('/home/ubuntu/sandbox/storage/logs/sample-schedule.log')
            // 成功時にメールを送信する。
            ->emailOutputTo('success@example.com')
            // 失敗時にメールを送信する。
            ->emailOutputOnFailure('failure@example.com')
            // 成功時にコマンドを呼ぶ
            ->onSuccess(function (Stringable $output) {
                 // ここでは別記事で作成したログ出力バッチを呼んでいる
                 Artisan::call('batch:sample-log');
            });
    }

    /**
     * Register the commands for the application.
     *
     * @return void
     */
    protected function commands()
    {
        $this->load(__DIR__.'/Commands');

        require base_path('routes/console.php');
    }
}

メソッドの説明 時間関連

時間に関連するメソッドについて。基本的にはcronでほとんどのパターンが網羅できるので、個人的にはcronを使うのが好き。月の最終日指定は月次処理などで便利かもしれない。より詳しいメソッドの種類については本家参照。

メソッド説明
->cron(‘* * * * *’)Cron指定に基づき実行。基本これだけあれば大抵の時間指定は可能。
’分 時 日 月 曜日’の順。
->everyMinute()毎分実行。
->cron(‘* * * * *’)と同じ。
->hourlyAt(17)毎時17分に実行。
->cron(’17 * * * *’)と同じ。
->dailyAt(’13:00′)毎日13:00に実行
->cron(‘0 13 * * *’)と同じ。
->weeklyOn(1, ‘8:00’)毎週月曜日の8:00時に実行。
->cron(‘0 8 * * 1’)と同じ。
->monthlyOn(4, ’15:00′)毎月4日の15:00に実行
->cron(‘0 15 4 * *’)と同じ。
->lastDayOfMonth(’15:00′)毎月最終日の15:00時に実行
cronはスクリプトを書かないと無理なので省略。

メソッドの説明 その他

時間以外のメソッドについて。

メソッド説明
->environments([‘staging’, ‘production’]).envのAPP_ENVがstagingかproductionの場合のみ実行
->runInBackground()バックグラウンドで実行する。これを指定しなかった場合は同時にスケジューリングされたタスクも逐次実行される。
->withoutOverlapping(10)多重起動を禁止する。引数の数値は分で指定可能。指定しない場合は24時間待機する模様。
->sendOutputTo(‘/path/to/file’)実行結果をファイル出力する。出力は上書きされる。
->appendOutputTo(‘/path/to/file’)実行結果をファイル出力する。出力は追記される。
->emailOutputTo(‘mail@example.com’)正常完了時にメールを送信する。
->emailOutputOnFailure(‘mail@example.com’)異常終了時にメールを送信する。
->onSuccess(function (Stringable $output) {
// 成功時に実行する処理
});
正常完了時に実行する処理があれば記述する。

その他検証

多重起動防止の->withoutOverlapping() が実際に効いているかの確認。SampleScheduleBatchのsleep(180)の部分をコメントインして確認する。しばらく放置した後のログの内容は以下。今回のバッチの内容では、2分毎に実行されるがsleep(180)で3分処理に時間がかかるので、1回おきに実行されればOK。

$ tail /home/ubuntu/sandbox/storage/logs/schedule.log
Running scheduled command: ('/opt/remi/php73/root/usr/bin/php' 'artisan' batch:sample-schedule > '/dev/null' 2>&1 ; '/opt/remi/php73/root/usr/bin/php' 'artisan' schedule:finish "framework/schedule-579f0822ad21c3707a6574c81f1091b47431c980" "$?") > '/dev/null' 2>&1 &
No scheduled commands are ready to run.
Running scheduled command: ('/opt/remi/php73/root/usr/bin/php' 'artisan' batch:sample-schedule > '/dev/null' 2>&1 ; '/opt/remi/php73/root/usr/bin/php' 'artisan' schedule:finish "framework/schedule-579f0822ad21c3707a6574c81f1091b47431c980" "$?") > '/dev/null' 2>&1 &
No scheduled commands are ready to run.

余談だが、->appendOutputTo()で出力した後に->emailOutputTo()でメールを送ると今までの結果が全て送られてきたのでappendしてメールする場合は工夫が必要そう。

コメント

タイトルとURLをコピーしました