【Laravel】定期的にDBやファイルのバックアップ処理を行う

# はじめに
今回は、Laravelでバックアップ処理を定期的に実行する方法について書こうと思います。

バックアップを普通に実装しようとすると、ただソースファイルやDBをエクスポートするだけでなく、そのままだと容量が大きいので圧縮処理を行ったり、バックアップ中に更新処理された場合のデータ整合性問題を考えたりと、あらゆる面を考慮したロジックを組まないといけないので少し大変なイメージがあります。

しかし、Laravelにはそういった面も備わったバックアップ処理を実行できるライブラリがいくつか既に存在しています。今回はその中の１つ「spatie/laravel-backup」を使用して手軽にバックアップ処理を実装してみようと思います。

「[spatie/laravel-backup](https://github.com/spatie/laravel-backup
)」は、ベルギーに拠点を置くWeb制作会社「Spatie」が開発したLaravel専用のバックアップライブラリです。
調べた限り、Laravelのバックアップ用ライブラリとして使用されている例が多く、実装の手順も簡単そうだったので選びました。
現時点ではオープンソースで配布されているものなので問題なくインストールできます。

最終的に実装したバックアップ処理を、Laravelのタスクスケジューラ機能を用いて定期的に実行することで今回やりたいことを実現しようと思います。

# 環境
Laravel 10.28.0
spatie/laravel-backup 8.4

# 全体の流れ
1. 「spatie/laravel-backup」のインストール
2. バックアップの設定ファイルを編集
3. タスクスケジューラに登録
4. cronで定期実行できるように設定


# 1. 「spatie/laravel-backup」のインストール
以下を実行して「spatie/laravel-backup」をインストールします。
```
composer require spatie/laravel-backup
```
次に、以下を実行して、バックアップの設定ファイルを作成します。
```
php artisan vendor:publish --provider="Spatie\Backup\BackupServiceProvider"
```
「config/backup.php」にバックアップの編集ファイルが作成されます。

# 2. バックアップの設定ファイルを編集
バックアップの設定ファイル「config/backup.php」を編集します。
このファイルでは、バックアップデータの保存先だったり、バックアップデータの保持期間などを設定することができます。
デフォルトのままでも問題なく動作するのでそのままでも大丈夫です。
以下は自分が変更を加えた点です。

## バックアップデータの保存先
デフォルトだと、バックアップデータの名前が「APP_NAME(アプリケーション名)」、保存先ディスクが「local」に設定されているので、「storage/app/(アプリケーション名)」にバックアップデータが保存されるようになっています。

このままでも問題ないですが、「この中にバックアップデータが入っている」ということが分かりやすい場所に保存した方がいいと思いました。
そこで、色々方法があると思いますが、今回はバックアップ専用のディスクを新たに作成して、そこに保存するように変更しようと思います。

まずは、「config/filesystems.php」にバックアップ用のディスク「backup」を作成します。
```php:config/filesystems.php
    // （省略）
    
    'disks' => [

        'local' => [
            'driver' => 'local',
            'root' => storage_path('app'),
            'throw' => false,
        ],
    
    // 追加
    /* ----- ここから ----- */    
        'backup' => [
            'driver' => 'local',
            'root' => storage_path('app/backup'),
            'throw' => false,
        ],
    /* ----- ここまで ----- */

       'public' => [
            'driver' => 'local',
            'root' => storage_path('app/public'),
            'url' => env('APP_URL').'/storage',
            'visibility' => 'public',
            'throw' => false,
        ],    
        // (省略)    
    ],
    
    // (省略)
```

次に「config/backup.php」にディスクの変更を反映します。
変更すべき箇所は、バックアップデータの保存先「destination」と、監視先「monitor_backups」です。
「monitor_backups」は設定しなくても問題なくバックアップできますが、バックアップ監視用コマンド「php artisan backup:monitor」を実行する際に、監視先ディスクが異なり、想定外の動作になってしまうのでこちらも変更が必要です。

```php:config/backup.php
return [

    'backup' => [

        'name' => env('APP_NAME', 'laravel-backup'),
    
    // (省略)
    
        'destination' => [
            'disks' => [
                'backup',    // 変更
            ],    
        ],            

    // (省略)
    
    'monitor_backups' => [
        [
            'name' => env('APP_NAME', 'laravel-backup'),
            'disks' => ['backup'],    // 変更
            'health_checks' => [
                \Spatie\Backup\Tasks\Monitor\HealthChecks\MaximumAgeInDays::class => 1,
                \Spatie\Backup\Tasks\Monitor\HealthChecks\MaximumStorageInMegabytes::class => 5000,
            ],
        ],
    ],  
    // (省略)    
];
    
```
これで「storage/app/backup/(アプリケーション名)」にバックアップデータが保存されるようになりました。

## DBのダンプ設定
DBのダンプを実行する際にトランザクションを使用するかを指定できます。
今回はDBのダンプにmysqldumpを使用することを想定して書きます。
mysqldumpはダンプ実行時にデフォルトでテーブルがロックされるようになっています。DB容量が小さい場合は問題ないかもしれませんが、DB容量が大きい場合だとダンプ時間が長くなるため、ダンプデータの不整合やアプリケーションの一時利用停止といった問題が発生してしまいます。
こういったことを事前に防ぐために、トランザクション設定を使用することができます。
（ただし、トランザクション設定はmysqlのストレージエンジンにinnoDB(MySQL5.5~)を使っていることが条件になります。）
トランザクションを使用すると、テーブルをロックせずにダンプすることができるので、データの整合性も保ったままアプリケーションを停止させることなくダンプできるようになります。

トランザクションの設定は「config/database.php」の中で行います。
mysqlの中にdumpを新しく作り、'useSingleTransaction'をtrueにします。
```php:config/database.php
    // (省略）

    'connections' => [    
        'mysql' => [
            'driver' => 'mysql',
            'url' => env('DATABASE_URL'),
            'host' => env('DB_HOST', '127.0.0.1'),
            'port' => env('DB_PORT', '3306'),
            'database' => env('DB_DATABASE', 'forge'),
            'username' => env('DB_USERNAME', 'forge'),
            'password' => env('DB_PASSWORD', ''),
    
    // (省略）
    
            'dump' => [
                'useSingleTransaction' => true,    // 追加
            ],
    
        ],  
    
    // (省略）
    
    ],

    // (省略）
    
```

# 3. タスクスケジューラに登録
バックアップ処理を行うコマンドを「app/Console/Kernel.php」のschedule関数の中に登録します。

登録するコマンドは「バックアップを実行するコマンド」と「古いバックアップデータを削除するコマンド」です。
```shell
# バックアップ実行
php artisan backup:run  

# 古いバックアップデータを削除
php artisan backup:clean
```
（例）全体バックアップを１日1回、DBのバックアップを1時間毎に行う
```php:app/Console/Kernel.php
protected function schedule(Schedule $schedule)
{
        $schedule->command('backup:run')->daily();
        $schedule->command('backup:run --only-db')->hourly();
        $schedule->command('backup:clean')->dailyAt('05:00');    
}    
```
「--only-db」オプションは、名前の通りDBだけのバックアップデータを取得します。DB以外のソースファイルのバックアップを取得しないので注意です。
また、デフォルトだとバックアップ実行時に設定されたメールアドレスに通知するようになっています。それが不要の場合は「--disable-notifications」オプションを付けると通知が来なくなります。
他にも色々オプションがあります。

更に知りたい方は、[公式ドキュメント](https://spatie.be/docs/laravel-backup/v8/taking-backups/overview)を参考にしてみてください。

# 4. cronジョブを設定する
3で登録したタスクは、cronで毎分該当するタスクがあるかどうかを確認して、該当するタスクがあれば実行するようにします。

crontabを編集します。
```
crontab -e
```
```
* * * * * cd /.../laravel-project && php artisan schedule:run >> /dev/null 2>&1    
```
「php artisan schedule:run」で、スケジュールされたジョブがないかを確認し、該当するジョブがあればそれを実行します。
これにより、バックアップ処理の定期実行が完成しました。

# まとめ
「spatie/laravel-backup」を用いてLaravelのバックアップ処理を定期実行する方法について書いてみました。

ちなみに、バックアップは自動なので便利ですが、リストアは手動のようでした。
手動なので、バックアップデータを解凍してソースファイルを取り出して、、という感じです。
DBに関しては、sqlファイルを取り出してインポートします。
リストアの方法に関してももっと便利に行う方法があれば共有したいと思います。

---
参考サイト
[Laravel-backup](https://spatie.be/docs/laravel-backup/v8/introduction)
[Laravelでバックアップ処理を実装する](https://grune.co.jp/blog/laravel-backup/)
[mysqlのバックアップ(mysqldump)のロック問題](https://www.seeds-std.co.jp/blog/creators/2015-07-31-213123/)