docs(ai): make operating guide fully standalone
This commit is contained in:
+611
-51
@@ -1,6 +1,6 @@
|
|||||||
# STORAGE LABOS / FlashOps — AI 协作与系统操作指南
|
# STORAGE LABOS / FlashOps — AI 协作与系统操作指南
|
||||||
|
|
||||||
> 文档版本:1.0
|
> 文档版本:2.0
|
||||||
>
|
>
|
||||||
> 核对日期:2026-07-27
|
> 核对日期:2026-07-27
|
||||||
>
|
>
|
||||||
@@ -8,9 +8,13 @@
|
|||||||
>
|
>
|
||||||
> 目标:只把本文件交给 AI,也能让它安全地理解、运行、修改、验证和交付本系统
|
> 目标:只把本文件交给 AI,也能让它安全地理解、运行、修改、验证和交付本系统
|
||||||
|
|
||||||
本文件是 AI 接手项目时的第一入口。更完整的产品、架构、数据模型、状态机和生产运维说明见
|
本文件是可以单独分发给 AI 的完整系统说明书,不依赖仓库中的任何其他文档。它已经内嵌产品边界、
|
||||||
[ProjectManual.md](ProjectManual.md)。如果本文件与代码冲突,以代码和测试为准,并在本次变更中
|
架构、数据模型、Run/Step 状态机、恢复阶梯、API、Agent 协议、开发流程、生产运维、Git 和安全规则。
|
||||||
同步修正文档;如果生产信息与代码冲突,先做只读核验,不要凭文档直接修改服务器。
|
即使 AI 只能看到这一份文件,也应能够正确查询线上系统、运行本地模拟、判断允许执行的动作,并在
|
||||||
|
获得仓库或服务器权限后安全开发和运维。
|
||||||
|
|
||||||
|
如果 AI 同时能看到源码,以实际代码和测试结果为准;如果生产状态与本文不一致,先只读核验并报告
|
||||||
|
差异,不要凭本文直接覆盖服务器。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -21,12 +25,13 @@
|
|||||||
~~~text
|
~~~text
|
||||||
你正在维护 STORAGE LABOS / FlashOps。
|
你正在维护 STORAGE LABOS / FlashOps。
|
||||||
|
|
||||||
请先完整阅读仓库根目录 CONTRIBUTING.md,再按其中的权限边界、架构红线、测试门禁和
|
你收到的这份文档已经包含操作本系统所需的产品、架构、状态机、API、Agent、生产运维、
|
||||||
Git 交付流程工作。需要深入理解时再阅读 ProjectManual.md 及 flashops/docs/。
|
安全和 Git 规则。请完整阅读并只以本文作为任务上下文,不要要求用户再提供其他项目文档。
|
||||||
|
|
||||||
先检查仓库状态并保护现有未提交改动。默认只操作本地开发环境;除非任务明确要求,不得修改
|
如果任务同时提供仓库,先检查状态并保护现有未提交改动;如果只有本文,先使用公开 GET 接口进行
|
||||||
生产服务器、生产数据、DNS、证书、Gitea 设置或调用生产环境写接口。任何真实硬件、固件刷写、
|
只读核验。默认只操作本地开发环境;除非任务明确要求,不得修改生产服务器、生产数据、DNS、证书、
|
||||||
Format、Sanitize、Reset、断电或 OOB 动作都必须停下并取得明确授权。
|
Gitea 设置或调用生产环境写接口。任何真实硬件、固件刷写、Format、Sanitize、Reset、断电或 OOB
|
||||||
|
动作都必须停下并取得明确授权。
|
||||||
|
|
||||||
任务:<在这里写具体目标>
|
任务:<在这里写具体目标>
|
||||||
|
|
||||||
@@ -50,7 +55,8 @@ Format、Sanitize、Reset、断电或 OOB 动作都必须停下并取得明确
|
|||||||
| 本地 API 文档 | <http://localhost:8000/docs> |
|
| 本地 API 文档 | <http://localhost:8000/docs> |
|
||||||
| 后端 | Python 3.9+、FastAPI、SQLAlchemy |
|
| 后端 | Python 3.9+、FastAPI、SQLAlchemy |
|
||||||
| 当前数据库 | SQLite,必须保持单 worker |
|
| 当前数据库 | SQLite,必须保持单 worker |
|
||||||
| 当前执行能力 | 模拟 Run 与 Agent dry-run;不代表真实硬件动作已交付 |
|
| 仓库代码能力 | 模拟 Run 与 Agent dry-run;不代表真实硬件动作已交付 |
|
||||||
|
| 当前线上能力 | API 版本 0.1.0,仅模拟 Run;Agent 路由尚未发布 |
|
||||||
| 当前测试基线 | 25 项 |
|
| 当前测试基线 | 25 项 |
|
||||||
| 生产认证边界 | 预览站匿名开放,尚无应用级 RBAC |
|
| 生产认证边界 | 预览站匿名开放,尚无应用级 RBAC |
|
||||||
| Gitea | 公开仓库匿名可读;注册账号需管理员批准 |
|
| Gitea | 公开仓库匿名可读;注册账号需管理员批准 |
|
||||||
@@ -84,21 +90,20 @@ AI 应按以下顺序判断是否可以行动:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 3. 信息源优先级
|
## 3. 决策依据与证据优先级
|
||||||
|
|
||||||
AI 不应只根据文件名或旧说明猜测当前能力,按下面顺序取证:
|
AI 不应只根据文件名或旧说明猜测当前能力,按下面顺序取证:
|
||||||
|
|
||||||
1. 当前用户的明确任务与限制;
|
1. 当前用户的明确任务、授权与限制;
|
||||||
2. 当前工作树、Git diff 和实际源码;
|
2. 本文件写明的安全边界和不可执行动作;
|
||||||
3. 自动化测试及可重复的本地运行结果;
|
3. 线上健康检查、API 返回、服务状态和日志等实际证据;
|
||||||
4. 本文件;
|
4. 如果 AI 获得仓库:当前工作树、Git diff、源码和自动化测试;
|
||||||
5. ProjectManual.md;
|
5. 如果 AI 获得服务器:生效配置、运行进程、数据路径和备份;
|
||||||
6. flashops/docs/ 下的架构、状态机和 ADR;
|
6. 历史提交或历史部署记录;
|
||||||
7. README、历史提交、部署模板;
|
7. 推测。
|
||||||
8. 推测。
|
|
||||||
|
|
||||||
如果代码、测试和文档互相矛盾,必须在交付说明中指出。不得把“规划”“草案”“静态页面”描述成
|
如果本文、代码、测试和生产状态互相矛盾,必须在交付说明中指出。不得把“规划”“草案”“静态
|
||||||
已经可用于真实硬件的能力。
|
页面”描述成已经可用于真实硬件的能力,也不得因为拥有技术访问能力就推断拥有写入授权。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -132,20 +137,18 @@ git log -5 --oneline
|
|||||||
先识别用户已有的未提交改动。不得使用 reset --hard、checkout 丢弃文件、clean -fd 或其他会
|
先识别用户已有的未提交改动。不得使用 reset --hard、checkout 丢弃文件、clean -fd 或其他会
|
||||||
抹掉用户工作的命令。不得把不相关改动混进自己的提交。
|
抹掉用户工作的命令。不得把不相关改动混进自己的提交。
|
||||||
|
|
||||||
### 4.3 推荐阅读路线
|
### 4.3 只有本文时
|
||||||
|
|
||||||
按任务读取最少但足够的资料:
|
只有本文、没有仓库时,AI 仍可以:
|
||||||
|
|
||||||
| 任务 | 必读文件 |
|
- 使用第 6 节列出的公开 GET 接口查询健康、指标、Run、事件和 Host;
|
||||||
|---|---|
|
- 根据第 5 节解释状态、恢复、检查点和系统边界;
|
||||||
| 普通开发 | 本文件、根 README、flashops/README.md |
|
- 根据第 11 节指导获授权的人完成服务器检查、发布、备份和故障排查;
|
||||||
| 状态机或恢复 | flashops/docs/state-machine.md、decisions/0003-* |
|
- 根据第 2 节拒绝未授权生产写入与危险硬件动作;
|
||||||
| 事件和数据一致性 | flashops/docs/domain-model.md、decisions/0002-* |
|
- 输出诊断结论、变更方案、验证清单和交接记录。
|
||||||
| 安全门禁 | flashops/docs/decisions/0004-*、safety/gates.py |
|
|
||||||
| Agent | flashops/docs/agent-protocol.md、services/host-agent/README.md |
|
只有本文时,AI 不得声称已经修改代码、提交 Git、连接服务器或执行生产动作。只有在真正获得相应
|
||||||
| 工作流 | flashops/docs/workflow-spec.md |
|
工具和授权后,才能执行这些操作。
|
||||||
| 适配器或真实工具 | flashops/docs/adapter-sdk.md |
|
|
||||||
| 部署、域名、备份 | ProjectManual.md 第 12 章、flashops/deploy/ |
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -153,18 +156,14 @@ git log -5 --oneline
|
|||||||
|
|
||||||
~~~text
|
~~~text
|
||||||
STORAGE LABOS/
|
STORAGE LABOS/
|
||||||
├── CONTRIBUTING.md AI 与团队统一入口
|
├── CONTRIBUTING.md 本文:完整 AI 系统说明书
|
||||||
├── ProjectManual.md 完整产品、开发和运维手册
|
|
||||||
├── README.md 项目总览
|
|
||||||
├── .gitea/ PR 模板与未来 CI 工作流
|
├── .gitea/ PR 模板与未来 CI 工作流
|
||||||
├── flashops/
|
├── flashops/
|
||||||
│ ├── services/control-plane/ FastAPI 控制平面
|
│ ├── services/control-plane/ FastAPI 控制平面
|
||||||
│ ├── services/host-agent/ 拉模式 Host Agent
|
│ ├── services/host-agent/ 拉模式 Host Agent
|
||||||
│ ├── docs/ 架构、协议、状态机、ADR
|
|
||||||
│ ├── deploy/ FlashOps 与 Gitea 部署模板
|
│ ├── deploy/ FlashOps 与 Gitea 部署模板
|
||||||
│ ├── var/ 本地数据库和证据,禁止提交
|
│ ├── var/ 本地数据库和证据,禁止提交
|
||||||
│ ├── Makefile 开发和验证入口
|
│ └── Makefile 开发和验证入口
|
||||||
│ └── README.md 可运行能力说明
|
|
||||||
└── 前端UI八页面完成/ FastAPI 同源挂载的静态控制台
|
└── 前端UI八页面完成/ FastAPI 同源挂载的静态控制台
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
@@ -173,7 +172,8 @@ STORAGE LABOS/
|
|||||||
- FastAPI 控制平面、14 张持久化表和 SQLite 数据库;
|
- FastAPI 控制平面、14 张持久化表和 SQLite 数据库;
|
||||||
- Run 事件溯源、状态投影、状态机和安全门禁;
|
- Run 事件溯源、状态投影、状态机和安全门禁;
|
||||||
- 模拟回归、故障注入、L1–L3 恢复演示和 Evidence Bundle;
|
- 模拟回归、故障注入、L1–L3 恢复演示和 Evidence Bundle;
|
||||||
- Agent 注册、token、心跳、步骤租约、ACK、续租、事件、完成、超时回收和幂等链路;
|
- 仓库代码已具备 Agent 注册、token、心跳、步骤租约、ACK、续租、事件、完成、超时回收和幂等链路,
|
||||||
|
但该增量尚未发布到当前公网环境;
|
||||||
- 公开预览控制台及部分真实 API 接入页面。
|
- 公开预览控制台及部分真实 API 接入页面。
|
||||||
|
|
||||||
当前不应声称已经具备:
|
当前不应声称已经具备:
|
||||||
@@ -184,10 +184,217 @@ STORAGE LABOS/
|
|||||||
- 多工位生产调度、PostgreSQL 高可用;
|
- 多工位生产调度、PostgreSQL 高可用;
|
||||||
- 已验收的失败聚类自动决策。
|
- 已验收的失败聚类自动决策。
|
||||||
|
|
||||||
|
### 5.1 五层架构与依赖方向
|
||||||
|
|
||||||
|
~~~text
|
||||||
|
浏览器控制台 ──HTTP──▶ FastAPI 控制平面 ◀──HTTP 长轮询── Host Agent
|
||||||
|
│ │
|
||||||
|
▼ ├──▶ 工具适配器
|
||||||
|
SQLite / 证据 └──▶ 独立 OOB 控制器
|
||||||
|
~~~
|
||||||
|
|
||||||
|
五个逻辑层:
|
||||||
|
|
||||||
|
1. 产品交互层:控制台页面和 HTTP API;
|
||||||
|
2. 控制平面:工作流物化、状态机、安全门禁、资源锁、事件和恢复决策;
|
||||||
|
3. 数据面:客户测试主机上的拉模式 Agent,只执行控制平面批准的步骤;
|
||||||
|
4. 带外平面:独立于测试主机的 OOB 观测与电源控制,主机死亡后仍应可用;
|
||||||
|
5. 证据与分析层:事件时间线、Evidence Bundle、失败签名和审计。
|
||||||
|
|
||||||
|
依赖必须单向:
|
||||||
|
|
||||||
|
- Agent 不依赖控制平面的 Python 模块,只通过 HTTP 契约通信;
|
||||||
|
- 控制平面不主动反连 Agent,Agent 必须主动注册、心跳和领取步骤;
|
||||||
|
- 状态机与恢复决策保持纯函数,不做数据库、网络或时钟 I/O;
|
||||||
|
- API 路由只校验请求并调用 service,不承载核心业务判断;
|
||||||
|
- 所有危险动作在下发 Agent 前先经过安全门禁;
|
||||||
|
- 带外通道不能依赖被测主机的 OS、网络栈或 Agent 进程。
|
||||||
|
|
||||||
|
### 5.2 核心数据模型
|
||||||
|
|
||||||
|
当前 SQLite 中的核心对象如下,AI 讨论数据时应使用这些名称:
|
||||||
|
|
||||||
|
| 对象 | 作用 |
|
||||||
|
|---|---|
|
||||||
|
| TestHost | 测试主机、Agent 身份、心跳和工具链能力 |
|
||||||
|
| OobController | 独立带外控制器、电源和观测能力 |
|
||||||
|
| Dut | 被测盘及序列号、系统盘保护、状态 |
|
||||||
|
| FirmwareArtifact | 固件版本、型号适配和校验信息 |
|
||||||
|
| Workflow | 版本化 SOP 与步骤定义 |
|
||||||
|
| Run | 一次工作流执行及当前状态投影 |
|
||||||
|
| RunStep | Run 物化后的顺序步骤、attempt、租约和结果 |
|
||||||
|
| RunEvent | append-only 统一事件时间线 |
|
||||||
|
| RecoveryAction | 恢复级别、触发原因、结果和证据 |
|
||||||
|
| ResourceLock | DUT、Host 等独占资源锁 |
|
||||||
|
| FailureSignature | 归一化失败签名 |
|
||||||
|
| RunFailure | Run 与失败签名的关联 |
|
||||||
|
| EvidenceBundle | 证据包 URI、完整率、大小和哈希 |
|
||||||
|
| AuditLog | 人工或系统管理动作的审计记录 |
|
||||||
|
|
||||||
|
RunEvent 是状态变化的权威事实;Run 和 RunStep 上的 state 是事件施加后的当前投影。修改投影时必须
|
||||||
|
与事件处于同一数据库事务,不能先改状态后补事件。
|
||||||
|
|
||||||
|
### 5.3 Run 状态机
|
||||||
|
|
||||||
|
~~~text
|
||||||
|
QUEUED ──资源锁成功──▶ PREFLIGHT ──六项门禁通过──▶ RUNNING
|
||||||
|
│ │ │
|
||||||
|
└──紧急停止──▶ ABORTED ├──门禁拒绝──▶ REJECTED ├──完成──▶ COMPLETED
|
||||||
|
└──紧急停止──▶ ABORTED ├──人工暂停──▶ PAUSED
|
||||||
|
│ │
|
||||||
|
│ └──继续──▶ RUNNING
|
||||||
|
├──故障──▶ RECOVERING
|
||||||
|
│ ├──恢复成功──▶ RUNNING
|
||||||
|
│ └──耗尽/不安全──▶ FROZEN
|
||||||
|
├──冻结──▶ FROZEN
|
||||||
|
└──紧急停止──▶ ABORTED
|
||||||
|
~~~
|
||||||
|
|
||||||
|
状态含义:
|
||||||
|
|
||||||
|
| 状态 | 含义 |
|
||||||
|
|---|---|
|
||||||
|
| QUEUED | 等待 DUT、Host 等独占资源 |
|
||||||
|
| PREFLIGHT | 执行安全门禁并冻结环境指纹 |
|
||||||
|
| RUNNING | 正在执行步骤或循环 |
|
||||||
|
| RECOVERING | 正在执行恢复阶梯 |
|
||||||
|
| PAUSED | 在安全检查点人工暂停 |
|
||||||
|
| COMPLETED | 正常结束,verdict 为 PASS 或 FAIL |
|
||||||
|
| FROZEN | 冻结现场,禁止覆盖性动作 |
|
||||||
|
| ABORTED | 紧急停止 |
|
||||||
|
| REJECTED | 安全门禁拒绝启动 |
|
||||||
|
|
||||||
|
终态为 COMPLETED、FROZEN、ABORTED、REJECTED,终态不可继续转移。任何非法转移都应被视为代码
|
||||||
|
缺陷,不能吞掉异常继续运行。任意非终态都可以因紧急停止进入 ABORTED;终态不能再次停止、暂停或继续。
|
||||||
|
|
||||||
|
### 5.4 Step 状态机与租约
|
||||||
|
|
||||||
|
~~~text
|
||||||
|
PENDING ──租给 Agent──▶ DISPATCHED ──ACK──▶ RUNNING
|
||||||
|
▲ ├──▶ SUCCEEDED
|
||||||
|
│ ├──▶ FAILED
|
||||||
|
└──仅幂等且可重试时 attempt+1 ◀─────────────├──▶ TIMED_OUT
|
||||||
|
├──▶ CANCELLED
|
||||||
|
└──▶ SKIPPED
|
||||||
|
~~~
|
||||||
|
|
||||||
|
- PENDING:等待调度;
|
||||||
|
- DISPATCHED:已分配 Agent 并创建 60 秒租约,等待 ACK;
|
||||||
|
- RUNNING:Agent 已确认接手,执行期间必须续租;
|
||||||
|
- SUCCEEDED、FAILED、TIMED_OUT、CANCELLED、SKIPPED:步骤终态;
|
||||||
|
- 每次派发增加 attempt,旧 attempt 的 ACK、续租、事件和完成上报必须返回 409;
|
||||||
|
- DISPATCHED 未 ACK 而租约到期时退回 PENDING;
|
||||||
|
- RUNNING 租约到期时,只有幂等且仍有重试额度的步骤能退回 PENDING;
|
||||||
|
- 非幂等步骤或重试耗尽时进入 TIMED_OUT,Run verdict 变为 INCONCLUSIVE 并冻结;
|
||||||
|
- retry 默认是 0;刷写、Format、Sanitize 等危险步骤默认非幂等且不得自动重试。
|
||||||
|
|
||||||
|
### 5.5 五级恢复阶梯
|
||||||
|
|
||||||
|
恢复动作根据可用通道逐级升级,不允许跳过安全判断:
|
||||||
|
|
||||||
|
| 级别 | 动作 | 使用前提 | 典型触发 |
|
||||||
|
|---|---|---|---|
|
||||||
|
| L1_AGENT_SOFT | Agent 优雅停止或软重启进程 | Agent 仍在线 | 脚本卡死、进程僵死 |
|
||||||
|
| L2_OS_REBOOT | 通过 OS 通道重启主机 | 主机网络仍在线 | Agent 崩溃、驱动异常 |
|
||||||
|
| L3_OOB_RESET | OOB 触发主板 Reset | OOB 在线 | 心跳丢失、蓝屏 |
|
||||||
|
| L4_OOB_ATX_POWER | OOB 模拟 ATX 长按关机再开机 | OOB 在线 | Reset 无效 |
|
||||||
|
| L5_OOB_AC_CYCLE | AC 断电、安全等待、重新上电 | OOB 在线 | ATX 无效、DUT 掉盘最后手段 |
|
||||||
|
| FREEZE | 冻结现场并停止自动恢复 | 无 | 数据完整性失败或阶梯耗尽 |
|
||||||
|
|
||||||
|
不可更改的恢复规则:
|
||||||
|
|
||||||
|
1. 数据完整性不一致直接 FREEZE,不能先重启或重跑;
|
||||||
|
2. OOB 离线时 L3–L5 不可用,应标记 INFRA_FAILURE 并冻结,不能误判 DUT_DEFECT;
|
||||||
|
3. 每次恢复必须记录 trigger、level、outcome、时间和 evidence_uri;
|
||||||
|
4. 恢复成功只表示系统重新可执行,不表示原步骤成功;
|
||||||
|
5. 每循环最多恢复 3 次,每 Run 最多恢复 20 次;
|
||||||
|
6. L5 断电后默认安全等待 10 秒再上电;
|
||||||
|
7. 真实 L3–L5 当前没有交付,现有演示只证明状态和证据链路。
|
||||||
|
|
||||||
|
失败分类必须区分:
|
||||||
|
|
||||||
|
- DUT_DEFECT:证据支持被测盘缺陷;
|
||||||
|
- INFRA_FAILURE:供电、网络、Host、OOB 等基础设施问题;
|
||||||
|
- SCRIPT_FAILURE:测试脚本或工具问题;
|
||||||
|
- DATA_INTEGRITY:数据校验失败,立即冻结;
|
||||||
|
- UNKNOWN:证据不足,不能强行归类。
|
||||||
|
|
||||||
|
### 5.6 检查点与断点续跑
|
||||||
|
|
||||||
|
检查点只在安全的步骤边界创建,不在无法证明幂等的步骤中间创建。
|
||||||
|
|
||||||
|
| 中断位置 | 恢复后的行为 |
|
||||||
|
|---|---|
|
||||||
|
| 步骤已 SUCCEEDED 且检查点已保存 | 从下一步继续 |
|
||||||
|
| RUNNING 步骤幂等 | 可重跑当前步骤 |
|
||||||
|
| RUNNING 步骤非幂等 | 不自动重跑,INCONCLUSIVE 并等待人工确认 |
|
||||||
|
| 循环体中的步骤 | 从当前 loop_index 的循环起点重跑整轮 |
|
||||||
|
|
||||||
|
检查点至少要能确定 loop_index、下一步骤、当前固件/环境指纹和最后事件序号。保存检查点必须先于
|
||||||
|
推进下一步骤;恢复时必须验证数据库投影、Agent attempt 和 DUT 当前状态一致。
|
||||||
|
|
||||||
|
### 5.7 心跳与双通道观测
|
||||||
|
|
||||||
|
~~~text
|
||||||
|
Agent 正常心跳:每 5 秒
|
||||||
|
15 秒未收到:Host → DEGRADED,只记事件,不立刻做危险动作
|
||||||
|
30 秒未收到:Host → OFFLINE,并结合 OOB 判断
|
||||||
|
├── OOB 在线且主机有电:可进入 L3
|
||||||
|
├── OOB 在线但主机无电:INFRA_FAILURE
|
||||||
|
└── OOB 也离线:INFRA_FAILURE + FREEZE
|
||||||
|
心跳恢复:核对 Run、Step、attempt、租约和 DUT 枚举后再决定续跑
|
||||||
|
~~~
|
||||||
|
|
||||||
|
单一心跳、网络 ping 或 UI 状态都不能独立证明 DUT 故障。涉及恢复和缺陷归因时必须组合 Agent、
|
||||||
|
Host 网络、OOB 电源/画面和 DUT 枚举至少两个通道的证据。
|
||||||
|
|
||||||
|
### 5.8 六项安全门禁
|
||||||
|
|
||||||
|
创建 Run 前至少检查:
|
||||||
|
|
||||||
|
1. discovered_serial 与登记 DUT 序列号完全一致;
|
||||||
|
2. 目标不是系统盘,且没有系统分区或根文件系统;
|
||||||
|
3. 破坏性动作已有明确授权且授权尚未过期;
|
||||||
|
4. 固件型号、目标设备和校验值匹配;
|
||||||
|
5. Host 在线、未被其他 Run 占用且环境指纹满足要求;
|
||||||
|
6. 需要 OOB 的工作流必须确认 OOB 在线且能力匹配。
|
||||||
|
|
||||||
|
任一门禁失败都进入 REJECTED,不得通过 UI、API 参数或直接数据库写入绕过。真实危险命令还必须
|
||||||
|
增加签名命令模板、参数 schema、allowlist、审批人、审批时间、可牺牲 DUT 和短期授权。
|
||||||
|
|
||||||
|
### 5.9 Evidence Bundle
|
||||||
|
|
||||||
|
最小证据包包含 manifest.json 与按 seq 排序的 events.ndjson,并记录:
|
||||||
|
|
||||||
|
- Run、Workflow、DUT、Firmware、Host 和环境指纹;
|
||||||
|
- 完整事件时间线和人工介入;
|
||||||
|
- 步骤结果、退出码、日志 URI;
|
||||||
|
- 恢复动作与恢复前后证据;
|
||||||
|
- 检查点、verdict、完整率、大小和哈希。
|
||||||
|
|
||||||
|
大日志和二进制产物存对象目录,数据库只保存索引、摘要和 URI。事件按控制平面分配的 seq 排序,
|
||||||
|
不能依赖不同主机的本地时间戳。任何人工暂停、继续、急停、审批或覆盖默认策略的动作都必须留下事件。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 6. AI 如何安全使用正在运行的系统
|
## 6. AI 如何安全使用正在运行的系统
|
||||||
|
|
||||||
|
### 6.0 控制台页面
|
||||||
|
|
||||||
|
| 页面 | URL | 当前能力 |
|
||||||
|
|---|---|---|
|
||||||
|
| 总览 | /console/Dashboard.dc.html | 产品看板,部分指标仍为静态展示 |
|
||||||
|
| 任务中心 | /console/Tasks.dc.html | 已接安全预检和创建 Run |
|
||||||
|
| 实时运行 | /console/LiveRun.dc.html | 已接 Run、事件、演示、暂停、继续和急停 |
|
||||||
|
| 证据浏览器 | /console/Evidence.dc.html | 主要为静态样例,真实下载待接 |
|
||||||
|
| 失败中心 | /console/Failures.dc.html | 静态原型,失败聚类执行待实现 |
|
||||||
|
| 资产中心 | /console/Assets.dc.html | 静态原型,资产 CRUD 待实现 |
|
||||||
|
| 版本比较 | /console/Compare.dc.html | 静态原型,真实 A/B 查询待实现 |
|
||||||
|
| 工作流模板 | /console/Workflows.dc.html | 静态原型,编辑与发布 API 待实现 |
|
||||||
|
|
||||||
|
控制台和 API 同源,动态请求使用 /api/v1 相对路径。页面展示不是后端能力证据;AI 必须通过 API
|
||||||
|
返回或测试确认功能是否真实接入。事件轮询使用 after_seq 增量读取。
|
||||||
|
|
||||||
### 6.1 公网只读检查
|
### 6.1 公网只读检查
|
||||||
|
|
||||||
以下请求只读取公开信息,可用于健康诊断:
|
以下请求只读取公开信息,可用于健康诊断:
|
||||||
@@ -196,10 +403,32 @@ STORAGE LABOS/
|
|||||||
curl -fsS https://flashops.imagebrewing.com/api/v1/health
|
curl -fsS https://flashops.imagebrewing.com/api/v1/health
|
||||||
curl -fsS https://flashops.imagebrewing.com/api/v1/dashboard/summary
|
curl -fsS https://flashops.imagebrewing.com/api/v1/dashboard/summary
|
||||||
curl -fsS 'https://flashops.imagebrewing.com/api/v1/runs?limit=10'
|
curl -fsS 'https://flashops.imagebrewing.com/api/v1/runs?limit=10'
|
||||||
curl -fsS https://flashops.imagebrewing.com/api/v1/agent/hosts
|
|
||||||
~~~
|
~~~
|
||||||
|
|
||||||
不要在诊断任务中为了“试一下”调用生产 POST 接口。健康检查成功不代表业务能力或真实硬件已经验收。
|
不要在诊断任务中为了“试一下”调用生产 POST 接口。健康检查成功不代表业务能力或真实硬件已经验收。
|
||||||
|
当前公网 OpenAPI 版本为 0.1.0,Agent 路径尚未发布,访问 /api/v1/agent/hosts 返回 404 是已知部署
|
||||||
|
边界,不应误判成 Gitea、Nginx 或 Host 故障。
|
||||||
|
|
||||||
|
Run 查询的关键字段:
|
||||||
|
|
||||||
|
| 字段 | 含义 |
|
||||||
|
|---|---|
|
||||||
|
| id、name | Run 标识和名称 |
|
||||||
|
| state、verdict | 当前状态与 PASS / FAIL / INCONCLUSIVE |
|
||||||
|
| execution_mode | simulated 或 agent_dry_run |
|
||||||
|
| workflow_key、workflow_version | 固定的工作流版本 |
|
||||||
|
| dut_id、test_host_id | 绑定资产 |
|
||||||
|
| loop_target、loop_done、progress | 循环目标与完成进度 |
|
||||||
|
| checkpoint | 最近安全恢复点 |
|
||||||
|
| environment | 创建时冻结的环境指纹 |
|
||||||
|
| recovery_count | 已执行恢复次数 |
|
||||||
|
| unattended_completion | 是否无人干预完成 |
|
||||||
|
| human_touches | 人工介入次数 |
|
||||||
|
| freeze_reason | 冻结原因 |
|
||||||
|
| evidence | 详情接口返回的证据包索引 |
|
||||||
|
|
||||||
|
事件字段为 id、seq、ts、source、kind、severity、step_id、loop_index、message、payload。必须按 seq
|
||||||
|
消费;after_seq 传客户端最后确认的 seq。source 可能是 control_plane、agent、oob 或 human。
|
||||||
|
|
||||||
### 6.2 本地启动
|
### 6.2 本地启动
|
||||||
|
|
||||||
@@ -242,6 +471,8 @@ curl -fsS 'http://127.0.0.1:8000/api/v1/runs/RUN_ID/events?after_seq=0'
|
|||||||
|
|
||||||
### 6.4 API 快速索引
|
### 6.4 API 快速索引
|
||||||
|
|
||||||
|
下表中的非 Agent 路径已在当前公网部署;Agent 路径属于仓库代码基线,当前只适合本地开发和测试。
|
||||||
|
|
||||||
| 方法与路径 | 含义 | 默认安全级别 |
|
| 方法与路径 | 含义 | 默认安全级别 |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| GET /api/v1/health | 服务健康 | 只读 |
|
| GET /api/v1/health | 服务健康 | 只读 |
|
||||||
@@ -258,11 +489,68 @@ curl -fsS 'http://127.0.0.1:8000/api/v1/runs/RUN_ID/events?after_seq=0'
|
|||||||
| POST /api/v1/runs/{id}/emergency-stop | 紧急停止 | 高影响写操作 |
|
| POST /api/v1/runs/{id}/emergency-stop | 紧急停止 | 高影响写操作 |
|
||||||
| POST /api/v1/agent/* | Agent 注册与执行协议 | 凭据操作;不得随意调用 |
|
| POST /api/v1/agent/* | Agent 注册与执行协议 | 凭据操作;不得随意调用 |
|
||||||
|
|
||||||
请求体和响应模型以运行时 OpenAPI 为准。本地启动后优先读取
|
核心请求体已经内嵌如下;运行时 OpenAPI 只用于核验部署版本是否发生变化,不是阅读本文的前置条件。
|
||||||
<http://localhost:8000/openapi.json> 或 <http://localhost:8000/docs>,不要凭记忆构造字段。
|
|
||||||
|
Preflight 请求:
|
||||||
|
|
||||||
|
~~~json
|
||||||
|
{
|
||||||
|
"dut_id": "可选 DUT ID",
|
||||||
|
"host_id": "可选 Host ID",
|
||||||
|
"firmware_id": "可选固件 ID",
|
||||||
|
"discovered_serial": "探针实际发现的序列号"
|
||||||
|
}
|
||||||
|
~~~
|
||||||
|
|
||||||
|
Create Run 请求:
|
||||||
|
|
||||||
|
~~~json
|
||||||
|
{
|
||||||
|
"name": "固件 A/B 无人值守回归",
|
||||||
|
"workflow_id": null,
|
||||||
|
"dut_id": null,
|
||||||
|
"host_id": null,
|
||||||
|
"firmware_a_id": null,
|
||||||
|
"firmware_b_id": null,
|
||||||
|
"loops": 8,
|
||||||
|
"inject_failure": true,
|
||||||
|
"execution_mode": "simulated",
|
||||||
|
"created_by": "console-demo"
|
||||||
|
}
|
||||||
|
~~~
|
||||||
|
|
||||||
|
- loops 范围 1–500;
|
||||||
|
- execution_mode 只能是 simulated 或 agent_dry_run;
|
||||||
|
- simulated 会由控制平面启动内置模拟执行器;
|
||||||
|
- agent_dry_run 会物化步骤并等待独立 Agent 领取,但不允许真实主机命令。
|
||||||
|
|
||||||
|
暂停、继续和紧急停止使用:
|
||||||
|
|
||||||
|
~~~json
|
||||||
|
{
|
||||||
|
"actor": "操作者或 AI 任务标识",
|
||||||
|
"reason": "可审计的具体原因"
|
||||||
|
}
|
||||||
|
~~~
|
||||||
|
|
||||||
|
常见状态码:
|
||||||
|
|
||||||
|
| 状态码 | 含义 |
|
||||||
|
|---:|---|
|
||||||
|
| 200 | 查询或动作成功 |
|
||||||
|
| 201 | Run、注册等资源创建成功 |
|
||||||
|
| 204 | Agent 当前没有可领取步骤 |
|
||||||
|
| 400 | 业务参数或资产关系无效 |
|
||||||
|
| 401 | Agent 注册口令或 bearer token 无效 |
|
||||||
|
| 404 | Run、Host、DUT 等对象不存在 |
|
||||||
|
| 409 | 非法状态、资源冲突、旧 attempt 或幂等键冲突 |
|
||||||
|
| 422 | 请求字段或 Idempotency-Key 校验失败 |
|
||||||
|
| 503 | 生产环境缺少 Agent 注册口令 |
|
||||||
|
|
||||||
### 6.5 Agent 使用规则
|
### 6.5 Agent 使用规则
|
||||||
|
|
||||||
|
- 本节描述仓库已经实现但尚未发布到公网的 Agent 协议;不要向当前生产域名调用这些端点;
|
||||||
|
- Agent 是拉模式,控制平面永远不主动连接测试主机;
|
||||||
- 生产注册需要 X-FlashOps-Enrollment-Token;
|
- 生产注册需要 X-FlashOps-Enrollment-Token;
|
||||||
- 注册成功返回的原始 bearer token 只出现一次,只能保存到权限 0600 的本机状态文件;
|
- 注册成功返回的原始 bearer token 只出现一次,只能保存到权限 0600 的本机状态文件;
|
||||||
- 后续 Agent 请求使用 Authorization: Bearer;
|
- 后续 Agent 请求使用 Authorization: Bearer;
|
||||||
@@ -271,6 +559,60 @@ curl -fsS 'http://127.0.0.1:8000/api/v1/runs/RUN_ID/events?after_seq=0'
|
|||||||
- 未经授权不得代表真实 Host 注册、轮换 token 或领取生产步骤;
|
- 未经授权不得代表真实 Host 注册、轮换 token 或领取生产步骤;
|
||||||
- 日常开发优先使用 make dev-agent、make agent-demo 和测试夹具。
|
- 日常开发优先使用 make dev-agent、make agent-demo 和测试夹具。
|
||||||
|
|
||||||
|
Agent 端点和顺序:
|
||||||
|
|
||||||
|
| 顺序 | 方法与路径 | 行为 |
|
||||||
|
|---:|---|---|
|
||||||
|
| 1 | POST /api/v1/agent/register | 注册或轮换 token,上报 Host 指纹 |
|
||||||
|
| 2 | POST /api/v1/agent/{agent_id}/heartbeat | 上报健康、工具链和设备探针 |
|
||||||
|
| 3 | POST /api/v1/agent/{agent_id}/lease | 领取步骤,最长等待 20 秒 |
|
||||||
|
| 4 | POST /api/v1/agent/{agent_id}/steps/{step_id}/ack | 确认当前 attempt |
|
||||||
|
| 5 | POST /api/v1/agent/{agent_id}/steps/{step_id}/renew | 续租,不超过步骤 timeout |
|
||||||
|
| 6 | POST /api/v1/agent/{agent_id}/steps/{step_id}/events | 批量上报 1–100 条事件 |
|
||||||
|
| 7 | POST /api/v1/agent/{agent_id}/steps/{step_id}/complete | 上报 SUCCEEDED 或 FAILED |
|
||||||
|
|
||||||
|
注册体至少需要 agent_version,可选 host_id 或 host_name,并可携带 os_family、os_version、kernel、
|
||||||
|
cpu、motherboard、bios_version、ip_address、toolchain 和 labels。心跳体可携带 healthy、
|
||||||
|
agent_version、ip_address、toolchain 和 device_probe。
|
||||||
|
|
||||||
|
租约请求为:
|
||||||
|
|
||||||
|
~~~json
|
||||||
|
{"wait_timeout_s": 0}
|
||||||
|
~~~
|
||||||
|
|
||||||
|
wait_timeout_s 范围 0–20。ACK 与 renew 请求为 {"attempt": 1}。事件请求示例:
|
||||||
|
|
||||||
|
~~~json
|
||||||
|
{
|
||||||
|
"attempt": 1,
|
||||||
|
"events": [
|
||||||
|
{
|
||||||
|
"message": "step started",
|
||||||
|
"severity": "INFO",
|
||||||
|
"payload": {}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
~~~
|
||||||
|
|
||||||
|
完成请求示例:
|
||||||
|
|
||||||
|
~~~json
|
||||||
|
{
|
||||||
|
"attempt": 1,
|
||||||
|
"status": "SUCCEEDED",
|
||||||
|
"exit_code": 0,
|
||||||
|
"error_class": null,
|
||||||
|
"result": {},
|
||||||
|
"log_uri": null
|
||||||
|
}
|
||||||
|
~~~
|
||||||
|
|
||||||
|
ACK、renew、events、complete 都必须带新的 UUID 格式 Idempotency-Key。相同操作重放返回当前投影
|
||||||
|
并标记 replayed=true;同一个 key 用于其他步骤或其他动作时返回 409。重新注册会轮换 token,使旧
|
||||||
|
token 立即失效。控制平面只保存 token 的 SHA-256 摘要。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 7. 三条不可破坏的架构红线
|
## 7. 三条不可破坏的架构红线
|
||||||
@@ -428,16 +770,24 @@ git push -u origin docs/ai-operating-guide
|
|||||||
| 资源 | 当前值 |
|
| 资源 | 当前值 |
|
||||||
|---|---|
|
|---|---|
|
||||||
| 云服务器 | 腾讯云 CVM,62.234.39.66 |
|
| 云服务器 | 腾讯云 CVM,62.234.39.66 |
|
||||||
|
| 操作系统 | Ubuntu 24.04 |
|
||||||
|
| DNS | DNSPod A 记录,TTL 600 |
|
||||||
| FlashOps 域名 | flashops.imagebrewing.com |
|
| FlashOps 域名 | flashops.imagebrewing.com |
|
||||||
| FlashOps 服务 | flashops.service |
|
| FlashOps 服务 | flashops.service |
|
||||||
|
| 当前公网 API | 0.1.0,仅模拟 Run,未发布 Agent 路由 |
|
||||||
|
| 应用用户 | ubuntu |
|
||||||
| 应用目录 | /data/wangzhan/app/storage-labos |
|
| 应用目录 | /data/wangzhan/app/storage-labos |
|
||||||
| 内部端口 | 127.0.0.1:18080 |
|
| 内部端口 | 127.0.0.1:18080 |
|
||||||
| 应用数据 | /data/wangzhan/app/storage-labos/flashops/var |
|
| 应用数据 | /data/wangzhan/app/storage-labos/flashops/var |
|
||||||
| 私有环境文件 | /etc/flashops/flashops.env |
|
| 私有环境文件 | /etc/flashops/flashops.env |
|
||||||
|
| FlashOps Nginx | /www/server/panel/vhost/nginx/flashops.imagebrewing.com.conf |
|
||||||
|
| FlashOps 证书 | /etc/letsencrypt/live/flashops.imagebrewing.com/ |
|
||||||
| Gitea 域名 | git.imagebrewing.com |
|
| Gitea 域名 | git.imagebrewing.com |
|
||||||
| Gitea 容器 | gitea,镜像版本固定为 1.27.0 |
|
| Gitea 容器 | gitea,镜像版本固定为 1.27.0 |
|
||||||
| Gitea 内部端口 | 127.0.0.1:13000 |
|
| Gitea 内部端口 | 127.0.0.1:13000 |
|
||||||
| Gitea 实时数据 | /data/gitea/data |
|
| Gitea 实时数据 | /data/gitea/data |
|
||||||
|
| Gitea Nginx | /www/server/panel/vhost/nginx/git.imagebrewing.com.conf |
|
||||||
|
| Gitea 证书 | /etc/letsencrypt/live/git.imagebrewing.com/ |
|
||||||
| COS 挂载 | /chucun |
|
| COS 挂载 | /chucun |
|
||||||
| Gitea 备份 | /chucun/wangzhan-production/backups/gitea/ |
|
| Gitea 备份 | /chucun/wangzhan-production/backups/gitea/ |
|
||||||
|
|
||||||
@@ -457,18 +807,228 @@ git push -u origin docs/ai-operating-guide
|
|||||||
FlashOps 当前是匿名预览环境。接入真实硬件或非公开数据前,必须先完成应用级 SSO / RBAC、
|
FlashOps 当前是匿名预览环境。接入真实硬件或非公开数据前,必须先完成应用级 SSO / RBAC、
|
||||||
审批、审计和命令模板签名;不能只依赖 Nginx Basic Auth。
|
审批、审计和命令模板签名;不能只依赖 Nginx Basic Auth。
|
||||||
|
|
||||||
|
### 11.1 FlashOps 服务管理与健康检查
|
||||||
|
|
||||||
|
只有获得服务器运维授权后才执行:
|
||||||
|
|
||||||
|
~~~bash
|
||||||
|
sudo systemctl status flashops --no-pager -l
|
||||||
|
sudo systemctl is-enabled flashops
|
||||||
|
curl -fsS http://127.0.0.1:18080/api/v1/health
|
||||||
|
curl -fsS https://flashops.imagebrewing.com/api/v1/health
|
||||||
|
~~~
|
||||||
|
|
||||||
|
需要重启时先确认没有不可中断的 Run,再执行:
|
||||||
|
|
||||||
|
~~~bash
|
||||||
|
sudo systemctl restart flashops
|
||||||
|
sudo systemctl status flashops --no-pager -l
|
||||||
|
curl -fsS http://127.0.0.1:18080/api/v1/health
|
||||||
|
~~~
|
||||||
|
|
||||||
|
服务必须只绑定 127.0.0.1:18080,不能把 Uvicorn 直接暴露公网。当前 SQLite 只允许运行一个
|
||||||
|
worker;增加 worker 前必须先迁移 PostgreSQL,并处理跨进程事件总线与锁。
|
||||||
|
|
||||||
|
关键环境变量:
|
||||||
|
|
||||||
|
| 变量 | 作用 |
|
||||||
|
|---|---|
|
||||||
|
| FLASHOPS_ENV | production 或 development |
|
||||||
|
| FLASHOPS_DATABASE_URL | 数据库 DSN |
|
||||||
|
| FLASHOPS_OBJECT_STORE_URL | 证据对象目录或对象存储 |
|
||||||
|
| FLASHOPS_BUS_URL | 外部事件总线,当前未启用 |
|
||||||
|
| FLASHOPS_AGENT_ENROLLMENT_TOKEN | Agent 首次注册口令 |
|
||||||
|
| FLASHOPS_API_HOST | API 监听地址 |
|
||||||
|
| FLASHOPS_API_PORT | API 端口 |
|
||||||
|
| FLASHOPS_CORS_ORIGINS | 允许的前端来源 |
|
||||||
|
| FLASHOPS_ENGINE_TICK_S | 引擎 tick |
|
||||||
|
| FLASHOPS_TIME_SCALE | 模拟时间倍率 |
|
||||||
|
|
||||||
|
生产秘密只放在 root-only、权限 0600 的 /etc/flashops/flashops.env。不得把值写进 systemd unit、
|
||||||
|
Nginx 注释、命令历史、聊天或 Git。
|
||||||
|
|
||||||
|
### 11.2 日志
|
||||||
|
|
||||||
|
~~~bash
|
||||||
|
sudo journalctl -u flashops -n 200 --no-pager
|
||||||
|
sudo journalctl -u flashops --since '30 minutes ago' --no-pager
|
||||||
|
sudo tail -n 100 /www/wwwlogs/flashops.imagebrewing.com.log
|
||||||
|
sudo tail -n 100 /www/wwwlogs/flashops.imagebrewing.com.error.log
|
||||||
|
~~~
|
||||||
|
|
||||||
|
持续跟踪日志只能用于短时诊断,结束后退出,避免长期占用会话。输出日志前先检查是否包含 token、
|
||||||
|
客户数据、设备序列号或其他敏感信息。
|
||||||
|
|
||||||
|
### 11.3 Nginx、DNS 与证书
|
||||||
|
|
||||||
|
任何配置修改都必须先测试,成功后才能 reload:
|
||||||
|
|
||||||
|
~~~bash
|
||||||
|
sudo nginx -t
|
||||||
|
sudo systemctl reload nginx
|
||||||
|
sudo certbot certificates
|
||||||
|
getent ahostsv4 flashops.imagebrewing.com
|
||||||
|
getent ahostsv4 git.imagebrewing.com
|
||||||
|
~~~
|
||||||
|
|
||||||
|
证书模拟续期:
|
||||||
|
|
||||||
|
~~~bash
|
||||||
|
sudo certbot renew --dry-run --non-interactive --no-random-sleep-on-renew
|
||||||
|
~~~
|
||||||
|
|
||||||
|
两个域名应指向 62.234.39.66;HTTP 应 301 到 HTTPS;证书 SAN 必须匹配访问域名。ACME challenge
|
||||||
|
路径必须保持可达。证书续期后由 deploy hook reload Nginx。
|
||||||
|
|
||||||
|
FlashOps 当前配置不应包含 auth_basic、allow 或 deny 访问限制。若匿名访问出现 401/403,先检查
|
||||||
|
浏览器旧认证缓存、Nginx 生效配置和上游安全产品;Agent API 因 token 无效返回 401 是另一回事。
|
||||||
|
|
||||||
|
### 11.4 标准发布流程
|
||||||
|
|
||||||
|
1. 确认目标 Git 提交、工作树和本次发布范围;
|
||||||
|
2. 在本地执行 make test 与 make demo;
|
||||||
|
3. 检查暂存和发布内容不含 .venv、var、缓存、凭据和运行产物;
|
||||||
|
4. 使用 SQLite 在线备份 API 备份当前生产数据库;
|
||||||
|
5. 保存当前可运行发布包、SHA-256 和回滚说明;
|
||||||
|
6. 将新版本上传到独立 incoming 目录,不覆盖正在运行的目录;
|
||||||
|
7. 校验上传包 SHA-256,安装或复用服务器虚拟环境;
|
||||||
|
8. 保留生产 flashops/var,绝不能用本地空 var 覆盖;
|
||||||
|
9. 校验 systemd unit 和私有环境文件权限;
|
||||||
|
10. 切换版本并重启 flashops;
|
||||||
|
11. 轮询 127.0.0.1:18080/api/v1/health;
|
||||||
|
12. 执行 nginx -t,必要时 reload;
|
||||||
|
13. 从服务器内部和外部各验证一次 HTTPS、最终跳转、health 和 dashboard JSON;
|
||||||
|
14. 检查最近日志无新 error,并记录提交号、时间、备份和回滚点。
|
||||||
|
|
||||||
|
不要在生产目录直接手工改源码。发布物必须能映射到 Git 提交。发布失败时优先切回已经验证的旧发布
|
||||||
|
目录,并保留失败目录和日志用于排查,不能边修边覆盖证据。
|
||||||
|
|
||||||
|
### 11.5 SQLite 备份与恢复边界
|
||||||
|
|
||||||
|
运行中的 SQLite 应使用在线备份 API,不要直接复制可能正在写入的数据库:
|
||||||
|
|
||||||
|
~~~bash
|
||||||
|
sudo /data/wangzhan/app/storage-labos/flashops/.venv/bin/python - <<'PY'
|
||||||
|
import datetime
|
||||||
|
import sqlite3
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
source = Path('/data/wangzhan/app/storage-labos/flashops/var/flashops.db')
|
||||||
|
stamp = datetime.datetime.now().strftime('%Y%m%d-%H%M%S')
|
||||||
|
target = Path('/data/wangzhan/backups') / ('flashops-db-' + stamp + '.db')
|
||||||
|
target.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
with sqlite3.connect(source) as src, sqlite3.connect(target) as dst:
|
||||||
|
src.backup(dst)
|
||||||
|
print(target)
|
||||||
|
PY
|
||||||
|
~~~
|
||||||
|
|
||||||
|
备份后记录文件大小和 SHA-256。数据库恢复是高影响动作,本文不授权 AI 自动执行。获明确授权后也要:
|
||||||
|
|
||||||
|
1. 停止 FlashOps;
|
||||||
|
2. 核对恢复目标、备份时间和校验值;
|
||||||
|
3. 再备份当前数据库;
|
||||||
|
4. 替换后修复 ubuntu:ubuntu 所有权和文件权限;
|
||||||
|
5. 启动服务;
|
||||||
|
6. 验证 health、表数量、最新 Run 和事件 seq 连续性;
|
||||||
|
7. 保留恢复前数据库,直到验收完成。
|
||||||
|
|
||||||
|
### 11.6 Gitea 运维与 COS 备份
|
||||||
|
|
||||||
|
Gitea 公开仓库允许匿名查看和 HTTPS 克隆;账号注册后需要管理员人工批准。当前不开放独立 Git SSH
|
||||||
|
端口,也没有生产 Actions Runner。
|
||||||
|
|
||||||
|
获授权后的只读检查:
|
||||||
|
|
||||||
|
~~~bash
|
||||||
|
sudo docker ps --filter name=^gitea$
|
||||||
|
sudo docker logs --tail 100 gitea
|
||||||
|
curl -fsS http://127.0.0.1:13000/api/healthz
|
||||||
|
sudo systemctl status flashops-gitea-backup.timer --no-pager
|
||||||
|
sudo systemctl list-timers flashops-gitea-backup.timer --no-pager
|
||||||
|
~~~
|
||||||
|
|
||||||
|
手工触发一次可恢复的 Gitea dump:
|
||||||
|
|
||||||
|
~~~bash
|
||||||
|
sudo systemctl start flashops-gitea-backup.service
|
||||||
|
sudo systemctl status flashops-gitea-backup.service --no-pager
|
||||||
|
find /chucun/wangzhan-production/backups/gitea -maxdepth 1 -type f \
|
||||||
|
-name 'gitea-*.zip' -ls
|
||||||
|
~~~
|
||||||
|
|
||||||
|
备份脚本使用 Gitea 自身 dump,生成 ZIP 和 SHA-256 sidecar,成功复制到 COS 后清理本机临时包。
|
||||||
|
定时器每日约 03:20 运行并带随机延迟。实时仓库、Git 对象和 Gitea SQLite 必须留在
|
||||||
|
/data/gitea/data 本地文件系统;COSFS 不提供数据库依赖的完整锁与原子语义,只能用于备份。
|
||||||
|
|
||||||
|
Gitea 恢复或升级是高影响操作:必须先验证备份、阅读目标版本迁移说明、安排维护窗口,并保留旧容器
|
||||||
|
镜像和 /data/gitea/data 快照。镜像必须固定版本,不能使用 latest。
|
||||||
|
|
||||||
|
### 11.7 故障定位
|
||||||
|
|
||||||
|
公网 502:
|
||||||
|
|
||||||
|
~~~bash
|
||||||
|
sudo systemctl status flashops --no-pager -l
|
||||||
|
sudo journalctl -u flashops -n 200 --no-pager
|
||||||
|
curl -v http://127.0.0.1:18080/api/v1/health
|
||||||
|
sudo nginx -t
|
||||||
|
sudo tail -n 100 /www/wwwlogs/flashops.imagebrewing.com.error.log
|
||||||
|
~~~
|
||||||
|
|
||||||
|
常见原因是服务未启动、Python 依赖错误、18080 端口不一致、var 不可写或 systemd 安全策略阻止路径。
|
||||||
|
|
||||||
|
SQLite locked:
|
||||||
|
|
||||||
|
- 确认只有一个 Uvicorn worker;
|
||||||
|
- 检查是否有脚本绕过 session 直接写数据库;
|
||||||
|
- 所有状态写入应走同一事务和事件入口;
|
||||||
|
- 需要多进程时迁移 PostgreSQL,不能继续增加 SQLite 锁重试掩盖问题。
|
||||||
|
|
||||||
|
Gitea 页面不可访问:
|
||||||
|
|
||||||
|
~~~bash
|
||||||
|
sudo docker ps --filter name=^gitea$
|
||||||
|
sudo docker logs --tail 100 gitea
|
||||||
|
curl -fsS http://127.0.0.1:13000/api/healthz
|
||||||
|
getent ahostsv4 git.imagebrewing.com
|
||||||
|
sudo nginx -t
|
||||||
|
~~~
|
||||||
|
|
||||||
|
HTTPS 证书异常时核对 DNS、证书路径、证书域名、有效期和 Nginx 是否已 reload。不要在原因未确认时
|
||||||
|
重复签发证书或删除现有证书目录。
|
||||||
|
|
||||||
|
### 11.8 生产验收清单
|
||||||
|
|
||||||
|
- [ ] flashops.service 为 active;
|
||||||
|
- [ ] Gitea 容器为 running,loopback health 为 pass;
|
||||||
|
- [ ] Nginx active 且 nginx -t 成功;
|
||||||
|
- [ ] 两个域名解析到 62.234.39.66;
|
||||||
|
- [ ] HTTP 301 到 HTTPS;
|
||||||
|
- [ ] 两张证书域名正确、未过期、自动续期可用;
|
||||||
|
- [ ] 未登录访问 FlashOps 控制台最终返回 200;
|
||||||
|
- [ ] FlashOps health 返回 status=ok;
|
||||||
|
- [ ] dashboard summary 返回合法 JSON;
|
||||||
|
- [ ] 未登录可以查看并克隆 public Git 仓库;
|
||||||
|
- [ ] 生产 var 数据可写,但应用源码目录不被随意写入;
|
||||||
|
- [ ] 最新日志没有新增 error;
|
||||||
|
- [ ] FlashOps 数据库备份有 SHA-256;
|
||||||
|
- [ ] Gitea COS dump 有 ZIP 和有效 SHA-256 sidecar;
|
||||||
|
- [ ] 发布提交号、时间和回滚点已经记录。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 12. 常见任务剧本
|
## 12. 常见任务剧本
|
||||||
|
|
||||||
### 12.1 “看看项目并继续完善”
|
### 12.1 “看看项目并继续完善”
|
||||||
|
|
||||||
1. 阅读本文件、README 和相关源码;
|
1. 以本文的能力边界、状态机和安全规则建立基线;
|
||||||
2. 运行测试与演示建立基线;
|
2. 若获得仓库,检查工作树、源码和测试;只有本文时先做线上只读核验;
|
||||||
3. 区分已实现、部分实现和待实现;
|
3. 运行可用的测试与模拟演示;
|
||||||
4. 优先选择一个能独立验收的纵向切片;
|
4. 区分已实现、部分实现和待实现;
|
||||||
5. 实现、补测试、同步文档;
|
5. 优先选择一个能独立验收的纵向切片;
|
||||||
6. 报告实际证据,不把规划写成完成。
|
6. 实现、补测试、同步本文;
|
||||||
|
7. 报告实际证据,不把规划写成完成。
|
||||||
|
|
||||||
### 12.2 “排查网站打不开”
|
### 12.2 “排查网站打不开”
|
||||||
|
|
||||||
@@ -480,12 +1040,12 @@ FlashOps 当前是匿名预览环境。接入真实硬件或非公开数据前
|
|||||||
|
|
||||||
### 12.3 “修改 API”
|
### 12.3 “修改 API”
|
||||||
|
|
||||||
1. 阅读路由、schema、service 和相关测试;
|
1. 只有获得仓库后才能实施修改;先核对路由、schema、service 和测试;
|
||||||
2. 保持业务逻辑不堆在 route;
|
2. 保持业务逻辑不堆在 route;
|
||||||
3. 状态写入走事件记录;
|
3. 状态写入走事件记录;
|
||||||
4. 为正常与错误路径增加测试;
|
4. 为正常与错误路径增加测试;
|
||||||
5. 检查 OpenAPI;
|
5. 检查 OpenAPI;
|
||||||
6. 更新 ProjectManual 和本文件中的接口索引。
|
6. 更新本文中的接口、状态码、权限和部署说明。
|
||||||
|
|
||||||
### 12.4 “增加真实硬件能力”
|
### 12.4 “增加真实硬件能力”
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user