🔄 卡若AI 同步 2026-02-20 07:15 | 更新:金仓、运营中枢工作台 | 排除 >20MB: 5 个
This commit is contained in:
@@ -308,6 +308,7 @@ ss -tlnp | grep :端口号
|
||||
| `快速检查服务器.py` | 一键检查所有服务器状态 | `./scripts/` |
|
||||
| `一键部署.py` | 根据配置文件部署项目 | `./scripts/` |
|
||||
| `ssl证书检查.py` | 检查/修复SSL证书 | `./scripts/` |
|
||||
| `腾讯云镜像快照备份到CKB_NAS/tencent_image_snapshot_backup_to_nas.py` | 腾讯云镜像/快照元数据备份到 CKB NAS,1000G 限制与超限邮件告警 | `./scripts/腾讯云镜像快照备份到CKB_NAS/` |
|
||||
|
||||
---
|
||||
|
||||
|
||||
1
01_卡资(金)/金仓_存储备份/服务器管理/scripts/腾讯云镜像快照备份到CKB_NAS/.gitignore
vendored
Normal file
1
01_卡资(金)/金仓_存储备份/服务器管理/scripts/腾讯云镜像快照备份到CKB_NAS/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
config.env
|
||||
91
01_卡资(金)/金仓_存储备份/服务器管理/scripts/腾讯云镜像快照备份到CKB_NAS/README.md
Normal file
91
01_卡资(金)/金仓_存储备份/服务器管理/scripts/腾讯云镜像快照备份到CKB_NAS/README.md
Normal file
@@ -0,0 +1,91 @@
|
||||
# 腾讯云镜像/快照备份到 CKB NAS
|
||||
|
||||
将腾讯云 CVM 自定义镜像、云硬盘快照的元数据与(可选)镜像文件备份到 **CKB NAS**(192.168.1.201),并限制备份目录总容量为 **1000GB**,超过时发邮件告警。
|
||||
|
||||
## 功能
|
||||
|
||||
- **镜像**:拉取账号下全部地域的**自定义镜像**列表,写入 NAS 的 `meta/images_*.json`、`meta/images_latest.json`
|
||||
- **快照**:拉取全部地域的**云硬盘快照**列表,写入 NAS 的 `meta/snapshots_*.json`、`meta/snapshots_latest.json`
|
||||
- **容量限制**:备份根目录总大小超过 **1000GB**(可配置)时:
|
||||
- 发送告警邮件到配置的邮箱
|
||||
- 不再拉取新的镜像文件(仅更新元数据)
|
||||
- **镜像文件落盘**:腾讯云不提供镜像/快照“直接下载”。需在控制台将**自定义镜像导出到 COS**,再通过 COS 工具(如 coscmd)或本脚本扩展逻辑,从 COS 下载到 NAS。
|
||||
|
||||
## 前置条件
|
||||
|
||||
1. **CKB NAS 已挂载到本机**
|
||||
- CKB NAS 内网 IP:`192.168.1.201`(见群晖 NAS 管理 SKILL)
|
||||
- 在 Mac:访达 → 前往 → 连接服务器 → `smb://192.168.1.201/共享名`,挂载后得到如 `/Volumes/ckb_backup`
|
||||
- 本脚本使用的目录为挂载点下的子目录,如 `/Volumes/ckb_backup/tencent_cloud_backup`
|
||||
|
||||
2. **腾讯云凭证**
|
||||
- 环境变量 `TENCENTCLOUD_SECRET_ID`、`TENCENTCLOUD_SECRET_KEY`,或
|
||||
- 在 `运营中枢/工作台/00_账号与API索引.md` 的「腾讯云」段落填写 SecretId/SecretKey
|
||||
|
||||
3. **告警邮件(可选)**
|
||||
- 使用 QQ 邮箱时需在 QQ 邮箱 → 设置 → 账户 → 开启 SMTP 并生成**授权码**,在 `config.env` 中填 `SMTP_PASSWORD=授权码`
|
||||
|
||||
## 配置
|
||||
|
||||
1. 复制配置示例并编辑:
|
||||
```bash
|
||||
cd "01_卡资(金)/金仓_存储备份/服务器管理/scripts/腾讯云镜像快照备份到CKB_NAS"
|
||||
cp config.example.env config.env
|
||||
```
|
||||
2. 编辑 `config.env`:
|
||||
- **NAS_BACKUP_ROOT**:NAS 上备份根目录(必须已挂载),如 `/Volumes/ckb_backup/tencent_cloud_backup`
|
||||
- **SIZE_LIMIT_GB**:容量上限(GB),默认 `1000`
|
||||
- **ALERT_EMAIL_TO**:超限告警收件人
|
||||
- **SMTP_*****:发信用(QQ 邮箱用授权码)
|
||||
|
||||
## 运行
|
||||
|
||||
```bash
|
||||
# 建议使用项目根下已有 venv(含 tencentcloud-sdk)
|
||||
cd /Users/karuo/Documents/个人/卡若AI
|
||||
. .venv_tencent/bin/activate # 或 服务器管理/scripts 下的 venv
|
||||
pip install tencentcloud-sdk-python-common tencentcloud-sdk-python-cvm tencentcloud-sdk-python-cbs
|
||||
|
||||
python3 "01_卡资(金)/金仓_存储备份/服务器管理/scripts/腾讯云镜像快照备份到CKB_NAS/tencent_image_snapshot_backup_to_nas.py"
|
||||
```
|
||||
|
||||
## 定时拉取(有新镜像/快照即同步元数据)
|
||||
|
||||
- 建议用 cron 或 launchd 定期执行(如每天 2:00),这样**新产生的镜像/快照**会在下次执行时被拉取并写入 NAS 的 `meta/`。
|
||||
- 示例 crontab(每天 2:00):
|
||||
```bash
|
||||
0 2 * * * /Users/karuo/Documents/个人/卡若AI/.venv_tencent/bin/python3 /Users/karuo/Documents/个人/卡若AI/01_卡资(金)/金仓_存储备份/服务器管理/scripts/腾讯云镜像快照备份到CKB_NAS/tencent_image_snapshot_backup_to_nas.py >> /tmp/tencent_backup_nas.log 2>&1
|
||||
```
|
||||
- 若备份目录超过 1000GB,脚本会发邮件提醒,且不会从 COS 拉取新镜像文件(仅更新元数据)。
|
||||
|
||||
## 镜像文件如何落到 NAS(需先导出到 COS)
|
||||
|
||||
1. 腾讯云控制台 → 云服务器 → 镜像 → 自定义镜像 → 选择镜像 → **导出镜像** → 选择同地域 COS 桶与前缀。
|
||||
2. 导出完成后,在 COS 桶中会得到镜像文件(如 `.qcow2` / `.vhd`)。
|
||||
3. 将 COS 中的文件下载到 NAS:
|
||||
- 方式 A:安装 [coscmd](https://cloud.tencent.com/document/product/436/10976),配置后执行 `coscmd download -r bucket://prefix /path/on/nas`
|
||||
- 方式 B:在本脚本中扩展「从 COS 列举并下载」逻辑(需配置 `COS_BUCKET`、`COS_REGION`、`COS_PREFIX`)。
|
||||
|
||||
## 快照说明
|
||||
|
||||
- 腾讯云**云硬盘快照**不提供“下载到本地”的接口,仅支持云内恢复、从快照创建云盘/镜像。
|
||||
- 本脚本将快照**元数据**(ID、名称、大小、创建时间等)同步到 NAS,便于审计与清单管理。
|
||||
- 若需将某快照“备份成文件”,需:用该快照创建自定义镜像 → 再按上文将镜像导出到 COS → 从 COS 下载到 NAS。
|
||||
|
||||
## 文件结构(NAS 备份根目录下)
|
||||
|
||||
```
|
||||
tencent_cloud_backup/
|
||||
├── meta/
|
||||
│ ├── images_20260219_020000.json
|
||||
│ ├── images_latest.json
|
||||
│ ├── snapshots_20260219_020000.json
|
||||
│ └── snapshots_latest.json
|
||||
└── (可选)images/ # 若从 COS 下载镜像文件,可放于此
|
||||
```
|
||||
|
||||
## 相关
|
||||
|
||||
- 服务器管理 SKILL:`01_卡资(金)/金仓_存储备份/服务器管理/SKILL.md`
|
||||
- 群晖/CKB NAS:`01_卡资(金)/金仓_存储备份/群晖NAS管理/SKILL.md`
|
||||
- 账号与 API 索引:`运营中枢/工作台/00_账号与API索引.md`
|
||||
@@ -0,0 +1,26 @@
|
||||
# 腾讯云镜像/快照备份到 CKB NAS - 配置示例
|
||||
# 复制为 config.env 并填写真实值,config.env 勿提交 Git
|
||||
|
||||
# ---------- NAS 备份目录(必须先挂载 CKB NAS 的 SMB 共享)
|
||||
# 示例:Mac 挂载后 /Volumes/ckb_backup ;Linux /mnt/ckb/tencent_cloud_backup
|
||||
NAS_BACKUP_ROOT=/Volumes/ckb_backup/tencent_cloud_backup
|
||||
|
||||
# 容量上限(GB),超过则发邮件告警,且本次不再拉取新镜像文件
|
||||
SIZE_LIMIT_GB=1000
|
||||
|
||||
# ---------- 告警邮件(超过 SIZE_LIMIT_GB 时发送)
|
||||
ALERT_EMAIL_TO=zhiqun@qq.com
|
||||
# QQ 邮箱发信需用授权码,非登录密码。见:QQ邮箱 -> 设置 -> 账户 -> POP3/IMAP -> 生成授权码
|
||||
SMTP_HOST=smtp.qq.com
|
||||
SMTP_PORT=465
|
||||
SMTP_USER=zhiqun@qq.com
|
||||
SMTP_PASSWORD=
|
||||
|
||||
# ---------- 腾讯云(可选,不填则从 00_账号与API索引.md 读取)
|
||||
# TENCENTCLOUD_SECRET_ID=
|
||||
# TENCENTCLOUD_SECRET_KEY=
|
||||
|
||||
# ---------- COS 同步(可选。若在控制台已将镜像导出到 COS,填此项可从 COS 拉取到 NAS)
|
||||
# COS_BUCKET=your-bucket-1251077262
|
||||
# COS_REGION=ap-guangzhou
|
||||
# COS_PREFIX=export_images
|
||||
@@ -0,0 +1,14 @@
|
||||
# 腾讯云镜像/快照备份到 CKB NAS - 定时执行示例
|
||||
# 每天 2:00 执行,有新镜像/快照会自动拉取元数据到 NAS;超过 1000G 会发邮件告警
|
||||
|
||||
# 1) 先挂载 CKB NAS(Mac 可开机自动挂载或手动)
|
||||
# 访达 → 连接服务器 → smb://192.168.1.201/共享名
|
||||
# 挂载后例如:/Volumes/ckb_backup
|
||||
|
||||
# 2) 在脚本目录配置 config.env(NAS_BACKUP_ROOT、告警邮箱、SMTP)
|
||||
|
||||
# 3) crontab -e 添加(路径按本机实际修改):
|
||||
0 2 * * * NAS_BACKUP_ROOT=/Volumes/ckb_backup/tencent_cloud_backup /Users/karuo/Documents/个人/卡若AI/.venv_tencent/bin/python3 /Users/karuo/Documents/个人/卡若AI/01_卡资(金)/金仓_存储备份/服务器管理/scripts/腾讯云镜像快照备份到CKB_NAS/tencent_image_snapshot_backup_to_nas.py >> /tmp/tencent_backup_nas.log 2>&1
|
||||
|
||||
# 或使用 config.env 中的 NAS_BACKUP_ROOT(不设环境变量):
|
||||
# 0 2 * * * cd /Users/karuo/Documents/个人/卡若AI/01_卡资(金)/金仓_存储备份/服务器管理/scripts/腾讯云镜像快照备份到CKB_NAS && /Users/karuo/Documents/个人/卡若AI/.venv_tencent/bin/python3 tencent_image_snapshot_backup_to_nas.py >> /tmp/tencent_backup_nas.log 2>&1
|
||||
@@ -0,0 +1,310 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
腾讯云 CVM 镜像与云硬盘快照 → 备份到 CKB NAS
|
||||
|
||||
- 拉取账号下全部自定义镜像、快照的元数据并写入 NAS
|
||||
- 可选:从 COS 同步已导出的镜像文件到 NAS(需先在控制台将镜像导出到 COS)
|
||||
- 备份目录容量上限(默认 1000GB),超过则发邮件告警且不再拉取新文件
|
||||
|
||||
凭证:环境变量 或 00_账号与API索引.md § 腾讯云
|
||||
配置:同目录 config.env(参考 config.example.env)
|
||||
依赖:tencentcloud-sdk-python-common, tencentcloud-sdk-python-cvm, tencentcloud-sdk-python-cbs
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import smtplib
|
||||
import sys
|
||||
from datetime import datetime
|
||||
from email.mime.text import MIMEText
|
||||
from pathlib import Path
|
||||
|
||||
# 多地域
|
||||
CVM_REGIONS = [
|
||||
"ap-guangzhou", "ap-shanghai", "ap-beijing", "ap-chengdu",
|
||||
"ap-nanjing", "ap-shenzhen-fsi", "ap-hongkong",
|
||||
]
|
||||
CBS_REGIONS = CVM_REGIONS # 云硬盘与 CVM 地域一致
|
||||
|
||||
def _find_karuo_ai_root():
|
||||
d = Path(__file__).resolve().parent
|
||||
for _ in range(6):
|
||||
if d.name == "卡若AI" or (
|
||||
(d / "运营中枢").is_dir() and (d / "01_卡资(金)").is_dir()
|
||||
):
|
||||
return d
|
||||
d = d.parent
|
||||
return None
|
||||
|
||||
def _read_tencent_creds():
|
||||
sid = os.environ.get("TENCENTCLOUD_SECRET_ID")
|
||||
skey = os.environ.get("TENCENTCLOUD_SECRET_KEY")
|
||||
if sid and skey:
|
||||
return sid, skey
|
||||
root = _find_karuo_ai_root()
|
||||
if not root:
|
||||
return None, None
|
||||
idx = root / "运营中枢" / "工作台" / "00_账号与API索引.md"
|
||||
if not idx.is_file():
|
||||
return None, None
|
||||
text = idx.read_text(encoding="utf-8")
|
||||
in_tencent = False
|
||||
sid = skey = None
|
||||
for line in text.splitlines():
|
||||
if "### 腾讯云" in line:
|
||||
in_tencent = True
|
||||
continue
|
||||
if in_tencent and line.strip().startswith("###"):
|
||||
break
|
||||
if not in_tencent:
|
||||
continue
|
||||
m = re.search(r"\|\s*[^|]*(?:SecretId|密钥)[^|]*\|\s*`([^`]+)`", line, re.I)
|
||||
if m and m.group(1).strip().startswith("AKID"):
|
||||
sid = m.group(1).strip()
|
||||
m = re.search(r"\|\s*SecretKey\s*\|\s*`([^`]+)`", line, re.I)
|
||||
if m:
|
||||
skey = m.group(1).strip()
|
||||
return sid, skey
|
||||
|
||||
def _load_config():
|
||||
script_dir = Path(__file__).resolve().parent
|
||||
env_file = script_dir / "config.env"
|
||||
if not env_file.is_file():
|
||||
env_file = script_dir / "config.example.env"
|
||||
config = {
|
||||
"NAS_BACKUP_ROOT": os.environ.get("NAS_BACKUP_ROOT", ""),
|
||||
"SIZE_LIMIT_GB": int(os.environ.get("SIZE_LIMIT_GB", "1000")),
|
||||
"ALERT_EMAIL_TO": os.environ.get("ALERT_EMAIL_TO", ""),
|
||||
"SMTP_HOST": os.environ.get("SMTP_HOST", "smtp.qq.com"),
|
||||
"SMTP_PORT": int(os.environ.get("SMTP_PORT", "465")),
|
||||
"SMTP_USER": os.environ.get("SMTP_USER", ""),
|
||||
"SMTP_PASSWORD": os.environ.get("SMTP_PASSWORD", ""),
|
||||
}
|
||||
if env_file.is_file():
|
||||
for line in env_file.read_text(encoding="utf-8").splitlines():
|
||||
line = line.strip()
|
||||
if not line or line.startswith("#") or "=" not in line:
|
||||
continue
|
||||
k, _, v = line.partition("=")
|
||||
k, v = k.strip(), v.strip().strip('"').strip("'")
|
||||
if k in config:
|
||||
if k == "SIZE_LIMIT_GB":
|
||||
try:
|
||||
config[k] = int(v)
|
||||
except ValueError:
|
||||
pass
|
||||
elif k == "SMTP_PORT":
|
||||
try:
|
||||
config[k] = int(v)
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
config[k] = v
|
||||
for k, v in os.environ.items():
|
||||
if k in config and v:
|
||||
if k == "SIZE_LIMIT_GB":
|
||||
try:
|
||||
config[k] = int(v)
|
||||
except ValueError:
|
||||
pass
|
||||
elif k == "SMTP_PORT":
|
||||
try:
|
||||
config[k] = int(v)
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
config[k] = v
|
||||
return config
|
||||
|
||||
def _dir_size_gb(path: Path) -> float:
|
||||
"""目录占用大小(GB)"""
|
||||
if not path.exists() or not path.is_dir():
|
||||
return 0.0
|
||||
total = 0
|
||||
try:
|
||||
for entry in path.rglob("*"):
|
||||
if entry.is_file():
|
||||
try:
|
||||
total += entry.stat().st_size
|
||||
except OSError:
|
||||
pass
|
||||
except OSError:
|
||||
pass
|
||||
return total / (1024 ** 3)
|
||||
|
||||
def _send_alert_email(config: dict, used_gb: float, limit_gb: int):
|
||||
to_addr = config.get("ALERT_EMAIL_TO") or ""
|
||||
if not to_addr:
|
||||
print("[告警] 未配置 ALERT_EMAIL_TO,跳过发邮件。")
|
||||
return
|
||||
user = config.get("SMTP_USER") or ""
|
||||
password = config.get("SMTP_PASSWORD") or ""
|
||||
if not user or not password:
|
||||
print("[告警] 未配置 SMTP_USER/SMTP_PASSWORD,跳过发邮件。")
|
||||
return
|
||||
subject = "[腾讯云备份] CKB NAS 备份目录已超过 %d GB 限制" % limit_gb
|
||||
body = (
|
||||
"腾讯云镜像/快照备份目录容量超限告警\n\n"
|
||||
"备份根目录:%s\n"
|
||||
"当前占用:%.2f GB\n"
|
||||
"设定上限:%d GB\n\n"
|
||||
"请清理旧备份或扩大限额,否则将不再拉取新镜像/快照文件。\n"
|
||||
"时间:%s\n"
|
||||
) % (
|
||||
config.get("NAS_BACKUP_ROOT", ""),
|
||||
used_gb,
|
||||
limit_gb,
|
||||
datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
||||
)
|
||||
msg = MIMEText(body, "plain", "utf-8")
|
||||
msg["Subject"] = subject
|
||||
msg["From"] = user
|
||||
msg["To"] = to_addr
|
||||
try:
|
||||
with smtplib.SMTP_SSL(config.get("SMTP_HOST", "smtp.qq.com"), config.get("SMTP_PORT", 465)) as s:
|
||||
s.login(user, password)
|
||||
s.sendmail(user, [to_addr], msg.as_string())
|
||||
print("[告警] 已发送邮件至 %s" % to_addr)
|
||||
except Exception as e:
|
||||
print("[告警] 发邮件失败: %s" % e)
|
||||
|
||||
def main():
|
||||
config = _load_config()
|
||||
nas_root = config.get("NAS_BACKUP_ROOT") or ""
|
||||
if not nas_root:
|
||||
print("请配置 NAS_BACKUP_ROOT(CKB NAS 挂载后的备份根目录)")
|
||||
print("参考 config.example.env,复制为 config.env 并填写。")
|
||||
return 1
|
||||
nas_root = Path(nas_root)
|
||||
limit_gb = config.get("SIZE_LIMIT_GB", 1000)
|
||||
|
||||
# 检查当前占用
|
||||
used_gb = _dir_size_gb(nas_root)
|
||||
if used_gb >= limit_gb:
|
||||
print("备份目录已超过 %d GB(当前 %.2f GB),发送告警邮件并跳过拉取新文件。" % (limit_gb, used_gb))
|
||||
_send_alert_email(config, used_gb, limit_gb)
|
||||
# 仍可更新元数据(不占大空间)
|
||||
else:
|
||||
print("备份目录当前 %.2f GB / %d GB" % (used_gb, limit_gb))
|
||||
|
||||
sid, skey = _read_tencent_creds()
|
||||
if not sid or not skey:
|
||||
print("未配置腾讯云 SecretId/SecretKey(环境变量或 00_账号与API索引.md)")
|
||||
return 1
|
||||
|
||||
try:
|
||||
from tencentcloud.common import credential
|
||||
from tencentcloud.cvm.v20170312 import cvm_client, models as cvm_models
|
||||
from tencentcloud.cbs.v20170312 import cbs_client, models as cbs_models
|
||||
except ImportError:
|
||||
print("请安装: pip install tencentcloud-sdk-python-common tencentcloud-sdk-python-cvm tencentcloud-sdk-python-cbs")
|
||||
return 1
|
||||
|
||||
cred = credential.Credential(sid, skey)
|
||||
meta_dir = nas_root / "meta"
|
||||
meta_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# 1) 拉取自定义镜像(多地域)
|
||||
all_images = []
|
||||
for region in CVM_REGIONS:
|
||||
try:
|
||||
client = cvm_client.CvmClient(cred, region)
|
||||
req = cvm_models.DescribeImagesRequest()
|
||||
req.Limit = 100
|
||||
req.Offset = 0
|
||||
# 只拉自定义镜像
|
||||
while True:
|
||||
resp = client.DescribeImages(req)
|
||||
items = getattr(resp, "ImageSet", None) or []
|
||||
for img in items:
|
||||
if getattr(img, "ImageType", "") == "PRIVATE_IMAGE":
|
||||
all_images.append({
|
||||
"Region": region,
|
||||
"ImageId": getattr(img, "ImageId", ""),
|
||||
"ImageName": getattr(img, "ImageName", ""),
|
||||
"ImageSize": getattr(img, "ImageSize", 0),
|
||||
"CreatedTime": getattr(img, "CreatedTime", ""),
|
||||
"ImageState": getattr(img, "ImageState", ""),
|
||||
})
|
||||
if len(items) < req.Limit:
|
||||
break
|
||||
req.Offset += req.Limit
|
||||
except Exception as e:
|
||||
print("[%s] 镜像列表: %s" % (region, e))
|
||||
|
||||
images_file = meta_dir / ("images_%s.json" % datetime.now().strftime("%Y%m%d_%H%M%S"))
|
||||
images_file.write_text(json.dumps(all_images, ensure_ascii=False, indent=2), encoding="utf-8")
|
||||
# 保留最新一份为 images_latest.json
|
||||
(meta_dir / "images_latest.json").write_text(
|
||||
json.dumps(all_images, ensure_ascii=False, indent=2), encoding="utf-8"
|
||||
)
|
||||
print("镜像元数据已写入 %s(共 %d 个自定义镜像)" % (images_file, len(all_images)))
|
||||
|
||||
# 2) 拉取快照(多地域)
|
||||
all_snapshots = []
|
||||
for region in CBS_REGIONS:
|
||||
try:
|
||||
client = cbs_client.CbsClient(cred, region)
|
||||
req = cbs_models.DescribeSnapshotsRequest()
|
||||
req.Limit = 100
|
||||
req.Offset = 0
|
||||
while True:
|
||||
resp = client.DescribeSnapshots(req)
|
||||
items = getattr(resp, "SnapshotSet", None) or []
|
||||
for sn in items:
|
||||
all_snapshots.append({
|
||||
"Region": region,
|
||||
"SnapshotId": getattr(sn, "SnapshotId", ""),
|
||||
"SnapshotName": getattr(sn, "SnapshotName", ""),
|
||||
"DiskSize": getattr(sn, "DiskSize", 0),
|
||||
"CreatedTime": getattr(sn, "CreatedTime", ""),
|
||||
"SnapshotState": getattr(sn, "SnapshotState", ""),
|
||||
})
|
||||
if len(items) < req.Limit:
|
||||
break
|
||||
req.Offset += req.Limit
|
||||
except Exception as e:
|
||||
print("[%s] 快照列表: %s" % (region, e))
|
||||
|
||||
snapshots_file = meta_dir / ("snapshots_%s.json" % datetime.now().strftime("%Y%m%d_%H%M%S"))
|
||||
snapshots_file.write_text(json.dumps(all_snapshots, ensure_ascii=False, indent=2), encoding="utf-8")
|
||||
(meta_dir / "snapshots_latest.json").write_text(
|
||||
json.dumps(all_snapshots, ensure_ascii=False, indent=2), encoding="utf-8"
|
||||
)
|
||||
print("快照元数据已写入 %s(共 %d 个快照)" % (snapshots_file, len(all_snapshots)))
|
||||
|
||||
# 3) 若配置了 COS,可从 COS 同步镜像文件到 NAS(需 tencentcloud-sdk-python-cos)
|
||||
cos_bucket = os.environ.get("COS_BUCKET") or ""
|
||||
if not cos_bucket and os.path.isfile(Path(__file__).parent / "config.env"):
|
||||
for line in (Path(__file__).parent / "config.env").read_text(encoding="utf-8").splitlines():
|
||||
if line.strip().startswith("COS_BUCKET=") and "=" in line:
|
||||
cos_bucket = line.split("=", 1)[1].strip().strip('"').strip("'")
|
||||
break
|
||||
if cos_bucket and used_gb < limit_gb:
|
||||
try:
|
||||
from tencentcloud.cos.cos_client import CosS3Client
|
||||
from tencentcloud.cos.cos_config import CosConfig
|
||||
region = os.environ.get("COS_REGION", "ap-guangzhou")
|
||||
cos_prefix = os.environ.get("COS_PREFIX", "export_images")
|
||||
config_cos = CosConfig(Region=region, SecretId=sid, SecretKey=skey)
|
||||
client_cos = CosS3Client(config_cos)
|
||||
# 列举并下载(示例:只列举,实际下载需根据 COS 返回的 Key 逐条 download)
|
||||
# 此处简化:仅提示用户可在此扩展 COS 下载逻辑
|
||||
print("[COS] 已配置 COS_BUCKET,如需自动从 COS 拉取镜像文件,请在脚本中扩展 COS 下载逻辑。")
|
||||
except ImportError:
|
||||
print("[COS] 未安装 cos-python-sdk-v5,跳过从 COS 同步。")
|
||||
else:
|
||||
print("镜像文件需在腾讯云控制台「导出镜像」到 COS 后,再通过 COS 同步或 coscmd 下载到 NAS。")
|
||||
|
||||
# 若开始时未超限但结束时超限(例如本轮有下载),再发一次告警
|
||||
used_gb_after = _dir_size_gb(nas_root)
|
||||
if used_gb_after >= limit_gb and used_gb < limit_gb:
|
||||
_send_alert_email(config, used_gb_after, limit_gb)
|
||||
|
||||
return 0
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -37,3 +37,4 @@
|
||||
| 2026-02-19 18:07:18 | 🔄 卡若AI 同步 2026-02-19 18:07 | 更新:运营中枢工作台 | 排除 >20MB: 5 个 |
|
||||
| 2026-02-19 19:40:16 | 🔄 卡若AI 同步 2026-02-19 19:40 | 更新:金仓、水桥平台对接、运营中枢工作台 | 排除 >20MB: 5 个 |
|
||||
| 2026-02-19 20:11:22 | 🔄 卡若AI 同步 2026-02-19 20:11 | 更新:金仓、运营中枢工作台 | 排除 >20MB: 5 个 |
|
||||
| 2026-02-20 07:09:16 | 🔄 卡若AI 同步 2026-02-20 07:09 | 更新:金仓、水桥平台对接、运营中枢参考资料、运营中枢工作台 | 排除 >20MB: 5 个 |
|
||||
|
||||
@@ -40,3 +40,4 @@
|
||||
| 2026-02-19 18:07:18 | 成功 | 成功 | 🔄 卡若AI 同步 2026-02-19 18:07 | 更新:运营中枢工作台 | 排除 >20MB: 5 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) |
|
||||
| 2026-02-19 19:40:16 | 成功 | 成功 | 🔄 卡若AI 同步 2026-02-19 19:40 | 更新:金仓、水桥平台对接、运营中枢工作台 | 排除 >20MB: 5 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) |
|
||||
| 2026-02-19 20:11:22 | 成功 | 成功 | 🔄 卡若AI 同步 2026-02-19 20:11 | 更新:金仓、运营中枢工作台 | 排除 >20MB: 5 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) |
|
||||
| 2026-02-20 07:09:16 | 成功 | 成功 | 🔄 卡若AI 同步 2026-02-20 07:09 | 更新:金仓、水桥平台对接、运营中枢参考资料、运营中枢工作台 | 排除 >20MB: 5 个 | [仓库](http://open.quwanzhi.com:3000/fnvtk/karuo-ai) [百科](http://open.quwanzhi.com:3000/fnvtk/karuo-ai/wiki) |
|
||||
|
||||
Reference in New Issue
Block a user