← 回目录
29 Week 06 · 脚本与收尾

备份脚本 + 日志切割

今天有个比命令更重要的观念:什么时候不该自己造轮子。日志切割就是典型——系统自带的比你写的强。

tar:打包压缩

先把工具认清
四个参数
# 打包并压缩
$ tar czf backup.tar.gz /etc/nginx

# 解包
$ tar xzf backup.tar.gz

# 只看内容不解开 ← 恢复前一定先看
$ tar tzf backup.tar.gz | head

# 解到指定目录
$ tar xzf backup.tar.gz -C /tmp/restore/
字母的含义
c / x / t
create 创建 / extract 解开 / list 列表。三选一。
z
用 gzip 压缩。文件名带 .gz 就要加它。j 是 bz2,J 是 xz,压得更小但更慢)
f
指定文件名,后面紧跟文件名。f 必须放在最后一个字母位置。
v
显示处理过程。脚本里别加——会刷屏、拖慢速度。
记法

打包 czf,解包 xzf,看看 tzf三个记住就够用。

备份脚本

/opt/backup.sh
#!/bin/bash
#=========================================
# backup.sh —— 备份配置,保留 7 天
#=========================================
set -u

SRC_LIST="/etc/nginx /etc/ssh"     # 要备份什么
DST="/backup"                      # 备份放哪
KEEP_DAYS=7                        # 保留几天
DATE=$(date +%F)
LOG="/var/log/backup.log"

mkdir -p "$DST"                    # Day 02:-p 已存在也不报错

for src in $SRC_LIST; do
    name=$(basename "$src")        # /etc/nginx → nginx
    file="$DST/${name}-${DATE}.tar.gz"

    if tar czf "$file" "$src" 2>/dev/null; then
        size=$(du -h "$file" | cut -f1)
        echo "$(date '+%F %T') 备份成功 $file ($size)" >> "$LOG"
    else
        echo "$(date '+%F %T') [失败] 备份 $src 出错" >> "$LOG"
    fi
done

#—— 清理过期备份(Day 05 的 -mtime +N)——
deleted=$(find "$DST" -name "*.tar.gz" -mtime +$KEEP_DAYS -print -delete | wc -l)
echo "$(date '+%F %T') 清理了 ${deleted} 个过期备份" >> "$LOG"
几个设计考虑
文件名带日期
nginx-2026-09-07.tar.gz——一眼知道是哪天的,也不会互相覆盖。
用 if 判断 tar 是否成功
备份最怕"以为备了其实没备"。判断返回值并记日志,才知道到底成没成。
-print -delete 组合
find 删除时同时打印删了哪些,日志里能追溯。
保留天数做成变量
磁盘紧张时改一个数字就行。

备份最重要的一件事

比备份本身还重要
没验证过的备份,等于没有备份

很多事故是这样的:出事了,去拿备份,发现备份是空的、或者根本解不开——脚本报错了但没人看日志。

所以要定期做恢复演练。这句话在面试里很有分量。

恢复演练怎么做
# 1. 先看包里有什么(别直接解到原位置!)
$ tar tzf /backup/nginx-2026-09-07.tar.gz | head

# 2. 解到临时目录验证
$ mkdir -p /tmp/restore
$ tar xzf /backup/nginx-2026-09-07.tar.gz -C /tmp/restore/
$ ls -l /tmp/restore/etc/nginx/

# 3. 和当前配置对比,确认内容是对的
$ diff /tmp/restore/etc/nginx/nginx.conf /etc/nginx/nginx.conf

注意 tar 默认是相对路径解压——包里存的是 etc/nginx/...(去掉了开头的 /),所以解到 /tmp/restore 会变成 /tmp/restore/etc/nginx/这个特性避免了误覆盖系统文件。

日志切割:别自己写

今天的核心观念

日志会一直涨。你可能想写个脚本定期删——但系统自带的 logrotate 比你写的强得多,而且它已经在你机器上跑着了。

看看它已经在管什么
$ cat /etc/logrotate.conf         # 全局默认
$ ls /etc/logrotate.d/            # 每个服务一个配置
nginx  sssd  chrony  dnf

$ cat /etc/logrotate.d/nginx
给自己的日志加一份配置
$ sudo vi /etc/logrotate.d/mycheck

/var/log/check.log {
    daily                # 每天切一次
    rotate 7             # 保留 7 份
    compress             # 旧的压缩成 .gz
    delaycompress        # 延迟一轮再压(避免正在写的被压)
    missingok            # 文件不存在也不报错
    notifempty           # 空文件不切
    create 644 root root # 切完新建一个,指定权限属主
}
几个关键指令
daily / weekly / size 100M
时间大小触发切割。生产上常用 size——按大小更可控。
rotate 7
保留 7 份历史,再老的自动删
create 644 root root
切割后立刻创建新的空文件并设好权限不加这个,服务可能因为找不到文件而写不进日志。
copytruncate
另一种模式:先复制再清空原文件。适合那些不支持"重新打开日志文件"的程序——Day 21 那个「rm 了但空间没释放」的问题,用它可以避免。
测试配置(别直接等它自己跑)
$ sudo logrotate -d /etc/logrotate.d/mycheck
     ↑ -d = debug,只演练不执行,看它打算做什么

$ sudo logrotate -f /etc/logrotate.d/mycheck
     ↑ -f = force,强制立刻执行一次,用于验证
-d 先演练,这是好习惯

nginx -tmount -a 一个道理——这三十天里反复出现的模式:动手前先验证。

今天的面试点

「日志文件太大怎么处理?」

别答"写脚本定期删"——那说明你不知道有现成方案。

「用系统自带的 logrotate:可以按大小或时间切割、自动压缩、保留 N 份自动删。配置放 /etc/logrotate.d/,改完用 logrotate -d 先演练一遍。」

加一句更好:「如果程序不支持重新打开日志文件,就用 copytruncate 模式,避免出现「文件删了但空间没释放」的情况。

「你们的备份策略是什么?」

「配置文件每天 tar 打包,文件名带日期,保留 7 天,用 find -mtime +7 -delete 自动清理,挂在 crontab 凌晨跑。」

然后一定加这句:「另外我会定期做恢复演练——把备份解到临时目录验证能不能正常打开。没验证过的备份等于没有备份。

这句话的分量比前面所有命令加起来都重。

今天的练习

敲完再走 · 约 18 分钟
  • 练 tar 三件套:tar czf /tmp/t.tar.gz /etc/sshtar tzf 看内容 → tar xzf ... -C /tmp/r/ 解开
  • 手敲 /opt/backup.shchmod +x 后跑一次,看 /backup//var/log/backup.log
  • 验证备份可用:解到 /tmp/restore,用 diff 和当前配置对比
  • 测试清理逻辑sudo touch -d "10 days ago" /backup/old-test.tar.gz 造一个 10 天前的文件,重跑脚本看它被删掉
  • /etc/logrotate.d/mycheck,用 logrotate -d 演练
  • sudo logrotate -f 强制执行一次,看 /var/log/ 下多出 check.log.1
  • 把备份脚本挂到 crontab:0 2 * * * /opt/backup.sh
明天是最后一天

Day 30 不敲命令——把这三十天串成四段能说出口的话,练怎么在面试里讲。

学会了但说不出来,等于没学。