我在使用的是腾讯云轻量应用服务器,免费快照额度只有2个,每一次都是手动删除旧快照再创建新快照实在太麻烦,问了客服也没有直接设置的功能,但好在提供了API接口来实现。
所以当前使用了1Panel面板的计划任务和腾讯云API来实现:自动识别并删除旧的自动快照,立即创建一个新的快照。
一、获取API 密钥
1.创建子账号,点击新建用户 → 选择自定义创建,用户类型:可访问资源并接收消息,填写用户名(如 lighthouse-snapshot),访问方式勾选编程访问,完成创建后,务必下载或复制好 SecretId 和 SecretKey。
2.进入访问管理 → 策略,点击 新建自定义策略 → 按策略语法创建 → 空白模板。
名称随意(如 Lighthouse-Snapshot),粘贴以下 JSON:
{
"statement": [
{
"action": [
"lighthouse:CreateInstanceSnapshot",
"lighthouse:DeleteSnapshots",
"lighthouse:DescribeSnapshots",
"lighthouse:DescribeSnapshotsDeniedActions"
],
"effect": "allow",
"resource": [
"*"
]
}
],
"version": "2.0"
}3.自动跳回自定义策略列表,找到刚创建的子账号,关联刚才建立的策略,API密钥即使泄漏,攻击者也只能操作快照。
二、服务器环境准备
安装 Python 虚拟环境及腾讯云 SDK,本教程以 Debian 12 为例,其他系统请自行调整包管理器命令,如果系统提示没有 pip,可以先安装 python3-pip。
# 1. 安装 python3 虚拟环境依赖
apt update
apt install python3.11-venv -y
# 2. 创建专属虚拟环境
mkdir -p /opt/scripts
python3 -m venv /opt/scripts/venv
# 3. 激活虚拟环境并安装腾讯云 SDK
source /opt/scripts/venv/bin/activate
pip install tencentcloud-sdk-python-lighthouse
deactivate三、编写自动化脚本
1.创建脚本文件:
vim /opt/scripts/snapshot_backup.py2.将以下完整代码粘贴进去,并按注释修改开头4个配置变量:
#!/opt/scripts/venv/bin/python3
# -*- coding: utf-8 -*-
import time
import logging
from tencentcloud.common import credential
from tencentcloud.common.exception.tencent_cloud_sdk_exception import TencentCloudSDKException
from tencentcloud.lighthouse.v20200324 import lighthouse_client, models
# ========== 请修改以下4个配置 ==========
SECRET_ID = "AKIDxxxxxxxxxxxxxxxx" # 子用户的 SecretId
SECRET_KEY = "xxxxxxxxxxxxxxxxxxxx" # 子用户的 SecretKey
REGION = "ap-xxxxx" # 实例所在地域,如 ap-shanghai
INSTANCE_ID = "lhins-xxxxxxxx" # 轻量应用服务器实例 ID
SNAPSHOT_NAME_PREFIX = "auto-snap" # 自动快照的名称前缀,可自定义
MAX_RETRY = 10 # 删除后等待确认的最大次数
RETRY_INTERVAL = 5 # 每次等待秒数
# ======================================
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
try:
cred = credential.Credential(SECRET_ID, SECRET_KEY)
client = lighthouse_client.LighthouseClient(cred, REGION)
except Exception as e:
logger.error(f"初始化客户端失败: {e}")
exit(1)
def get_all_normal_snapshots():
"""获取所有状态为 NORMAL 的快照,并按创建时间升序排列"""
try:
req = models.DescribeSnapshotsRequest()
resp = client.DescribeSnapshots(req)
snapshots = [s for s in resp.SnapshotSet if s.SnapshotState == 'NORMAL']
snapshots.sort(key=lambda x: x.CreatedTime if x.CreatedTime else '')
return snapshots
except TencentCloudSDKException as e:
logger.error(f"查询快照失败: {e}")
return []
def get_old_snapshot_id():
"""
决定删除哪个快照:
1. 优先删除名称以 SNAPSHOT_NAME_PREFIX 开头的自动快照
2. 若没有,则删除最早的一个普通快照(用于首次清理手动快照)
"""
snapshots = get_all_normal_snapshots()
if not snapshots:
return None
for snap in snapshots:
if snap.SnapshotName and snap.SnapshotName.startswith(SNAPSHOT_NAME_PREFIX):
return snap.SnapshotId
# 未找到自动前缀快照,返回最早的一个
logger.warning("未找到自动前缀快照,将删除最早的一个普通快照以腾出配额")
return snapshots[0].SnapshotId
def delete_snapshot(snapshot_id):
"""删除指定快照"""
try:
req = models.DeleteSnapshotsRequest()
req.SnapshotIds = [snapshot_id]
resp = client.DeleteSnapshots(req)
logger.info(f"已发起删除快照 {snapshot_id}")
return True
except TencentCloudSDKException as e:
logger.error(f"删除快照失败: {e}")
return False
def wait_snapshot_deleted(snapshot_id):
"""轮询等待快照彻底消失"""
for i in range(MAX_RETRY):
logger.info(f"等待快照 {snapshot_id} 被删除... ({i+1}/{MAX_RETRY})")
found = False
try:
req = models.DescribeSnapshotsRequest()
req.SnapshotIds = [snapshot_id]
resp = client.DescribeSnapshots(req)
if resp.SnapshotSet:
logger.info(f"快照仍存在,状态: {resp.SnapshotSet[0].SnapshotState}")
found = True
except TencentCloudSDKException as e:
if 'ResourceNotFound' in str(e):
logger.info(f"快照已删除")
return True
else:
logger.warning(f"查询状态异常: {e}")
if not found:
return True
time.sleep(RETRY_INTERVAL)
logger.error("删除超时,未能确认")
return False
def create_snapshot():
"""创建新快照,名称包含时间戳"""
try:
timestr = time.strftime("%Y%m%d-%H%M%S")
snapshot_name = f"{SNAPSHOT_NAME_PREFIX}-{timestr}"
req = models.CreateInstanceSnapshotRequest()
req.InstanceId = INSTANCE_ID
req.SnapshotName = snapshot_name
resp = client.CreateInstanceSnapshot(req)
logger.info(f"成功创建快照 {resp.SnapshotId},名称: {snapshot_name}")
return resp.SnapshotId
except TencentCloudSDKException as e:
logger.error(f"创建快照失败: {e}")
return None
if __name__ == "__main__":
logger.info("===== 开始自动快照轮换 =====")
old_id = get_old_snapshot_id()
if not old_id:
logger.warning("无可删除的快照,请检查配额和快照状态")
exit(1)
logger.info(f"待删除快照: {old_id}")
if not delete_snapshot(old_id) or not wait_snapshot_deleted(old_id):
logger.error("删除未完成,中止操作")
exit(1)
new_id = create_snapshot()
if new_id:
logger.info("===== 快照轮换成功 =====")
else:
logger.error("创建新快照失败")
exit(1)3.赋予执行权限并测试:
chmod +x /opt/scripts/snapshot_backup.py
/opt/scripts/venv/bin/python3 /opt/scripts/snapshot_backup.py出现的情况有,检测不到auto-snap前缀的快照,自动删除最早的手动快照,随后创建一个名如auto-snap-20260704-120000 的新快照
四、1Panel 配置定时任务
1.登录 1Panel 面板,进入计划任务,点击创建计划任务,填写脚本内容等:
/opt/scripts/venv/bin/python3 /opt/scripts/snapshot_backup.py >> /var/log/snapshot_backup.log 2>&12.保存后,可先点击右侧的 “执行” 手动测试一次,并查看日志确认无误。
常见问题
Q:我的系统是 CentOS,命令一样吗?
A:基本相同,只需将 apt 换成 yum 或 dnf,Python 包名可能略有差异(如 python3-venv 需要安装 python3-virtualenv)。
Q:我不想删除最早的手动快照怎么办?
A:可以先将手动快照重命名为带有 auto-snap 前缀,或者删除一个手动快照释放配额后再运行脚本。
没有下一篇了





