laravelのコマンドをCronで定期実行したかった

定期的にコマンドを実行したい時がある。そんな時に使うのがcronというプログラムらしい。
cronは指定していた時間にコマンドを実行してくれる。

今日はそんなcronを初めて触ってみたらそれなりに躓いたので備忘録として書きます。

環境:
Laravel8,CentOS


とりあえずは実行したいプログラムのコマンドを作らなければいけない。

コマンドで実行するプログラムのファイルを作るには

```
# php artisan make:command testCommand
Console command created successfully.
```

これで作れる。

出来上がるものはこんな感じ。

```testCommand.php
<?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;//実行する内容はここ
    }
}
```
まずはここにコマンドを登録する。

```testCommand.php
    protected $signature = 'command:save_string';//コマンドはここで定義
```

今回は元々command:nameと書かれている箇所を変更して'command:save_string'に。

次は実際に実行する処理を書こう。
書く場所はここ

```testCommand.php
    public function handle()
    {
        return 0;//実行する内容はここ
    }
```


最終的にはこんな感じ。

```testCommand.php
<?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
にスケジュールをセットすることができる。

```Kernel.php
    protected function schedule(Schedule $schedule)
    {
        $schedule->command('command:save_string')->everyMinute();
    }
```
everyMinutes()なので毎分呼ばれる。

これでLaravel側は終わり。

これだけでは定期実行できない。
今回はCentOS8で実行したいが、なんとcentOS８にはcronが入っていなかった。

https://linux.perlzemi.com/blog/20200118163235.html

上記を参考にインストール。デフォルトで入っている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の為改めて動かす必要があった。
参考

https://qiita.com/ponsuke0531/items/3e82df8bd8412983cb33

https://qiita.com/UNILORN/items/a1a3f62409cdb4256219

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から実行するには

https://naughtldy.hatenablog.jp/entry/2017/10/25/160000


docker exec -i [コンテナ名] /bin/bash -c "[コンテナの中で実行したいコマンド]"