前言
我在上一篇文章《通过对象存储搭建图床》里聊了一下搭建图床的方法和几个便利的工具,这只是对象存储的一个小用处,今天就来介绍一下对象存储另一个刚需场景——备份。图床是让博客的访问体验更好,而备份则是让博客始终有一条后路,保证博客不会因为一些突发情况彻底崩溃救不回来。
备份方案
在正式介绍对象存储备份之前,我们先来看几个常见的备份方案:
定期手动打包下载到本地电脑,这个不是那么容易坚持;
使用面板的定期备份功能,但备份包一直躺在服务器本地,一旦服务器出问题,这份备份也没法救我们的博客。
通过对象存储进行定期异地备份,服务器本地留最近 7 天,对象存储留最近 30 天,不用坚持手动,不怕服务器出问题,空闲了看一眼日志就完事儿了。
备份思路
动手备份之前先想清楚三个问题。
备什么:一个博客真正丢不起的就两样——网站文件和数据库。网站文件包括主题、插件配置、系统自带的设置;数据库里装的是全部文章内容、评论和用户数据。图片这类附件如果已经分流到图床,服务器上的文件体积其实很小。
备几份:备份圈有个经典的 3-2-1 原则——重要数据至少存三份、放在两种不同的介质上、其中一份要放在异地。对个人博客可以打个折:只用服务器本地放一份、对象存储里放一份,存两份、放在两种不同介质上、一份放在异地,3-2-1 原则就满足了大半;再讲究一点的话,可以每个月挑一份备份包下载到自己电脑,就齐了。
放哪里:服务器本地那份是"救急用的第一现场",恢复用是最快的;对象存储那份是"真正的保险",服务器和备份包必须不在同一个环境里,这才是异地容灾的意义。为什么值得把备份专门放到云上,Yuio 随笔有一篇文章《云备份开启的意义:保护数据,轻松应对突发》讲得很全,感兴趣可以看看。
我的做法是文件和数据库一起每天备一次,放在凌晨访问最低的时段。
先手动跑一遍备份流程
在自动化备份之前,先手动把流程走通一遍——每一步亲手跑过,出了问题才知道哪一环可能坏了。以下命令在 Ubuntu 22.04 和 Debian 12 上都直接可用。
第一步,建一个备份目录,之后所有备份包都放这里:
sudo mkdir -p /home/backup 第二步,打包网站文件。tar 是 Linux 自带的打包压缩工具,参数 czf 表示创建压缩包;-C / 表示先切到根目录再按相对路径打包,这样恢复时解回原位即可:
sudo tar -czf /home/backup/blog-files-$(date +%F).tar.gz -C / www/wwwroot/blog 命令里的 $(date +%F) 是让文件名自动带上今天的日期,比如 blog-files-2026-10-28.tar.gz,每天一个包互不覆盖,这个写法后面脚本里还要用。博客目录按你的实际路径改,我这里是 /www/wwwroot/blog 。
第三步,导出数据库。mysqldump 是 MySQL 自带的逻辑导出工具,一条命令把整个库导成一个 .sql 文件:
sudo mysqldump -u bloguser -p blogdb > /home/backup/blog-db-$(date +%F).sql 回车后会提示输入数据库密码。用户名和数据库名按实际的改。
如果是mysql容器,通过以下命令导出数据库:
sudo tar -czf /home/backup/blog-files-$(date +%F).tar.gz -C /opt/1panel/apps/halo/halo data
docker ps
docker exec mysql-x7f2 mysqldump -u root -p blogdb > /home/backup/blog-db-$(date +%F).sql第一行把博客的数据目录打包,路径以容器里看到的挂载点为准;第二行列出运行中的容器,找 MySQL 容器的名字(一般是 mysql- 加一串随机后缀,mysql-x7f2 是举例);第三行在容器里执行 mysqldump,导出到宿主机的备份目录,密码同样是回车后输入。
第四步,检查产物:
ls -lh /home/backup 能看到一包文件、一个 .sql 就算跑通了。另外提醒一句,备份包里是全站数据,谁拿到等于拿到整个博客,所以备份目录的权限最好收紧,只留自己能读。
顺带说一句,如果你用的是 Halo,它的后台自带全站备份导出,先用它把"有备份"这件事跑通也完全没问题,具体操作可以参考蜜汁博客的文章《装好Halo博客后,我们应该先做的七件事!》。
手动备份最大的敌人不是技术,是坚持。不过我是个懒人,坚持不了,这就需要自动化登场了。
定时自动化
cron 是 Linux 自带的定时任务工具,到点自动执行指定命令。可以把上一节的手动流程写进一个脚本,再交给 cron,备份就再也不用你操心了。
先解决一个绕不开的问题:脚本里没法手动输数据库密码。MySQL 的官方做法是把凭据写进 /root/.my.cnf,mysqldump 运行时会自动读取。新建这个文件:
sudo tee /root/.my.cnf > /dev/null <<'EOF'
[client]
user=bloguser
password="你的数据库密码"
EOF
sudo chmod 600 /root/.my.cnf chmod 600 让这个文件只有 root 能读——密码文件裸奔是大忌。写好后先验证一下免密导出是否生效:
sudo mysqldump blogdb > /tmp/test-dump.sql && head -3 /tmp/test-dump.sql && rm /tmp/test-dump.sql 能输出 SQL 开头几行就说明凭据生效了。接下来写脚本,新建 /home/backup/backup.sh:
#!/bin/bash
DATE=$(date +%F)
# 网站文件打包
tar -czf /home/backup/blog-files-$DATE.tar.gz -C / www/wwwroot/blog
# 数据库导出(凭据自动读取 /root/.my.cnf)
mysqldump blogdb > /home/backup/blog-db-$DATE.sql
# 本地只保留最近 7 天的备份find /home/backup -name "blog-*" -mtime +7 -delete
# 同步到对象存储(下一节配好 rclone 后生效)
rclone copy /home/backup s3:blog-backup --include "blog-*" 脚本里的 find 一行负责"滚动保留":-mtime +7 匹配 7 天前的文件,直接删掉,本地磁盘不会被备份撑爆。加一句说明:脚本默认由 root 的定时任务执行(见下文),所以内部没写 sudo;最后那行 rclone 现在还跑不通,配置好远程之后自动生效。
给脚本加执行权限,手动跑一次做验证:
sudo chmod +x /home/backup/backup.sh
sudo /home/backup/backup.sh 跑完 ls -lh /home/backup 确认今天的包都在。最后交给 cron,执行 sudo crontab -e(注意是 root 的定时任务,这也是脚本内部不用 sudo 的原因),加一行:
0 3 * * * /home/backup/backup.sh >> /home/backup/backup.log 2>&1 每天凌晨 3 点执行,输出追加到 backup.log,出错时打开日志就能定位。等第二天日志里没有报错、备份包按时出现,自动化就算真正落地。
用 1Panel 的朋友也可以走面板路线:计划任务里配一条备份任务同样能实现定时备份,具体可以参考我之前写过文章《如何在 1Panel 上备份数据教程》。
异地容灾:把备份推进对象存储
到上一步为止,备份已经全自动了,但它还只存在这台服务器上。快照虽然可以在机器出问题的时候,能够快速恢复,但备份文件和服务器往往还在同一套环境里,如果服务器的问题影响到备份文件,就没有挽回的余地了;
所谓的异地容灾,就是让备份的另一个副本待在跟服务器完全隔离的地方——对象存储就是这个绝佳的载体。
我们需要先配置一个 S3 协议的远程,这个协议是对象存储的通用接口协议,最早由亚马逊提出,现在各家对象存储都兼容,选它不代表要用亚马逊的服务,任何一家正规的对象存储都能接入,通过rclone 这条命令行同步工具,把备份包推进对象存储,终端里执行:
rclone config 按提示新建远程(比如就叫 s3),填接口地址(endpoint)、访问密钥 AK/SK,在对象存储的控制台里的密钥管理里照着填就行。
桶要单独建一个,比如 blog-backup。这里有个和图床完全相反的关键点:图床桶开公共读,备份桶必须私有。备份包里有完整的数据库,权限一开,等于把全站内容连用户数据一起裸奔。
配好后验证一下,能把文件列出来就通了:
rclone ls s3:blog-backup 之前在服务器写的定时任务脚本最后一行的 rclone copy 这时就自动生效了,参数里的 --include "blog-*" 表示只同步备份包,脚本和日志不用传上去。再补一个控制成本的动作:在对象存储控制台给备份桶设置生命周期规则,30 天自动过期,桶里的备份永远只保留最近一个月的。
如果你在用1Panel 的话,可以去“面板设置 → 备份账号”。新建账号,类型选 S3,把对象存储的接口地址(endpoint)、访问密钥 AK/SK、桶名填进去。
建好之后回到1Panel的计划任务,把任务的"备份账号"从"本地服务器"切换成刚建的 S3 账号。从此备份完成自动上传对象存储,保留份数到期自动清旧,本地一份、异地一份一次到位,快照也可以在创建时勾选同步到备份账号。
我现在图床和备份用的是同一家——VMRack 的对象存储,图床桶公共读、备份桶私有,分开建互不干扰。它现在公测期每月送 5GB 存储+10GB 下行流量,我这种体量的备份包放进去还有大把余量。如果你的站附件已经有好几个 GB,就先按"单日备份包体积 × 保留天数"算清楚总占用,再决定保留策略。
恢复演练与成本
备份文件本身不等于就一定能恢复。格式损坏、导出不完整、路径对不上,都只在真正恢复的那一刻暴露。所以恢复流程必须亲手演练一遍。
第一步,导入数据库(库不存在就先建):
mysql -u bloguser -p -e "CREATE DATABASE IF NOT EXISTS blogdb"
mysql -u bloguser -p blogdb < /home/backup/blog-db-2026-10-28.sql 如果是容器部署的话,换成这两行——-i 让容器接收外部管道输入,MYSQL_ROOT_PASSWORD 是 MySQL 容器创建时就存在的环境变量,引用它可以让密码不落明文:
docker exec -i mysql-x7f2 sh -c 'exec mysql -u root -p"$MYSQL_ROOT_PASSWORD" blogdb' < /home/backup/blog-db-2026-10-28.sql
sudo tar -xzf /home/backup/blog-files-2026-10-28.tar.gz -C /opt/1panel/apps/halo/halo 第二步,解包网站文件回原位:
sudo tar -xzf /home/backup/blog-files-2026-10-28.tar.gz -C / 第三步,打开博客确认文章、评论、配图都完整。
建议每季度挑一份备份包,在本地电脑或一台测试机上完整走一遍这三步——演练时发现的并解决的每一个问题,都是真出事时省下的慌乱,对于将网站迁移到本地部署测试环境,可以参考小龙虾博客的这篇文章《把WordPress博客搬到本地搭建测试环境》。
成本方面,以我现在的数据为例:
网站文件因为图片已经分流去图床,打包后只有几十 MB,数据库纯文本也就几 MB,单日两个包加起来不到 100MB;本地留 7 天不到 700MB,对象存储留 30 天约 3GB,公测期的免费额度装得下还有富余。下行流量只在恢复或下载备份时产生,平时几乎为零。也就是说,备份方案的成本大头依旧是存储,而存储恰恰是最便宜的那一项。
每周看一眼 backup.log 结尾有没有报错,再瞄一眼对象存储面板里今天的备份包在不在。
总结
根据你的实际情况跑通方案之后,日常要做的只剩两件事:
每周瞄一眼日志和面板
每季度演练一次恢复。
做好本地备份与对象存储异地容灾,服务器或者网站出问题了,随时可以复原,想搭个测试环境了,也可以随时搭建。所以做好备份工作对网站是很有必要的。