主题
06 - 存储架构:三卷持久化与数据治理
一句话:三卷按“是否进备份、IO 冷热、能否跨节点迁移”切开,data 承载会话与产物,secrets 只被进程读取且 include_secrets=false 让它永不进归档,backups 用低频盘做中转;“数据跟人走”的代价是每条生命周期规则都要落到卷级别,恢复点被定时备份的间隔锁死,密钥只能靠 Vault 权威与即时吊销兜住。
一、设计目标
数字分身的一切状态——会话记忆、技能配置、执行产物、密钥凭据——都必须与员工生命周期绑定。员工在职时数据实时可访问,闲置时数据低成本保留,离职时数据安全交接或清理。
存储架构围绕三个核心原则设计:
- 数据跟人走:实例可以销毁重建,但数据必须完整恢复
- 密钥零暴露:密钥/凭证永远不出现在备份、日志、员工可见界面中
- 分级存储:热数据(活跃实例)→ 温数据(暂存 PVC)→ 冷数据(对象存储归档)
二、三卷架构
每个员工实例挂载三个独立 PVC,职责隔离:
各卷详细说明
| 卷 | StorageClass | 容量 | 挂载路径 | 存储内容 | 备份策略 |
|---|---|---|---|---|---|
| data | qwenpaw-ssd | 10Gi | /app/working | 会话、记忆、产物、Skill 池、配置 | backup include_agents 覆盖 |
| secrets | qwenpaw-ssd | 1Gi | /app/working.secret | 模型配置、实例凭据 | 不入备份,Vault 为权威 |
| backups | qwenpaw-hdd | 5Gi | /app/working.backups | 实例内临时备份 | 定期归档到 MinIO 后清理 |
为什么分三个卷
- data 与 secrets 分离:备份时
include_secrets=false,secrets 卷不参与备份归档,降低密钥泄露风险 - data 与 backups 分离:backups 用 HDD 降低成本,且备份文件可定期清理不影响业务数据
- secrets 独立:容量小但 IO 要求高(启动时读取凭据),单独使用 SSD
三、数据分级与生命周期
各数据类型生命周期策略
| 数据类型 | 热(Active) | 温(Staged) | 冷(归档) | 清理 |
|---|---|---|---|---|
| 会话/记忆 | data 卷实时读写 | PVC 保留,秒级恢复 | backup 归档(含会话) | 离职后交接归档保留 90 天 |
| 技能池 | data 卷 skill_pool/ | PVC 保留 | backup 归档(含 skill_pool) | 跟随实例生命周期 |
| 执行产物 | data 卷 projects/{task_id}/ | PVC 保留 | 控制面取回到 MinIO | 按任务类型保留(默认 30 天) |
| 模型凭据 | secrets 卷 + K8s Secret | Vault 保留,PVC 保留 | 不入备份 | 离职即吊销(Vault 删除) |
| 实例配置 | data 卷 config.json | PVC 保留 | backup 归档(含 config) | 跟随实例生命周期 |
| 运行日志 | data 卷 qwenpaw.log | PVC 保留 | Loki 采集(30 天) | 日志轮转(7 天/100MB) |
四、备份机制
4.1 逻辑备份(主力)
通过 QwenPaw 内置 backup API 执行逻辑备份,备份内容为结构化归档:
POST /api/backups
Header: x-qwenpaw-runtime-token: {rt}
Body: {
"include_agents": true, // 工作区(会话/记忆/产物)
"include_global_config": true, // 全局配置
"include_skill_pool": true, // 技能池
"include_secrets": false // 密钥不入备份
}备份产出为一个 tar.gz 归档文件,写入 backups 卷,同时由控制面异步转存到 MinIO:
MinIO 归档路径:
qwenpaw-backups/{user_id}/{timestamp}.tar.gz
归档元信息(PostgreSQL):
backup_id | user_id | instance_id | created_at | size_bytes | minio_path | includes_agents | includes_skill_pool | includes_secrets4.2 备份触发时机
| 触发场景 | 备份类型 | 说明 |
|---|---|---|
| 闲置暂存 | 全量逻辑备份 | 停 Pod 前必须完成备份,RPO ≈ 0 |
| 定时备份 | 增量逻辑备份 | 每日 00:00 执行,RPO ≤ 24h |
| 镜像升级 | 全量逻辑备份 | 升级前备份,失败可回滚 |
| 离职交接 | 全量导出备份 | 生成交接归档,通知交接人 |
4.3 恢复流程
五、产物交付存储
5.1 交付目录结构
MinIO 交付桶:qwenpaw-artifacts
交付路径:
artifacts/{user_id}/{task_id}/
├── report.html # 产物文件
├── chart.png
├── manifest.json # 产物清单
└── metadata.json # 任务元信息
manifest.json:
{
"task_id": "task_abc123",
"user_id": "zhangsan",
"instance_id": "inst_abc123",
"created_at": "2026-10-25T10:30:00Z",
"files": [
{"name": "report.html", "size": 15234, "checksum": "sha256:...", "content_type": "text/html"},
{"name": "chart.png", "size": 89012, "checksum": "sha256:...", "content_type": "image/png"}
],
"total_size": 104246,
"source": "delegate:opencode"
}5.2 产物保留策略
| 产物类型 | 保留时长 | 清理方式 |
|---|---|---|
| 普通任务产物 | 30 天 | 定时任务自动清理 |
| 审计相关产物 | 90 天 | 定时任务自动清理 |
| 交接归档 | 90 天 | 管理员手动确认清理 |
| 大文件(> 50MB) | 7 天 | 超时转为链接(原文件删除) |
5.3 产物取回流程
六、存储容量规划与告警
6.1 容量估算
以 1000 员工规模为例:
| 存储类型 | 单实例 | 总计 | 说明 |
|---|---|---|---|
| data 卷(SSD) | 10Gi | 10Ti | 会话/记忆/产物/Skill |
| secrets 卷(SSD) | 1Gi | 1Ti | 凭据(极小) |
| backups 卷(HDD) | 5Gi | 5Ti | 临时备份 |
| MinIO 备份归档 | ~2Gi/月 | ~2Ti/月 | 逻辑备份压缩后 |
| MinIO 产物交付 | ~5Gi/月 | ~5Ti/月 | 同时受保留策略约束 |
6.2 告警规则
| 告警项 | 阈值 | 处置 |
|---|---|---|
| data 卷使用率 | > 80% | 告警 → 自动清理旧日志/临时文件 |
| data 卷使用率 | > 95% | 紧急告警 → 暂停产物写入 + 通知管理员扩容 |
| backups 卷使用率 | > 70% | 告警 → 触发归档转存到 MinIO + 清理本地 |
| MinIO 桶容量 | > 配额 80% | 预警 → 检查保留策略执行情况 |
| PVC 绑定失败 | 任何失败 | 紧急告警 → StorageClass 容量不足 |
七、数据安全原则
| 原则 | 实现 |
|---|---|
| 密钥不入备份 | include_secrets=false,Vault 为密钥唯一权威来源 |
| 密钥不进日志 | secrets 卷不挂载到日志采集路径 |
| 备份加密传输 | 控制面 → MinIO 走内网 TLS,归档文件 AES-256 加密 |
| 产物访问控制 | MinIO presigned URL(有效期 1 小时),按 user_id 鉴权 |
| 离职数据清理 | 密钥即时吊销 → 数据交接归档 → 归档到期自动清理 |
| 租户数据隔离 | PVC 按 user_id 命名 + K8s NetworkPolicy 实例间默认拒绝 |