跳到正文
fomoxx.A PERSONAL CORNER OF THE INTERNET

腾讯云轻量应用服务器不支持自动快照:用本机脚本 + systemd timer 实现双槽位轮换

数字生活

腾讯云轻量应用服务器不支持自动快照:用本机脚本 + systemd timer 实现双槽位轮换

腾讯云轻量应用服务器没有自动快照功能,我的实例又只有 2 个快照槽位。如果只靠手动创建,恢复点很快就会被用完,或者干脆想不起来做。最后我用一个跑在服务器本机的脚本调用腾讯云 API,自动删除最旧的自动快照、再创建一个新快照,由 systemd timer 每周日凌晨 04:30 触发一次。

先把最终方案放在前面,只想抄作业的读者可以直接用这套配置。

快速解决

  • 两个快照槽位全部用于自动快照,不保留固定人工基线。
  • 自动快照统一使用 Auto-Snapshot- 前缀,脚本只删除这个前缀下的快照,其他名称的人工快照默认受保护。
  • 每周执行一次,时间为每周日 04:30,由 systemd timer 调度,不用 Cloudflare Workers,也不用传统 cron。
  • Timer 设置 Persistent=false:如果服务器在计划时间没有运行,本周直接跳过,恢复开机后不补跑。
  • 脚本运行在被备份的轻量云本机。这样服务器发生严重故障、脚本无法运行时,不会有外部调度器继续创建可能已经损坏的新快照并覆盖旧恢复点。

环境

实例lhins-xxxxxxxx(替换为自己的实例 ID)
地域na-siliconvalley(美国西部 / 硅谷)
脚本路径/root/tencent-snapshot.sh
自动快照前缀Auto-Snapshot-
保留数量2

脚本顶部的关键配置:

TENCENT_SECRET_ID=""
TENCENT_SECRET_KEY=""
TENCENT_REGION="na-siliconvalley"
INSTANCE_ID="lhins-xxxxxxxx"

SNAPSHOT_PREFIX="Auto-Snapshot-"
KEEP_SNAPSHOTS=2

SecretId 与 SecretKey 直接写在这台服务器的本地脚本里,只保留在服务器本地,不要同步到任何云端笔记或版本管理。脚本文件权限:

chmod 700 /root/tencent-snapshot.sh

CAM 权限

快照脚本使用独立的 CAM API 密钥,只授予快照相关权限:

{
  "version": "2.0",
  "statement": [
    {
      "effect": "allow",
      "action": [
        "lighthouse:DescribeAllSnapshotsOverview",
        "lighthouse:DescribeSnapshots",
        "lighthouse:DescribeSnapshotsDeniedActions",
        "lighthouse:CreateInstanceSnapshot",
        "lighthouse:DeleteSnapshots"
      ],
      "resource": [
        "*"
      ]
    }
  ]
}

测试时踩过一个坑:调用创建快照报 UnauthorizedOperation.NoPermission,检查后发现是策略里漏了 lighthouse:CreateInstanceSnapshot,补上之后创建成功。

快照轮换逻辑

脚本每次执行依次做这些事:

  1. 执行可选健康检查。
  2. 调用 DescribeSnapshots 查询当前实例快照。
  3. 如果存在非 NORMAL 状态的快照,停止,不进行删除。
  4. 如果已有 2 个快照,只从 Auto-Snapshot- 前缀的快照里找最老的一个。
  5. 调用 DeleteSnapshots 删除这个最老的自动快照。
  6. 等待旧快照确认删除完成。
  7. 再执行一次健康检查。
  8. 执行 sync,尽量把文件系统缓存刷入磁盘。
  9. 调用 CreateInstanceSnapshot 创建新快照。
  10. 等待新快照进入 NORMAL 状态后结束。

因为只删指定前缀的快照,其他名称的人工快照默认受到保护,不会被轮换掉。

两个槽位就这样持续滚动,实际看到的快照名类似:

Auto-Snapshot-20260906T203000Z
Auto-Snapshot-20260913T203000Z

下一周执行时删除较旧的一个,再生成新的恢复点。

健康检查

脚本支持 PRECHECK_COMMAND,留空则跳过:

PRECHECK_COMMAND=""

也可以换成一个简单的检查,比如确认文件系统可写:

PRECHECK_COMMAND='touch /tmp/.snapshot-check && rm -f /tmp/.snapshot-check'

设计原则:健康检查失败就停止轮换,避免在服务器明显异常的时候删除历史恢复点。创建快照前保留一个固定动作,把缓存刷进磁盘:

PRE_SNAPSHOT_COMMAND="sync"

systemd Service

文件:/etc/systemd/system/tencent-snapshot.service

[Unit]
Description=Tencent Lighthouse Automatic Snapshot
After=network-online.target
Wants=network-online.target

[Service]
Type=oneshot
ExecStart=/root/tencent-snapshot.sh
TimeoutStartSec=30min

这个 service 只负责把脚本执行一次,不常驻后台。

systemd Timer

文件:/etc/systemd/system/tencent-snapshot.timer

[Unit]
Description=Run Tencent Lighthouse Snapshot Weekly

[Timer]
OnCalendar=Sun *-*-* 04:30:00
AccuracySec=1min
Persistent=false

[Install]
WantedBy=timers.target

Persistent=false 是刻意设置的,不是忘了开。如果服务器在周日 04:30 处于关机或故障状态,恢复后不会立刻补做快照,而是保留已有恢复点,等下一周正常轮换。这一点和很多备份 timer 的默认习惯相反,原因在后面的取舍部分说明。

启用自动运行

systemctl daemon-reload
systemctl enable --now tencent-snapshot.timer

确认 timer 已启用:

systemctl is-enabled tencent-snapshot.timer

预期输出:

enabled

确认 timer 正在等待:

systemctl is-active tencent-snapshot.timer

预期输出:

active

查看下一次执行时间:

systemctl list-timers --all | grep tencent-snapshot

手工测试

先检查 Bash 语法:

bash -n /root/tencent-snapshot.sh

无输出即通过。

直接运行脚本:

/root/tencent-snapshot.sh

也可以模拟未来 timer 的调用方式:

systemctl start tencent-snapshot.service

查看执行状态:

systemctl status tencent-snapshot.service

注意 Type=oneshot 的 service 执行成功后会变成 inactive (dead),这是正常现象,重点看退出状态是不是 0/SUCCESS

日志与排障

看最近 100 行:

journalctl -u tencent-snapshot.service -n 100 --no-pager

看最近 7 天:

journalctl -u tencent-snapshot.service --since "7 days ago"

实时跟随:

journalctl -u tencent-snapshot.service -f

一次正常轮换的日志依次是:

开始处理实例
健康检查通过
查询当前快照
删除最老自动快照
等待删除完成
执行 sync
创建新快照
等待快照进入 NORMAL
快照轮换完成

为什么调度放在本机,而不是 Cloudflare Workers

最早的想法是把调度放到 Cloudflare Workers 上,好处是服务器自己出故障时,备份调度器不会跟着一起失效。这个方案最后放弃了,原因就出在那 2 个快照槽位上:

如果服务器已经发生数据损坏,但腾讯云控制面仍然能正常创建快照,外部调度器会按计划继续跑——每跑一次就删掉一个旧快照、写入一个基于损坏状态的新快照。两个槽位很快转完一轮,最后连一个干净的恢复点都不剩。

反过来,把调度放在本机是一种 fail-safe:服务器严重故障到本地脚本跑不起来时,自动快照自然停止,现存两个恢复点不会再被滚动覆盖。配合 Persistent=false,服务器停机期间同样不会补跑。

每周一次而不是每天,也是同一个限制下的选择。运行越频繁,两个恢复点之间的时间跨度越短。每周一次意味着通常可以保留本周和上周两个恢复点,更偏向灾难恢复,而不是细粒度的历史版本。

注意事项

  • 腾讯云快照是系统盘级恢复点,不应视作唯一的数据备份方案。
  • 如果数据库或应用数据非常重要,还应该增加独立的数据级备份,比如数据库 dump、配置文件归档或对象存储备份。
  • 修改 SNAPSHOT_PREFIX 之后,要确保现有两个希望被自动管理的快照也改成相同前缀,否则脚本会把它们当成人工快照并拒绝删除。
  • 不要把腾讯云 SecretKey 写进云端笔记、Git 仓库或日志。

常见问题

腾讯云轻量应用服务器支持自动快照吗?

不支持。轻量应用服务器只能手动创建快照,本文的方案是在服务器本机运行一个调用 Lighthouse API 的脚本,由 systemd timer 每周触发,自动删除最旧的自动快照并创建新快照,实现两个槽位的持续轮换。

只有 2 个快照槽位时,为什么不把调度放在 Cloudflare Workers 等外部调度器上?

如果服务器已经发生数据损坏但腾讯云控制面仍能创建快照,外部调度器会持续运行:每执行一次就删除一个旧快照、写入一个基于损坏状态的新快照,两个槽位转完一轮后,最后的好恢复点也会被覆盖。把调度放在本机是一种 fail-safe:服务器严重故障到脚本跑不起来时,自动快照自然停止,现存恢复点不会被继续滚动覆盖。

systemd timer 为什么设置 Persistent=false?

这是刻意设置的。如果服务器在周日 04:30 处于关机或故障状态,恢复后不会立刻补做快照,而是保留已有恢复点,等待下一周正常轮换。

为什么每周一次而不是每天?

因为只有两个快照槽位。运行越频繁,两个恢复点之间的时间跨度越短。每周一次意味着通常可以保留本周与上周两个恢复点,更偏向灾难恢复用途。

调用创建快照时报 UnauthorizedOperation.NoPermission 怎么办?

检查 CAM 策略是否包含 lighthouse:CreateInstanceSnapshot。本文测试时就因为漏了这个 action 报 NoPermission,补上后创建成功。

KEEP READING

继续阅读