FieryWing 的技术博客

服务器备份与基础监控

2026-02-28

服务器迟早会出问题,备份和监控能让你出事时有路可退。磁盘损坏、误删文件、升级失败、被入侵,这些都不是“会不会”,而是“什么时候”。这篇文章是我给自己的个人服务器整理的备份与监控方案,系统是 Debian / Ubuntu,工具都很朴素:tar、rsync、cron,再加一点监控。

备份什么

判断标准很简单:如果这台机器明天消失了,我需要哪些东西才能在新机器上恢复服务?列出来的就是要备份的。另外也别忘了记录“怎么装”的步骤,比如这台机器装了哪些软件,这份文档本身也要放在服务器之外。

3-2-1 原则

一个常被提到的思路是 3-2-1:至少保留 3 份数据副本,使用 2 种不同的存储介质,其中 1 份放在异地。对个人服务器不必机械照搬,但核心精神值得记住:不要把所有副本放在同一个地方,也不要只有一份。原数据加上一份本地备份、一份异机或对象存储的备份,已经比大多数人做得好了。

简单可靠的做法

tar czf blog-$(date +%F).tar.gz ~/blog
rsync -av ~/blog/ backup-host:/backup/blog/

用 cron 定时执行,并且保证备份放在另一台机器或对象存储上;备份和原数据放在同一块盘上不算备份。

用 tar 打包

tar 是最基础的归档工具。几个常用写法:

tar czf site-$(date +%F).tar.gz -C /var/www blog
tar tzf site-2026-02-28.tar.gz | head
tar xzf site-2026-02-28.tar.gz -C /tmp/restore-test

第一条打包并压缩,-C 指定先切换到某目录,避免把绝对路径打进包里;第二条只列出内容而不解压,用来快速检查;第三条解压到指定目录。恢复时不要直接解压覆盖线上目录,先解压到临时目录确认无误再替换。

用 rsync 同步

rsync 只传输变化的部分,适合定期同步到另一台机器。使用前确保能用 SSH 密钥免密登录备份机,配置方法见 SSH 那篇文章。

rsync -a --delete /var/www/blog/ backup@203.0.113.20:/backup/blog/

注意两点。路径末尾的斜杠有意义:源路径带斜杠表示同步目录里的内容,不带则把目录本身也复制过去。--delete 会删除目标端多出来的文件,让两边保持一致,但这也意味着源端误删文件后,下一次同步会把备份端的文件也删掉。所以只有同步而没有历史版本,不能算完整的备份;要么保留带日期的归档,要么使用支持快照的工具。第一次使用前可以加 -n 做空跑,预览将发生什么。

数据库要单独导出

数据库正在运行时,直接复制数据文件可能得到不一致的备份。应使用数据库自带的导出工具,例如 PostgreSQL 的 pg_dump 或 MySQL / MariaDB 的 mysqldump:

pg_dump -U appuser appdb | gzip > appdb-$(date +%F).sql.gz
mysqldump --single-transaction -u appuser -p appdb | gzip > appdb-$(date +%F).sql.gz

密码不要直接写在命令行里,那会出现在进程列表和 shell 历史中;可以使用配置文件(并把权限设为 600)或者让程序交互式询问。数据库在容器里时,用 docker compose exec 在容器中执行导出命令即可。

写一个备份脚本并用 cron 定时执行

把步骤放进脚本,便于测试和复用。示例脚本 /usr/local/bin/backup-blog.sh:

#!/bin/sh
set -eu

SRC=/var/www/blog
DEST=/var/backups/blog
STAMP=$(date +%F)

mkdir -p "$DEST"
tar czf "$DEST/blog-$STAMP.tar.gz" -C /var/www blog
cp /etc/caddy/Caddyfile "$DEST/Caddyfile-$STAMP"

# 保留最近 14 天的本地备份
find "$DEST" -type f -mtime +14 -delete

# 同步到异机
rsync -a "$DEST/" backup@203.0.113.20:/backup/blog/

赋予执行权限并手动运行一次,确认成功后再加入定时任务:

sudo chmod 750 /usr/local/bin/backup-blog.sh
sudo /usr/local/bin/backup-blog.sh
sudo crontab -e

在 crontab 里添加一行,每天凌晨 3 点 30 分执行并记录日志:

30 3 * * * /usr/local/bin/backup-blog.sh >> /var/log/backup-blog.log 2>&1

cron 的环境变量很少,脚本里尽量使用绝对路径。find ... -delete 这类命令威力很大,写之前先去掉 -delete 看看会匹配到哪些文件。

需要版本和加密时:restic、borg

如果希望有历史版本、去重和加密,可以了解 restic、borg 这类专门的备份工具,它们都支持增量快照,可以备份到本地目录、远程服务器或兼容的对象存储。它们各有特点,具体功能、支持的后端以及用法请以各自官方文档为准。无论用哪种工具,核心都不变:自动化、放异地、能还原、有监控。加密备份要额外注意保管好密钥或口令,丢了就无法恢复,而且它不应该只存放在被备份的那台服务器上。

备份要验证

定期试着还原一次,确认备份真的可用。我的做法是每隔一段时间在另一台机器或临时目录里走一遍恢复流程:

  1. 取最新的备份文件,检查文件大小是否正常,明显偏小多半有问题。
  2. 解压到临时目录:tar xzf 备份文件 -C /tmp/restore-test。
  3. 检查关键文件是否都在,数据库导出能否导入到一个测试库。
  4. 记录恢复所需的时间和遇到的问题,把步骤写成文档。

没有验证过的备份,只能算“可能有的备份”。

基础监控

补充几个排查时好用的命令:

df -h
du -sh /var/log/* | sort -h | tail
journalctl -p err -b --no-pager | tail -n 50
systemctl --failed

磁盘写满是个人服务器最常见的事故之一,原因往往是日志或 Docker 镜像堆积。监控备份本身也很重要:脚本失败时要有人知道。简单做法是脚本成功结束时才 touch 一个标记文件,另一个定时检查发现标记文件太旧就发通知;也可以让备份脚本在失败时发邮件或推送。证书到期的问题,如果用了 Caddy,大多会自动续期,但仍建议从外部检查网站证书是否有效,见《Caddy 入门》。

常见坑

小结