起因

网站文件是典型的“改着改着就没了”的数据:发个新版本、上传个附件、改个配置,谁也说不准哪次操作就把东西弄丢;再碰上磁盘坏道或误删,没有备份就只能重来。手工 tar 一次两次坚持得下来,长期可靠的做法只有一条:交给定时任务,让机器每天自己做。

需求

定时备份网站/home/www/htdocs/blog/home/www/backup/site-blog下,
并删除10天前的备份。

技术实现原理

整体思路:全量打包 + 时间戳文件名 + 按天数清理

一条因果链串起整个脚本:备份要能独立恢复,所以每次做完整打包——一个 tar.gz 就是一个完整快照,任何一个都能单独拿来还原;备份不能无限占盘,所以文件名带时间戳、再按保留天数清掉过期文件;要无人值守,所以整套逻辑交给 crontab 每天定点触发。

为什么要用 -X 排除目录

网站的 runtime、logs 这类目录是运行时产物:每次访问都在变、体积大、而且随时可以重新生成。把它们打进备份有三个坏处——体积暴涨、打包变慢、tar 读到正在变化的文件还会报 “file changed as we read”。用 -X 把它们从打包清单里剔掉,备份里剩下的就是“丢了真找不回来”的部分。

find -mtime 的清理逻辑

三个过滤条件各管一道闸:-name "$WEBSITE_PREFIX*" 按前缀匹配,只删本脚本自己产生的备份,目录里放的其他文件绝不误伤;-mtime +10 按“文件修改时间超过 10 天”筛选;-exec rm {} \; 把筛选结果逐个删掉。注意 -mtime 是按 24 小时取整的:+10 匹配到的是第 11 天及更早的文件,保留期要卡得准就得记住这个边界。

为什么逻辑写在脚本里而不是直接塞进 crontab

crontab 的命令行里 % 是特殊字符——会被解释成换行、后面的内容被当成标准输入,date +%Y%m%d 这类表达式直接写进 crontab 十有八九出错。把逻辑装进脚本、crontab 只留一行调用,既绕开这个坑,也方便单独测试:手动跑一遍脚本就能验证备份结果,不用等第二天凌晨。

创建脚本

1
vi /home/www/shell/site_backup_blog.sh

脚本内容

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#!/bin/bash
############### common file ################
#备份文件存放目录
WEBBACK_DIR="/home/www/backup/site-blog"
#格式化日期,备份文件时用日期来做文件名的
DATE=`date +%Y%m%d-%H%M%S`
#保存日期
DAYS=10
############ www info ######################
#WEB目录
WEBSITE_DIR="/home/www/htdocs/blog"

# 排除某些目录(日志和缓存),exclude.txt文件中写入
#/home/www/htdocs/blog/runtime
#/home/www/htdocs/blog/logs
#指定www备份文件的前缀
WEBSITE_PREFIX=web-blog-

# 判断备份目录是否存在,不存在则创建
if [ ! -d "$WEBBACK_DIR" ];then
mkdir -p $WEBBACK_DIR
fi

#开始备份网站目录,备份过程同上
tar zcvf ${WEBBACK_DIR}/${WEBSITE_PREFIX}${DATE}.tar.gz ${WEBSITE_DIR} -X /home/www/shell/exclude.txt

#只保留指定时间内的文件
find ${WEBBACK_DIR} -name "$WEBSITE_PREFIX*" -type f -mtime +${DAYS} -exec rm {} \;

脚本说明

  • 变量全部集中在文件头:目录、保留天数、前缀想改只动一处;
  • DATE=\date +%Y%m%d-%H%M%S``:文件名带精确到秒的时间戳,一天跑多次也不重名,恢复时一眼看出是哪刻的快照;
  • tar zcvf ... -X exclude.txt:c 建包、z 过 gzip 压缩、v 打印打包过程、f 指定输出文件;-X 跟一个排除清单文件,一行写一个要剔除的路径;
  • 开头的 mkdir -p:备份目录可能还没建,-p 会连父目录一起创建,且目录已存在时不报错;
  • 清理那行 find 的三个条件见上文原理。

设置执行权限

1
chmod 700 /home/www/shell/site_backup_blog.sh

设置定时任务

crontab -e

文件内容:

1
3 4 * * * /home/www/shell/site_backup_blog.sh

五个字段从左到右是 分 时 日 月 周,3 4 * * * 即每天 04:03 执行。

每天凌晨4:03分开始执行备份任务。挑 4:03 而不是整点 4:00 是个运维小惯例:整点是定时任务扎堆的时刻(日志轮转、别的备份都在整点跑),错开几分钟能少抢一次磁盘和 CPU。

踩坑记录与注意事项

  • % 在 crontab 里是特殊字符:直接把 date +%Y%m%d 写进 crontab 会被截断(% 后内容被当作标准输入),日期逻辑放脚本里(本文做法)或把 % 转义成 %;
  • cron 的环境是精简的:PATH 往往只有 /usr/bin:/bin,脚本用到非标准路径的工具时(如 /usr/local/bin 下的程序)写绝对路径最稳;
  • -mtime +10 的边界:匹配的是第 11 天及更早的文件,保留期要卡准就得按这个取整规则算;
  • 备份目录和网站同盘:本文 /home/www/backup 与站点在同一块盘,盘一坏两边一起没。真正的容灾要再把备份推到另一台机器/另一块盘(本站另有一篇 rsync+VPS 的方案干这件事);
  • tar 打到正在写的文件会告警:“file changed as we read” 就是备份瞬间文件还在变,用 -X 排除运行时目录既是减体积也是避开它;
  • 加上日志重定向好排查:crontab 里可写成 ... >> /home/www/backup/site-blog.log 2>&1,出问题翻日志,不用干等邮件;
  • 恢复时注意路径:tar 打包给的是绝对路径,默认会去掉开头的 /(提示 “Removing leading /' ...”),解出来是相对路径结构,恢复要 cd /` 再解压才能还原到原位。