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してメールする場合は工夫が必要そう。
コメント