定期的にコマンドを実行したい時がある。そんな時に使うのがcronというプログラムらしい。
cronは指定していた時間にコマンドを実行してくれる。
今日はそんなcronを初めて触ってみたらそれなりに躓いたので備忘録として書きます。
環境:
Laravel8,CentOS
とりあえずは実行したいプログラムのコマンドを作らなければいけない。
コマンドで実行するプログラムのファイルを作るには
# php artisan make:command testCommand
Console command created successfully.
これで作れる。
出来上がるものはこんな感じ。
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
class testCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'command:name';//コマンドはここで定義
/**
* 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()
{
return 0;//実行する内容はここ
}
}
まずはここにコマンドを登録する。
protected $signature = 'command:save_string';//コマンドはここで定義
今回は元々command:nameと書かれている箇所を変更して'command:save_string'に。
次は実際に実行する処理を書こう。
書く場所はここ
public function handle()
{
return 0;//実行する内容はここ
}
最終的にはこんな感じ。
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Models\Article;
use Weidner\Goutte\GoutteFacade as GoutteFacade;
class ArticleStoreBatch extends Command
{
private $md_articles;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'command:save_string';
/**
* The console command description.
*
* @var string
*/
protected $description = 'Command description';
/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
$this->md_articles = new Article;
}
/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
$data="ほぞんないよう";
$this->md_articles->store($data);
}
}
例として作成したのはコマンドを実行するとArticleのモデルを使ってテキストを保存する、だけのプログラム(雑)
Controllerなどと同じくモデルを使うときはuse 宣言が必要。
またArticleのインスタンスを作成するコードはconstructに書いた。
この時点でコマンドの登録は終わりなのでlaravelのディレクトリでコマンドを入力すれば実行できる
php artisan command:save_string
command:save_stringが実行されテキストが保存されたことが確認できる。
次はスケジューラに登録する。
laravel\app\Console\Kernel.php
にスケジュールをセットすることができる。
protected function schedule(Schedule $schedule)
{
$schedule->command('command:save_string')->everyMinute();
}
everyMinutes()なので毎分呼ばれる。
これでLaravel側は終わり。
これだけでは定期実行できない。
今回はCentOS8で実行したいが、なんとcentOS8にはcronが入っていなかった。
上記を参考にインストール。デフォルトで入っているanacronという別のcronがあるのであんいんすとーるもしないといけないらしい。
インスコしたら
# sudo vim /etc/crontab
でこんな感じのファイルが開くので編集する。
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
# For details see man 4 crontabs
# Example of job definition:
# .---------------- minute (0 - 59)
# | .------------- hour (0 - 23)
# | | .---------- day of month (1 - 31)
# | | | .------- month (1 - 12) OR jan,feb,mar,apr ...
# | | | | .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
# | | | | |
# * * * * * user-name command to be executed
~
~
~
~
~
~
編集後↓
SHELL=/bin/bash
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
# For details see man 4 crontabs
# Example of job definition:
# .---------------- minute (0 - 59)
# | .------------- hour (0 - 23)
# | | .---------- day of month (1 - 31)
# | | | .------- month (1 - 12) OR jan,feb,mar,apr ...
# | | | | .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
# | | | | |
# * * * * * user-name command to be executed
0,15,30,45 * * * * root cd /var/docker/project/backend && php artisan command:save_string
~
~
~
~
~
注意が必要なのはuser-nameを設定するのを忘れないこと。今回はrootと書いた。
忘れると後にこんな感じのエラーがでる。
● crond.service - Command Scheduler
Loaded: loaded (/usr/lib/systemd/system/crond.service; enabled; vendor preset: enabl>
Active: active (running) since Wed 2021-06-30 01:52:16 UTC; 2 days ago
Main PID: 801 (crond)
Tasks: 1 (limit: 11408)
Memory: 2.5M
CGroup: /system.slice/crond.service
mq801 /usr/sbin/crond -n
Jul 02 05:45:01 hogehogebatchserver crond[801]: (cd) ERROR (getpwnam() failed - user unk>
Jul 02 05:46:01 hogehogebatchserver crond[801]: (cd) ERROR (getpwnam() failed - user unk>
Jul 02 05:47:01 hogehogebatchserver crond[801]: (cd) ERROR (getpwnam() failed - user unk>
Jul 02 05:48:01 hogehogebatchserver crond[801]: (cd) ERROR (getpwnam() failed - user unk>
Jul 02 05:49:01 hogehogebatchserver crond[801]: (cd) ERROR (getpwnam() failed - user unk>
Jul 02 05:50:01 hogehogebatchserver crond[801]: (cd) ERROR (getpwnam() failed - user unk>
Jul 02 05:51:01 hogehogebatchserver crond[801]: (cd) ERROR (getpwnam() failed - user unk>
Jul 02 05:52:01 hogehogebatchserver crond[801]: (cd) ERROR (getpwnam() failed - user unk>
Jul 02 05:53:01 hogehogebatchserver crond[801]: (cd) ERROR (getpwnam() failed - user unk>
Jul 02 05:54:01 hogehogebatchserver crond[801]: (cd) ERROR (getpwnam() failed - user unk>
~
これで設定は完了。
一定時間ごとにcronによってコマンドが実行される。
ちなみにスケジューラーとcronで時間を設定することができるが今回はスケジューラでeveryMinuteなのに対しcronでは0,15,30,45と指定した。
この場合、cronは0,15,30,45分に実行されるのでスケジューラでeveryMinuteと書いていても毎分は動かない。
双方で異なる時間指定を行うと、時間が重なるタイミングでしか実行されないので注意。
実行結果は以下のコマンドで見れる
less /var/log/cron
ややこしくなるのでスケジューラを利用するならスケジューラで統一したほうがいいかもしれない。
完
番外編
ubuntuの場合
ubuntuの場合少しやり方が違う、らしい
当方ではlinuxサーバーの中にdockerでubuntu環境を構築していたのdocker内部でしか動かないコマンドはコンテナ外部のcronからでは動かなかった。
その為改めてコンテナ内部でcronを動かす必要があったがコンテナ内部はubuntuの為改めて動かす必要があった。
参考
aptでインストール
$ apt install -y cron
Reading package lists... Done
Building dependency tree
するとこんな感じのファイルが出来上がる
/etc/
├── cron.d
├── cron.daily
├── cron.hourly
├── cron.monthly
├── crontab
├── cron.weekly
コピーして編集しよう
cp /etc/crontab /etc/cron.d/hoge_cron
vim /etc/cron.d/hoge_cron
dockerコンテナないのコマンドを外部のcronから実行するには
docker exec -i [コンテナ名] /bin/bash -c "[コンテナの中で実行したいコマンド]"