- 运维
【免费下载链接】laravel-backup
A package to backup your Laravel app
本指南基于 laravel-backup 项目官方文档 high-level-overview 展开,系统讲解该包三大核心能力——备份(Taking backups)、清理旧备份(Cleaning up old backups)、监控备份健康(Monitoring the health of all backups),并结合 config/backup.php 与 src 下的源码实现,说明每条命令、每个配置项背后的真实调用逻辑。读完本文,你将掌握backup:run、backup:clean、backup:monitor、backup:list四条命令的完整用法,理解默认清理策略的六条规则,并能根据业务需求自定义健康检查与清理策略。
一、备份(Taking Backups):一条命令打包文件与数据库
备份的本质是产出一个.zip文件,其中包含两部分内容:
- 你在配置中指定的目录下的全部文件;
- 你所配置数据库的转储(dump)——包内置支持 MySQL、PostgreSQL、SQLite 和 Mongo 四种数据库(见 src/Tasks/Backup/DbDumperFactory.php 与 BackupJob.php 中的数据库转储逻辑)。
生成好的 zip 文件会自动复制到你在配置中指定的任意一个或多个文件系统(Filesystem)上,这些文件系统由 Laravel 自带的 filesystem 配置驱动。
1.1 触发备份的命令
执行一次全新备份,只需运行:
php artisan backup:run在大多数生产场景下,你都会希望把这个命令加入调度(Schedule),例如在routes/console.php中定时执行。
backup:run还提供了一组实用的运行时选项(可查阅 BackupCommand.php 中的命令签名定义):
| 选项 | 作用 | 示例 |
|---|---|---|
--only-to-disk | 只备份到指定磁盘,而非全部配置的磁盘 | php artisan backup:run --only-to-disk=s3 |
--only-db | 只备份数据库,跳过文件系统 | php artisan backup:run --only-db |
--db-name | 只备份指定名称的数据库连接(可多次传入) | php artisan backup:run --db-name=mysql |
--only-files | 只备份文件,跳过数据库转储 | php artisan backup:run --only-files |
--filename | 为备份 zip 指定自定义文件名 | php artisan backup:run --filename=my-backup.zip |
--filename-suffix | 在默认文件名后追加后缀 | php artisan backup:run --filename-suffix=-extra |
--exclude | 运行时额外排除目录或文件(可多次使用) | php artisan backup:run --exclude=storage/logs --exclude=storage/debugbar |
--destination-path | 覆盖备份的目标存储路径 | php artisan backup:run --destination-path=custom/path |
--disable-notifications | 本次运行不发送任何通知 | php artisan backup:run --disable-notifications |
--timeout | 设置备份进程的 PHP 超时时间(秒) | php artisan backup:run --timeout=600 |
--tries | 配置失败后的重试次数 | php artisan backup:run --tries=3 |
--config | 使用指定的自定义配置文件运行备份 | php artisan backup:run --config=backup_database |
从源码看,这些选项会直接作用于BackupJob对象:例如--only-db对应调用dontBackupFilesystem(),--only-files对应dontBackupDatabases(),--only-to-disk对应onlyBackupTo(),--exclude会调用fileSelection()->excludeFilesFrom()(见 BackupCommand.php)。--timeout则通过set_time_limit()调整 PHP 执行时间上限。
⚠️ 警告(原文档重点强调):请谨慎使用
--only-db与--only-files。在监控备份健康时,包不会区分"完整备份"与"只含文件或只含数据库的部分备份"。如果只做了部分备份,一旦出现问题,你可能无法从备份中完整恢复。同时源码也会阻止二者同时使用:guardAgainstInvalidOptions()会抛出InvalidCommand::create('Cannot use only-db and only-files together')(BackupCommand.php)。
1.2 备份内容配置:决定打包哪些文件与数据库
备份内容由config/backup.php中backup.source段决定,该段核心配置如下(完整文件见 config/backup.php):
'backup' => [ // 应用名称,监控备份时用此名称标识本应用 'name' => env('APP_NAME', 'laravel-backup'), 'source' => [ 'files' => [ // 需要纳入备份的目录与文件列表 'include' => [ base_path(), ], // 需要从备份中排除的目录与文件 // 备份过程自身使用的目录会被自动排除 'exclude' => [ base_path('vendor'), base_path('node_modules'), storage_path('framework'), ], // 是否跟随符号链接 'follow_links' => false, // 是否跳过不可读的目录 'ignore_unreadable_directories' => false, // 用于让 zip 内的目录变为相对路径;设为 null 则保留完整绝对路径 'relative_path' => null, ], // 需要备份的数据库连接名称(支持 MySQL、PostgreSQL、SQLite、Mongo) 'databases' => [ env('DB_CONNECTION', 'mysql'), ], ], ],几个关键点:
include默认包含整个项目根目录base_path()。配置文件中还留有一行注释:// storage_path(),提示如果你使用零停机部署(zero downtime deployment)且不跟随符号链接,可能需要额外包含storage_path()。exclude中列出的目录在打包时会被剔除;备份过程自身使用的目录(如备份目标磁盘目录、临时目录)也会被自动排除,具体逻辑见 BackupJob.php 的directoriesUsedByBackupJob()。- 数据库转储可针对每个连接自定义:你可以在
config/database.php中为某个连接添加dump键,例如:
'mysql' => [ // ... 'dump' => [ 'exclude_tables' => [ 'table_to_exclude_from_backup', 'another_table_to_exclude', ], ], ],如果 MySQL 服务器上全部使用 InnoDB 表,还可以设置'useSingleTransaction' => true来避免锁表。config/backup.php还提供database_dump_compressor(默认null,可设为Spatie\DbDumper\Compressors\GzipCompressor::class)、database_dump_file_timestamp_format、database_dump_filename_base(database或connection)、database_dump_file_extension等转储细节选项(config/backup.php)。
1.3 目标磁盘配置:决定备份存放在哪里
backup.destination段指定 zip 备份要复制到哪些文件系统:
'destination' => [ // 存放备份的磁盘名称列表 'disks' => [ 'local', ], ],config('backup.destination.disks')的默认值只包含local一个磁盘。请注意原文档的忠告:如果你只用本地磁盘存放备份,一旦该磁盘崩溃,你将一无所有——"拥有备份"不等于"拥有备份策略"!
因此强烈建议:在config/filesystems.php中额外配置一些磁盘(如 S3、Dropbox 等外部服务),并把它们加入destination.disks。如果你需要给磁盘底层的 Flysystem 驱动传递额外选项,可以在该磁盘配置中加一个backup_options数组:
// config/filesystems.php 'disks' => [ 's3' => [ 'driver' => 's3', // ... 'backup_options' => [ // 在这里添加额外选项 ], ], ],一个重要的容错设计(源码事实):当向某个文件系统复制 zip 失败时,包仍然会继续尝试向其他所有已配置的文件系统复制(见 BackupJob.php 的copyToBackupDestinations())。只有所有目标都失败时,整个备份才会被判定为失败;destination.continue_on_failure配置项(默认false)控制着这种"部分失败继续执行"的行为。
1.4 备份的文件名与执行流程
默认情况下,zip 文件名格式为<配置中的应用名称>/<Y-m-d-H-i-s>.zip,例如my-app/2026-10-09-03-07-18.zip。文件名的时间戳格式常量定义在 BackupJob.php(FILENAME_FORMAT = 'Y-m-d-H-i-s.zip'),备份名(即"应用名"目录)则通过--destination-path或配置的 name 决定。此外config/backup.php还提供destination.filename_prefix(zip 文件名前缀)、compression_method(默认ZipArchive::CM_DEFAULT)与compression_level(默认 9)等压缩相关配置(config/backup.php)。
从 BackupJob::run() 可以还原完整执行链路:
- 在临时目录(
storage/app/backup-temp,见 config/backup.php)中准备一个名为temp的临时目录; createBackupManifest():先转储所有配置的数据库(触发DumpingDatabase事件),再收集需要备份的文件,写入manifest.txt(触发BackupManifestWasCreated事件);createZipContainingEveryFileInManifest():把清单中的全部文件与数据库转储打成 zip(触发BackupZipWasCreated事件);若配置了verify_backup,还会用ZipArchive重新打开 zip 校验文件数(见verifyBackup(),BackupJob.php);copyToBackupDestinations():把 zip 复制到所有目标磁盘,成功后触发BackupWasSuccessful事件;- 复制完成后删除临时目录与源 zip。
需要备份的文件越多,zip 越大。请确保磁盘上有足够的空闲空间来生成 zip 文件;源 zip 在被复制到所有目标后会被删除。
1.5 用独立配置跑多个备份域
如果希望按不同范围分别备份 Laravel 应用的各个区域——例如使用不同的调度、数据库连接、文件系统磁盘或清理设置——可以创建自定义的备份配置文件,放在config/目录下:
config/backup_database.phpconfig/backup_invoices.phpconfig/backup_uploads.php
然后分别运行备份与清理命令:
php artisan backup:run --config=backup_database php artisan backup:clean --config=backup_database php artisan backup:run --config=backup_invoices php artisan backup:clean --config=backup_invoices php artisan backup:run --config=backup_uploads php artisan backup:clean --config=backup_uploads从源码看,--config选项会让命令直接从指定配置键重新构建Config对象:Config::fromArray(config($this->option('config')))(BackupCommand.php)。这为调度、保留策略和目标磁盘提供了完全独立的灵活性。
二、清理旧备份(Cleaning Up Old Backups):磁盘空间守门员
随着时间的推移,备份数量与占用的存储空间会不断增长,你终将需要清理旧备份。运行:
php artisan backup:clean如需使用特定配置执行清理:
php artisan backup:clean --config=backup先记住一个关键默认行为:无论备份多大、多老,包默认永远不会删除最新的一份备份。
2.1 清理策略配置
config/backup.php中cleanup段决定了哪些备份应当被删除(完整配置见 config/backup.php):
'cleanup' => [ // 用于清理旧备份的策略类。 // 默认策略会保留一定天数内的全部备份; // 该期限过后仅保留每日备份;再往后仅保留每周备份,以此类推。 // 无论怎么配置,默认策略永远不会删除最新的备份。 'strategy' => \Spatie\Backup\Tasks\Cleanup\Strategies\DefaultStrategy::class, 'default_strategy' => [ // 必须保留全部备份的天数 'keep_all_backups_for_days' => 7, // 必须保留每日备份的天数 'keep_daily_backups_for_days' => 16, // 必须保留每周备份的周数 'keep_weekly_backups_for_weeks' => 8, // 必须保留每月备份的月数 'keep_monthly_backups_for_months' => 4, // 必须保留每年备份的年数 'keep_yearly_backups_for_years' => 2, // 清理后若存储仍超出该值(MB),继续删除最旧备份直到达标 // 设为 null 表示不限大小 'delete_oldest_backups_when_using_more_megabytes_than' => 5000, ], ],配置文件中的注释对这些参数做了更细的补充说明:在keep_all_backups_for_days期满后,每天只保留当天最近的一份备份,同一天内更早的备份会被删除;keep_weekly_backups_for_weeks、keep_monthly_backups_for_months、keep_yearly_backups_for_years同理——如果你每天只创建一次备份,则对应阶段不会再有备份被移除(config/backup.php)。
2.2 默认策略(DefaultStrategy)的六条规则
包提供了一套"有主见"(opinionated)的旧备份判定方法,即DefaultStrategy。它在 DefaultStrategy.php 中实现,工作规则如下:
- 规则 #1:无论大小与年龄,永不删除最新备份(源码中对应
$backups->shift()先把最新备份拿出来保护起来,见 DefaultStrategy.php); - 规则 #2:保留
keepAllBackupsForDays指定天数内的所有备份; - 规则 #3:对早于规则 #2 覆盖范围的所有备份,仅在
keepDailyBackupsForDays指定的天数内保留每日备份; - 规则 #4:对早于规则 #3 覆盖范围的所有备份,仅在
keepMonthlyBackupsForMonths指定的月数内保留每周备份; - 规则 #5:仅保留
keepYearlyBackupsForYears指定年数内的每年备份(针对早于规则 #4 覆盖范围的所有备份); - 规则 #6:如果存储总用量超出
deleteOldestBackupsWhenUsingMoreMegabytesThan指定的容量,开始删除最旧备份,直到用量低于该限额(对应 DefaultStrategy.php 中的removeOldBackupsUntilUsingLessThanMaximumStorage())。
策略在实现上把备份按Ymd(日)、YW(周)、Ym(月)、Y(年)四种时间粒度分组,每个分组只保留一份,其余删除(DefaultStrategy.php)。当然,默认配置中的这些数字完全可以按需调整。
2.3 自定义清理策略
如果默认策略无法满足你的需求,可以创建自己的策略:继承抽象类Spatie\Backup\Tasks\Cleanup\CleanupStrategy,只需要实现这一个方法:
use Spatie\Backup\BackupDestination\BackupCollection; public function deleteOldBackups(BackupCollection $backupCollection)BackupCollection继承自Illuminate\Support\Collection,内部包含按年龄排序的Spatie\Backup\BackupDestination\Backup对象,最新备份排在最前面(见 BackupCollection.php 中newest()、oldest()的实现)。利用集合可以方便地手动删除最旧备份:
// 获取一个 `Spatie\Backup\BackupDestination\Backup` 实例 $backup = $backups->oldestBackup(); // 删除该备份 $backup->delete();不要忘记在 laravel-backup 配置文件的cleanup.strategy键中填写你自定义策略的完整类名。
三、监控所有备份的健康状况(Monitoring the Health of All Backups)
包还可以为每个安装了它的应用检查备份健康状况。一个备份在以下两种情况下被判定为"不健康(unhealthy)":
- 最近一次备份的日期过于久远,已不足以支撑恢复;
- 所有备份所需的存储空间不达标/超出限额。
除了监控应用自身的备份,其他应用的备份也可以被一并监控——这正是把它部署为独立监控服务的价值所在。
3.1 推荐的部署方式:独立监控安装
建议单独建立一个 Laravel 安装来做监控,最好放在独立服务器上。这样即使被监控的某个应用宕机,你依然能收到不健康备份的通知。
同时建议:使用监控功能时,备份应存放在中心化的存储磁盘(如 s3)。你仍然可以监控本地磁盘,但那样就必须把监控放进运行备份的那个应用里。
监控安装遵循常规的安装与设置流程,区别在于:不调度backup:run和backup:clean,而是调度监控命令:
// routes/console.php use Illuminate\Support\Facades\Schedule; Schedule::command('backup:monitor')->daily()->at('03:00');当然,如果你愿意,仍然可以调度backup:run与backup:clean来备份监控应用自身。
3.2 指定监控哪些备份
config/backup.php中monitor_backups段用于指定要监控的应用,以及监控在什么条件下判定某应用的备份不健康:
'monitor_backups' => [ [ 'name' => env('APP_NAME'), 'disks' => ['s3'], 'health_checks' => [ \Spatie\Backup\Tasks\Monitor\HealthChecks\MaximumAgeInDays::class => 1, \Spatie\Backup\Tasks\Monitor\HealthChecks\MaximumStorageInMegabytes::class => 5000, ], ], /* [ 'name' => 'name of the second app', 'disks' => ['s3'], 'health_checks' => [ \Spatie\Backup\Tasks\Monitor\HealthChecks\MaximumAgeInDays::class => 1, \Spatie\Backup\Tasks\Monitor\HealthChecks\MaximumStorageInMegabytes::class => 5000, ], ], */ ],两个内置健康检查的行为(均有源码佐证):
MaximumAgeInDays:如果最新备份比指定天数更旧,检查即失败;若没有备份也会失败。源码实现在 MaximumAgeInDays.php:无备份时判定不健康,否则判断$backup->date()->gt(now()->subDays($this->days))是否为真。不需要该检查时,直接把它从数组中移除即可。MaximumStorageInMegabytes:如果所有备份的总大小超过指定 MB 数,检查即失败。不需要时同样直接移除。
关键约定:监控条目中的name必须与被备份应用的backup.name配置键保持一致(该值默认来自env('APP_NAME'),见 config/backup.php)。从源码看,backup:monitor通过BackupDestinationStatusFactory::createForMonitorConfig()为每个受监控的"应用名 + 磁盘"组合生成状态对象,逐个检查健康与否,不健康时输出错误并触发UnhealthyBackupWasFound事件(见 MonitorCommand.php)。
3.3 查看所有备份的状态
要查看所有受监控目标文件系统的状态,使用:
php artisan backup:list该命令基于同一份monitor_backups配置生成一个概览表格,列包括:Name(备份名)、Disk(磁盘)、Reachable(可达)、Healthy(健康)、# of backups(备份数)、Newest backup(最新备份)、Used storage(已用存储),并在表格下方单独列出所有不健康目标及其失败检查项(见 ListCommand.php)。执行backup:monitor时,命令行会打印诸如The my-app backups on the s3 disk are considered healthy.或...considered unhealthy!的判定结果。
3.4 获得(不)健康备份的通知
当监控器发现健康或不健康的备份时,你可以收到通知。包内置了HealthyBackupWasFoundNotification与UnhealthyBackupWasFoundNotification,并支持 mail、Slack、Discord、通用 webhook 等通知渠道的配置(见 config/backup.php 与 src/Notifications 目录)。详见 发送通知 一节。
四、围绕三大能力的事件与扩展点
最后从架构角度总结:三大能力(备份、清理、监控)并非孤立模块,它们通过事件机制相互配合,均定义在 src/Events 下,主要包括:
- 备份生命周期:
DumpingDatabase、BackupManifestWasCreated、BackupZipWasCreated、BackupWasSuccessful、BackupHasFailed; - 清理结果:
CleanupWasSuccessful、CleanupHasFailed; - 监控结果:
HealthyBackupWasFound、UnhealthyBackupWasFound。
监听器与通知层通过 src/Notifications/EventHandler.php 将这些事件桥接到用户配置的通知渠道,同时包的日志也会写入config/backup.php中log_channel指定的日志渠道(默认使用 Laravel 默认渠道,设false可完全关闭备份日志)。了解这些事件,你就可以在自己的应用里监听它们,做告警、归档、二次校验等自定义扩展。
- 运维
【免费下载链接】laravel-backup
A package to backup your Laravel app
相关推荐
php-msf数据库连接池详解:Mysql与Redis高性能操作指南
php msf数据库连接池详解:Mysql与Redis高性能操作指南 php msf(PHP微服务框架)的连接池功能是其高性能架构的核心组成部分,能够显著提升数
YOLOv10 源码解析:Ultralytics HUB 工具函数库 ultralytics/hub/utils.py 全解
YOLOv10 源码解析:Ultralytics HUB 工具函数库 ultralytics/hub/utils.py 全解 导读 本篇文章以 docs/en/
运维claude-skills 实战:Django 应用接入 django-storages 与 AWS S3 的生产级文件存储配置指南
claude skills 实战:Django 应用接入 django storages 与 AWS S3 的生产级文件存储配置指南 本指南基于当前仓库中的 d
运维
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考