RitoLabo

LaravelのコマンドスケジューラでDBのバックアップを行う定時処理(Cron)を構築する

  • 公開:
  • 更新:
  • カテゴリ: PHP Laravel
  • タグ: PHP,Laravel,cron,5.5,5.4,5.3,Command,5.6

PHPフレームワークでWEBアプリケーションを構築しているのであれば、cronに登録し定時処理を行うプログラムもそのフレームワーク内で書くべきです。そうする事によって、データベースなどの設定情報が散らばってしまう事を防げますし、フレームワーク内に定時処理のソースがある事で管理も楽になります。

という事で今回は、Laravelのコマンドスケジューラを使い、Cronに登録して動かす、いわゆる定時処理を実装していきます。「データベースのバックアップ」を例に進めていきたいと思います。

アジェンダ
  1. 開発環境
  2. Commandクラスの生成
  3. Commandクラスの書式
  4. 処理の記述
  5. Commandクラスの登録
  6. 処理をスケジュールに登録
  7. cronへスケジューラを登録
  8. 二重起動の防止ロック

開発環境

今回の開発環境に関しては以下の通りです。

  • Linux CentOS 7
  • Apache 2.4
  • MySQL 5.7
  • PHP 7.1/7.2
  • Laravel 

Laravelのバージョンについては、5.6/5.5/5.4/5.3にて動作確認済みです。

Laravelのルートディレクトリを「laravel/」とします。

Commandクラスの生成

まずは処理を記述する為のCommandクラスを生成します。
laravelディレクトリに移動し、以下のartisanコマンドを叩きます。

# laravel ルートディレクトリへ移動
cd /path/to/projectroot/laravel

# 処理を記述するファイルの生成
php artisan make:command BackupDatabaseCommand --command="command:backupdb"

捕捉すると、

  1. artisanコマンドの「make:command」でCommandクラスを生成
  2. 引数にクラス名を指定
  3. commandオプションを付け、ファイル生成時に$signatureへ値を入力している。

となります。(※3に関しては後述します。)

[demo@localhost laravel]$ php artisan make:command BackupDatabaseCommand --command="command:backupdb"
Console command created successfully.

コマンドを叩くと、laravel/app/Console/ 直下に Commands ディレクトリが生成され、その中に BackupDatabaseCommand.phpファイルが作成されます。

laravel
├── app
│   ├── Console
│   │   ├── Commands
│   │   │   └── BackupDatabaseCommand.php
│   │   └──
Kernel.php

Commandクラスの書式

生成したCommandクラスを開くと、デフォルトでは以下のような記述となっています。

laravel/app/Console/Commands/BackupDatabaseCommand.php
<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;

class BackupDatabaseCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'command:backupdb';

/**
* 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 mixed
*/
public function handle()
{
//
}
}

上から説明していきます。

protected $signature = 'command:backupdb';

コンソールコマンドの名前を指定しています。
この部分は、ファイルを生成した時にcommandオプションで渡した文字列が格納されています。
そして、この文字列をartisanコマンドから渡す事で、処理が実行される事になります。
「backupdb」がこの処理の識別子で、「command:」は、artisanコマンド上でのグルーピングを表しています。

protected $description = 'Command description';

コンソールコマンドの説明を記述します。
多くのCommandクラスを作る場合は、ここは書いておくとより良いです。

public function __construct()
{
parent::__construct();
}

見ての通りのコンストラクタです。
初期値設定など行いたい場合はここへ記述出来ます。

public function handle()
{
//
}

ここにメインの処理を記述していきます。

処理の記述

では、生成したBackupDatabaseCommand.phpを開き、データベースのバックアップを行う処理を記述していきます。

<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;

class BackupDatabaseCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'command:backupdb';

/**
* The console command description.
*
* @var string
*/
protected $description = 'Command description';

protected $db_host;
protected $db_user;
protected $db_pass;
protected $db_name;
protected $store_path;

/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();

$this->db_host = env('DB_HOST'); // DBホスト
$this->db_user = env('DB_USERNAME'); // DBユーザ
$this->db_pass = env('DB_PASSWORD'); // DBパスワード
$this->db_name = env('DB_NAME'); // バックアップ対象スキーマ
$this->store_path = '/tmp'; // 保存先ディレクトリ

}

/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
// ファイル名
$file_name = sprintf('%s.sql', date('YMDHis'));
// ファイルフルパス
$file_path = sprintf('%s/%s', $this->store_path, $file_name);

$command = sprintf(
'mysqldump --single-transaction -h %s -u %s -p %s %s > %s',
$this->db_host,
$this->db_user,
$this->db_pass,
$this->db_name,
$file_path
);

exec($command, $output, $ret);
}
}

mysqldumpしているだけの、シンプルなものです。
protectedでメンバ変数を宣言し、コンストラクタで初期値設定、そしてhandleメソッドにデータベースのバックアップ処理を記述します。

ちなみに補足ですが、MySQL5.7ではデフォルトでコマンド内でのパスワード直書きを許容していないので、その場合は別途あれこれする必要があります。

Commandクラスの登録

処理を記述したら、Commandクラスが使えるように登録を行います。

laravel/app/Console/Kernel.php
protected $commands = [
//
];

// ↓ ここに記述

protected $commands = [
\App\Console\Commands\BackupDatabaseCommand::class,
];

登録した事により、artisanコマンドで手動実行も可能になりました。
laravelルートディレクトリから以下を叩くと実行されます。

php artisan command:backupdb

一度コマンドを叩き、正常に動作するかを確認しておきましょう。

処理をスケジュールに登録

いよいよ、作った処理をスケジュールへ登録します。現段階では手動での実行のみですが、スケジュールに登録する事によって、定時処理として動作させる事が出来るようになります。

Kernel.phpを開き、scheduleメソッドへ以下のように記述します。

laravel/app/Console/Kernel.php
protected function schedule(Schedule $schedule)
{
// 毎日深夜12時に実行
$schedule->command('command:backupdb')->daily();
}

ここでは、
「 command:backupdb を毎日、深夜0時に実行する」
という記述になっています。
尚、その他の記述方法としては以下になります。

cron('* * * * * *');
cronと同じ記法で記述する
everyMinute();
毎分
everyFiveMinutes();
5分毎
everyTenMinutes();
10分毎
everyFifteenMinutes();
15分毎
everyThirtyMinutes();
30分毎
hourly();
1時間毎
hourlyAt(23);
毎時23分
daily();
毎日夜0時
dailyAt('18:45');
毎日18:45
twiceDaily(4, 20);
毎日4:00と20:00時
weekly();
毎週実行
monthly();
毎月実行
monthlyOn(5, '19:00');
毎月5日の19:00
quarterly();
四半期ごと
yearly();
毎年実行
timezone('America/New_York');
タイムゾーン設定

cronへスケジューラを登録

最後に、cronへlaravelスケジューラを登録します。
laravelスケジューラ用にcron設定ファイルを作成し、その中へ記述します。

# cron設定ファイルの置き場所へ移動
cd /etc/cron.d

# laravelスケジューラの作成・記述
vim laravel_schedule
###################################################
* * * * * php /var/www/html/project_root/laravel/artisan schedule:run >> /dev/null 2>&1
###################################################

上記の記述でlaravelスケジューラが毎分実行され、その都度、実行するべきタスクがあれば実行させてくれます。

尚、cronの稼働確認は「/var/log/cron」で確認できますので、以下コマンドなど、適当に叩いて確認すると良いです(エラーはロギングされません)

# tailコマンドでログをwatchする
tail -f /var/log/cron

# tailコマンドでログの最新10件を表示する
tail -n 10 /var/log/cron

二重起動の防止ロック

例えば毎分やそれに近いような、頻繁に実行を行う定時処理を動かしている場合に、もし次の実行までに前の処理が終わらなかったら処理が二重にかぶって動作してしまいます。これが溜まってしまうとWEBサーバが落ちたりデータの整合性が取れなくなったりと、悲惨な事になりかねません。

Laravelのタスクスケジューラでは、withoutOverlapping()メソッドをチェインする事で、処理を起動しようとした際に前に処理がまだ動いている場合は待機してくれます。

laravel/app/Console/Kernel.php
protected function schedule(Schedule $schedule)
{
// 5分毎に実行
$schedule->command('command:backupdb')->everyFiveMinutes()->withoutOverlapping();
}

まとめ

以上で作業は完了となります。
cronの設定ファイルって、動かすものが増えると記述が多くなり、 あとで見返すとごちゃごちゃしていて至極煩雑なんですが、 laravelスケジューラの場合は、cronに記述するのはたったの1行だけで良いのでとても楽です。
あとは今回の手順の通りにCommandクラスを生成し、スケジュールに登録するだけで実行される。 とても便利な機能です。
実行日時などを確認する際も、いちいちサーバへ入らなくてもローカルのlaravelソースを確認すればよいのですから、手間も省けますね。
トータルで便利なので、是非試してみてください。