目的
通常サーバのタスクスケジューラはcronにて行うが、
- 少し面倒であること
- Laravelにタスクスケジューラの機能があること
から、画面を作成して画面経由でタスクスケジュール設定を行えるものを作成してみる。
前提
下記の環境で実装した。
- PHP7.1.x
- Laravel6.x+AdminLTE3
- PostgreSQL11.4
- Docker phpfpm
サンプル仕様
サーバのタスクスケジュールのため、画面操作するためにログインが必要とした。
タスク自体は別途実装済みであるとして、タスク名と実行スケジュールを設定する画面を
用意する。
タスクの実行はLaravelにタスクスケジューラをcron経由で実行するものとして、タスクスケジューラの設定を先の画面で設定したものを反映させるものとする。
タスクの実装
詳細は省略するが、タスクの雛形をartisanで作成できる。
% php artisan make:command タスク名
とりあえず、タスクが動いたか確認したいため、下記のタスクを実装した。
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\Log;
class Shout extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'command:shout';
〜省略〜
/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
Log::info("start shout!!");
}
}
見ての通り、実行されたらログが出力されるだけのタスクである。
モデル定義
定義するモデルはScrapingCommandである。
migrateの内容を下記に示す。
?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateScrapingCommandsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('scraping_commands', function (Blueprint $table) {
$table->bigIncrements('id');
$table->string("name",200);
$table->string("cmd",200);
$table->integer("cron1")->nullable();
$table->integer("cron2")->nullable();
$table->integer("cron3")->nullable();
$table->integer("cron4")->nullable();
$table->integer("cron5")->nullable();
$table->integer("flg")->default(0);
$table->softDeletes();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('scraping_commands');
}
}
画面実装
ログイン画面
タスク一覧画面
登録されているタスクの一覧表示している。
タスクスケジュールはcronと同じ形式である。スケジュールとは別に有効/無効の設定が可能である。
タスク設定画面
タスクの設定を行う。コマンド名はartisanで使用するコマンド名($signatureに設定しているもの)を設定する。
設定内容はScrapingCommandモデルに保存される。
Laravel側のスケジュール定義
LaravelのタスクスケジュールはApp\Console\Kernelのscheduleメソッドで定義するため、
同メソッドを下記のように修正した。
<?php
namespace App\Console;
use Illuminate\Console\Scheduling\Schedule;
use Illuminate\Support\Facades\Log;
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
use App\ScrapingCommand;
class Kernel extends ConsoleKernel
{
〜省略〜
/**
* Define the application's command schedule.
*
* @param \Illuminate\Console\Scheduling\Schedule $schedule
* @return void
*/
protected function schedule(Schedule $schedule)
{
$lst = ScrapingCommand::where("flg",1)->get();
foreach($lst as $rec) {
Log::info("cmd:".$rec->cmd);
Log::info("cron:".$rec->cronStr());
$schedule->command($rec->cmd)->cron($rec->cronStr());
}
}
〜省略〜
}
Cron側の設定
Cron側はLaravelのタスクスケジューラを実行するだけなので、
* * * * * /usr/local/bin/php /svr/app/artisan schedule:run >> /var/log/cron.log 2>&1
(注)/svr/appはLaravelをインストールしたディレクトリ
本来はcrontab -e で上記のファイルを作成することになるが、今回はDockerなので、
rootファイルとして作成しておく。
以上で、要望を実装済みであるが、今回はDockerで実装したため、コンテナ上でcronのインストールが必要になる。
そこで、cronのインストールはDockerイメージ作成時に行う。よってDockerファイルは
FROM php:fpm
# For composer
RUN apt-get update \
&& apt-get install -y gcc make zlib1g-dev libzip-dev libpng-dev unzip libmcrypt-dev libjpeg-dev libfreetype6-dev \
&& docker-php-ext-install zip \
&& docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ --with-png-dir=/usr/include \
&& docker-php-ext-install -j$(nproc) gd
# Install composer
RUN php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" \
&& php composer-setup.php \
&& php -r "unlink('composer-setup.php');" \
&& mv composer.phar /usr/local/bin/composer
# Set composer environment
ENV COMPOSER_ALLOW_SUPERUSER 1
ENV COMPOSER_HOME /composer
ENV PATH $PATH:/composer/vendor/bin
# Install laravel installer
RUN composer global require "laravel/installer"
# PHP's DB setting
RUN apt-get update \
&& apt-get install -y libpq-dev \
&& docker-php-ext-install pdo_mysql pdo_pgsql
# Install Node.js
RUN curl -sL https://deb.nodesource.com/setup_6.x | bash - \
&& apt-get update \
&& apt-get install -y nodejs
RUN cp /usr/share/zoneinfo/Asia/Tokyo /etc/localtime
RUN apt-get install -y cron
RUN mkdir /debugbar
RUN chmod go+w /debugbar
WORKDIR /svr
とした。
次にcrontabの登録とcron起動が必要になるが、今回は手動で行うものとした。
よって、上記で作成したイメージからコンテナを起動して、コンテナ上で
% cp root /var/spool/cron/crontab
% service cron start
を実行する。
rootファイルはあらかじめ/svrにコピーしておくこと。
考察
cronの設定って結構面倒なものである。何よりわざわざサーバにログインして作業しないと行けないのである。
この仕組みだと、画面で設定するだけなので大分楽である。タスクはLaravel上で動くため、
DB接続とかログ出力とかの考慮が必要なくなる。
問題点は
- crontabの設定とcronの起動をコンテナ起動後手動で行わなければならない。
Dockerイメージでcronの起動もできるらしいが、今回は割愛した - 5分毎等のスケジュールに対応できてない
これは実装の問題であるが。。。
サンプルで実装したプロジェクトはGitHubにアップしたので、必要であれば参照していただきたい。