跳转至

监控与备份

一、这页讲什么

服务「平时能用」和「出事能扛」是两件事。这页把后者补上:每天自动巡检一次(磁盘、内存、失败的服务、监听端口),发现问题有人收到通知;关键数据每天备份到另一块盘,并且真的恢复过一次。

读完能做成:一台服务器上跑起每日巡检与每日备份,收到第一条异常通知,还完成一次恢复演练。

二、开始之前

项 内容
更新日期 2026-10-21
适用版本 Ubuntu 24.04、systemd 255、restic 0.18
预计耗时 2 小时(含一次恢复演练)
风险级别 中。涉及常驻定时任务与备份目录,先在测试机走一遍
前置 一台 Linux 服务器;一块与系统盘独立的备份盘或远端存储;有 sudo 权限

先想清楚三件事:哪些数据丢了算事故(数据库、配置、上传文件)、备份放哪(不能与源数据同盘)、出问题时通知发给谁。

三、动手做

3.1 先手动跑一次巡检

不写脚本,先把要看的东西手工看一遍,知道正常长什么样。

$ df -h /
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda1        98G   42G   51G  46% /
$ free -h
               total        used        free      shared  buff/cache   available
Mem:            31Gi       7.2Gi       1.1Gi       312Mi        23Gi        23Gi
$ systemctl --failed
  UNIT                  LOAD   ACTIVE SUB    DESCRIPTION
  nginx.service         loaded failed failed A high performance web server
$ ss -tlnp | grep -E ':(80|443|22)'
LISTEN 0  511  0.0.0.0:80  0.0.0.0:*  users:(("nginx",pid=812,fd=6))

四项都能打印出结果就算成。systemctl --failed 有输出说明已经有服务挂了,先处理它再继续。

3.2 写成脚本,用 systemd timer 每天跑

cron 能跑,但 systemd timer 自带日志、失败重试与依赖,排障时更好查。

$ sudo tee /usr/local/bin/daily-check.sh >/dev/null <<'EOF'
#!/usr/bin/env bash
set -uo pipefail
echo "== $(date -Is) =="
df -h / | tail -1
free -h | sed -n '2p'
systemctl --failed --no-legend
ss -tlnp | grep -c LISTEN
EOF
$ sudo chmod +x /usr/local/bin/daily-check.sh
$ sudo /usr/local/bin/daily-check.sh
== 2026-10-21T10:40:03+08:00 ==
/dev/sda1        98G   42G   51G  46% /
Mem:            31Gi       7.2Gi       1.1Gi       312Mi        23Gi        23Gi

脚本手工跑通、输出四段就算成。

接着写服务与定时器,让它每天 09:00 跑并记进 journal。

# /etc/systemd/system/daily-check.service
[Unit]
Description=Daily host check
[Service]
Type=oneshot
ExecStart=/usr/local/bin/daily-check.sh
# /etc/systemd/system/daily-check.timer
[Unit]
Description=Run daily check at 09:00
[Timer]
OnCalendar=*-*-* 09:00:00
Persistent=true
[Install]
WantedBy=timers.target
$ sudo systemctl daemon-reload && sudo systemctl enable --now daily-check.timer
Created symlink /etc/systemd/system/timers.target.wants/daily-check.timer → /etc/systemd/system/daily-check.timer.
$ systemctl list-timers daily-check.timer
NEXT                        LEFT    UNIT               ACTIVATES
Wed 2026-10-22 09:00:00 CST 22h     daily-check.timer  daily-check.service
$ journalctl -u daily-check.service -n 5
Oct 21 09:00:02 server-a systemd[1]: Starting Daily host check...
Oct 21 09:00:03 server-a daily-check.sh[9213]: == 2026-10-21T09:00:02+08:00 ==

list-timers 显示下次触发时间、journalctl 有本次输出,两项都满足就算成。

异常要能主动通知:在脚本末尾加一条判断,磁盘使用率超过 80% 或存在失败服务时推一条消息出去。判断放在脚本里比放在告警系统里简单,也更容易审。

3.3 配置备份

备份工具选 restic 或 rsync 都行,关键不在工具,在于备份不在同一块盘、有保留策略、能校验。下面按 restic 写。

$ sudo apt install -y restic
$ export RESTIC_REPOSITORY=/mnt/backup/restic
$ export RESTIC_PASSWORD_FILE=/root/.restic-pass
$ restic init
created restic repository 3f9a1c7b2e at /mnt/backup/restic
$ restic backup /srv/data --tag daily
repository 3f9a1c7b opened, using repository format 2
Files:         412 new,     0 changed,     0 unmodified
Added to the repository: 128.4 MiB (96.2 MiB stored)
snapshot 8c1d2e saved

看到 snapshot ... saved 就算成。

加保留策略,否则备份盘会被撑满。每周保留最近 7 天、每月保留 6 个月,并让 restic 校验一次数据完整性。

$ restic forget --keep-daily 7 --keep-monthly 6 --prune
Applying Policy: keep 7 daily, 6 monthly snapshots
keep 7 snapshots:
...
$ restic check
using temporary cache in /tmp/restic-check-cache-1234
create exclusive lock for repository
load indexes
check all packs
no errors were found

no errors were found 才算备份可用。把 backup 与前一条 forget --prune 一起放进同一个 timer,顺序执行。

3.4 演练一次恢复

没恢复过的备份等于没有备份。挑一份快照,恢复到临时目录,比对文件数与内容。

$ restic restore latest --target /tmp/restore-test
restoring <Snapshot 8c1d2e of /srv/data at 2026-10-21 09:12:44> to /tmp/restore-test
$ diff -r /srv/data /tmp/restore-test/srv/data && echo SAME
SAME
$ rm -rf /tmp/restore-test

diff 无差异、打印 SAME 就算成。恢复演练每季度做一次,做完记下日期与耗时。

四、做完怎么确认

检查项 期望结果
systemctl list-timers 巡检与备份两个 timer 都有下次触发时间
journalctl -u daily-check.service 有最近一次巡检输出
restic snapshots 有当天快照
restic check no errors were found
恢复演练 恢复到临时目录后 diff 无差异
手动拔掉通知测试 手机能在 1 分钟内收到

五、卡住了怎么排查

症状 多半是 怎么办
timer 到点没跑 Persistent 没开、机器当时关机 开 Persistent=true,list-timers 看 NEXT 时间
备份任务报磁盘满 保留策略没生效或没 prune 先 restic forget --prune,再查备份盘容量
恢复后文件权限不对 非 root 恢复、或备份时用了不同用户 用同样的用户恢复,或恢复后重设属主
restic 密码没了 密码写在临时环境变量里没落盘 密码单独存好并纳入密钥管理;丢了打不开仓库
巡检脚本报 Permission denied sudo 执行的脚本读了用户目录 检查脚本跑的权限与路径
通知没送到 出口配置错了,或起了多个机器人 手动 curl 测试一次,只留一个出口
时间对不上 时区或 NTP 没配 timedatectl 看时区,开自动同步

六、为什么这样能成

巡检解决「坏之前有人知道」,备份解决「坏了能恢复」,演练解决「恢复这一步本身是否可信」。三件事分开做,任何一件缺失都会在事故里暴露。

备份的三条硬规则:至少两份副本、放在两种存储上、其中一份离开这台机器(常见说法是 3-2-1)。快照不是备份——同一个盘上的快照跟着盘一起坏。保留策略决定空间,校验决定数据是否完整,恢复演练决定备份是否真的可用。

告警要能叫醒人:只写日志没人看,只在仪表盘画曲线也没人看。异常汇总成一条消息发到人手上,才算闭环。

七、边界与替代

  • 只要单机排查、不要求恢复:用 journalctl 与 df 手工看即可,不必上定时任务。
  • 数据可以丢:缓存、构建产物这类目录不必备份,写清楚哪些不备份比全备更重要。
  • 没有独立的第二块存储:先解决存储,再做备份方案;同盘备份不算备份。
  • 不要追求全自动修复:自动重启能掩盖根因,先做到「知道 + 能恢复」,自动化放在后面。
  • 备份文件里不要存明文密码:仓库密码与加密密钥单独管理。

八、官方资料