docker環境でcron実行してみる

## 概要
ローカル環境で、Laravelスケジュールを設定して、自動実行してみたくて調べたことをメモする。

## ファイル構成
以下のようなファイル構成で構築してみる。
※ ソースコード：[Github-reflet/laravel5.6](https://github.com/reflet/laravel5.6)

```
├ docker
│ ├ cron                          // ← cron実行する(PHP-CLI)
│ │ ├ cron.root
│ │ └ Dockerfile
│ │
│ └ php                           // ← Apacheから実行される(PHP-FPM)
│   ├ .dockerignore
│   ├ Dockerfile
│   ├ php.ini
│   └ www.conf
│
├ src                             // ← Laravel関連のフォルダ
│ ├ app
│   ...
│ └ yarn.lock
│
└ docker-compose.yml
```

## cronコンテナについて
phpコンテナ内でcronを起動するのが簡単かと思ったが、意外とうまくいかない。

※ 下記に記載されている症状が発生... まあCMD変えたら、そらそうなるよね... (＾＾；)
　 → [PHPコンテナのDockerファイルでCMDを書くとnginxコンテナと接続できなくなった](https://qiita.com/ntm718/items/05b4a1a750e551f45d9c)

そこで、PHPコンテナをベースにCRONコンテナを別途作成しました。

```Dockerfile:./docker/cron/Dockerfile
# PHP-FPMのイメージをベースに構築する
FROM my-laravel5.6/php:7.3

# cronインストール
RUN apt-get install -y cron

# cron設定を配置する
COPY ./docker/cron/cron.root /etc/cron.d/cron

# ログを標準出力へ出力できるようにシンボリックリンクを作成する
RUN chmod 0644 /etc/cron.d/cron && ln -sf /proc/1/fd/1 /var/log/cron.log

# フォアグラウンドでcronを起動する
CMD cron -f
```

## cron設定について
crontabの設定は以下のようにしました。

```bash:./docker/cron/cron.root
* * * * * root /usr/local/bin/php /var/www/www.example.com/artisan schedule:run >> /var/log/cron.log 2>&1
```
※ シンボリックリンク ( `/var/log/cron.log` )で、標準出力に接続する

## docker-composeの設定について
以下のようにcronとphpコンテナの２つを起動するようにしてみました。

```yaml:./docker-compose.yml
version: '3'
services:
  php:
    image: my-laravel5.6/php:7.3
    build:
      context: ./
      dockerfile: ./docker/php/Dockerfile
    tty: true
    ports:
      - '9000:9000'
    volumes:
      - src:/var/www/www.example.com:cached
      - vendor:/var/www/www.example.com/vendor
      - node_modules:/var/www/www.example.com/node_modules
    depends_on:
      - mysql

  cron:
    image: my-laravel5.6/cron:7.3
    build:
      context: ./
      dockerfile: ./docker/cron/Dockerfile
    tty: true
    volumes:
      - src:/var/www/www.example.com:cached
      - vendor:/var/www/www.example.com/vendor
      - node_modules:/var/www/www.example.com/node_modules
    depends_on:
      - php

    ...

volumes:
  vendor:
  node_modules:
  src:
    driver_opts:
      type: none
      device: ${PWD}/src
      o: bind
```

## サーバ起動
dockerを起動してみる。

```bash
$ docker-compose build
```

## スケジュール設定
テストメール送信クラスを追加する。

```bash
$ docker-compose exec php php artisan make:mail TestEMail
```
テストメール送信のコマンドを追加する。

```bash
$ docker-compose exec php php artisan make:command EmailSendCommand
```

スケジュール設定を追加する。

```php:./src/app/Console/Kernel.php
    protected function schedule(Schedule $schedule)
    {
        // ↓ 1分ごとに定期実行する
        $schedule
            ->command('email:test')   // 実行するコマンド
            ->everyMinute()           // 実行間隔: 毎分
        ;
    }
```

## cron確認
テストメール送信する `$ php artisan emai:test` コマンドを1分ごとに実行している。
下記コマンドにて、定期状況をログで確認してみる。

```bash
$ docker-compose logs cron
cron_1  | No scheduled commands are ready to run.
cron_1  | No scheduled commands are ready to run.
cron_1  | No scheduled commands are ready to run.
cron_1  | No scheduled commands are ready to run.
cron_1  | Running scheduled command: '/usr/local/bin/php' 'artisan' email:test > '/dev/null' 2>&1
cron_1  | Running scheduled command: '/usr/local/bin/php' 'artisan' email:test > '/dev/null' 2>&1
cron_1  | Running scheduled command: '/usr/local/bin/php' 'artisan' email:test > '/dev/null' 2>&1
cron_1  | Running scheduled command: '/usr/local/bin/php' 'artisan' email:test > '/dev/null' 2>&1
```

問題ないようですね。

## 参考資料
* [Laravel 5.6 タスクスケジュール](https://readouble.com/laravel/5.6/ja/scheduling.html)
* [Docker + Cron環境を実現する３つの方法](https://qiita.com/YuukiMiyoshi/items/bb7f14436d60d4bd8a8b)
* [Laravelでschedule:runしたときのoutputがdockerで標準出力されない問題を解消](https://qiita.com/imunew/items/6b210ea05b70885ce990)
* [LaravelにDockerファイルでcronを導入して定期的にメール送信する方法](https://zenn.dev/kiyo_tech/articles/8e79e9873ff687)

以上