# 参考：OpenClaw 企业部署的运维监控方案

> **类型**：外部参考 / 同组织友邻产品的成熟做法
> **来源**：`FFAIWorkspace/openclaw` 仓库 `docs/enterprise-plan/` + `extensions/`（main @ 拉取于 2026-05-16）
> **用途**：为 [issue #311 — OOM / 内存监控 + Discord 告警基建](http://43.130.59.228/FFAIWorkspace/workspace/issues/311) 提供方案参考
> **不是**：FFAI Workspace 自身的部署方案（FFAI 自己怎么做请见 issue #311 决策后产出的实施文档）

---

## 为什么记录这个

issue #311 要给 FFAI Workspace（5 台应用服务器 + Gitea + Runner + 备份机）补一套监控告警基建。
组织内 OpenClaw 产品在企业部署（FutureClaw / AIxClaw）里已经跑了一套同类方案——
**复用其设计可以避开他们已经踩过的坑**（告警字段 camelCase、template 不写中文、cost 不能复用 provider 字段、host.docker.internal 跨 compose 抓取等）。

记录到这里只是参考材料；FFAI 自己的方案要不要照抄、抄到什么粒度，由 #311 决策。

## OpenClaw 方案一句话总结

**Prometheus + Grafana + cadvisor + node_exporter，单机栈，每个环境独立部署，Teams Webhook 告警；业务指标走 OTel Collector 中转。**

- 零软件成本
- 总内存预算 ~330 MiB（不含 OTel collector），可接受
- 公网访问走 Nginx 反代到 `:8443/grafana/` 子路径 + 云安全组 IP 白名单

## 架构分三步走

### Step 1 — 资源监控（4 容器，~330 MiB）

```
cadvisor ───────┐
                 ├─→ Prometheus（30d retention）─→ Grafana ──→ Teams Webhook
node_exporter ──┘
```

| 容器 | 版本 | 内存 | 作用 |
|---|---|---|---|
| Prometheus | v3.3.0 | ~100 MiB | 抓取 + TSDB |
| Grafana | 11.6.1 | ~70 MiB | Dashboard + alert engine |
| cadvisor | gcr v0.55.1 | ~150 MiB | 每容器 CPU/内存/网络 |
| node_exporter | v1.8.2 | ~10 MiB | 宿主 CPU/内存/磁盘 |

所有端口绑 `127.0.0.1`（不暴露公网），外部访问走 Nginx。

### Step 2 — 业务指标（+1 容器，~32 MiB）

```
App 进程内 diagnostics 埋点
  └─ diagnostics-otel 扩展（OTLP/HTTP push）
       └─ OTel Collector（OTLP receiver → Prometheus exporter:9464）
            └─ Prometheus scrape job → Grafana
```

只改配置不改应用代码（前提是应用已有 `diagnostics` 埋点）。

### Step 3 — 自研维度补强（sidecar 或 plugin）

OTel 抓不到的"按用户/按 channel"维度，他们写了：

1. **usage-stats-exporter**（独立 Node sidecar，~150 行）
   - 60s 周期扫 `sessions.json` + `*.jsonl`
   - 按 `(env, agent, channel, user, model)` 5 维聚合
   - 暴露 `/metrics`（Prometheus 文本格式）+ `/healthz`
   - 故意选 **gauge** 不选 counter——重启不丢"当前快照"

2. **meeting-minutes-stats**（OpenClaw plugin，跑在 gateway 进程内）
   - 复用 diagnostics-otel pipeline，不新增容器
   - 5 个 gauge metric，60s ticker

## 告警通道与规则

| 告警 | 阈值 | 持续 |
|---|---|---|
| 主机内存 | >80% | 5min |
| 磁盘 | >85% | 5min |
| Gateway 容器宕机 | 消失 | 2min |
| 单容器内存（关键容器）| >1 GiB | 5min |
| 容器总数 | test>20 / prod>50 | 5min |

通知渠道：**Teams Incoming Webhook**，中文消息模板。

> #311 计划用 **Discord webhook**（FFAI 项目已有 Discord MCP 通道）——告警渠道替换即可，规则可以照抄。

## 配置踩坑（OpenClaw 已沉淀的，#311 实施时可以避开）

事实源：OpenClaw 仓库 `docs/enterprise-plan/solution/operations/monitoring-plan.md` §「告警配置注意事项」（外部仓库，无本仓库内链接）

1. **Grafana 告警规则必须用 camelCase 字段名**（`noDataState` / `execErrState`），不是 snake_case
2. **每条规则必须有 reduce step**（A: 查询 → B: reduce last → C: threshold），少一步会"查到了但不告警"
3. **通知模板里只用 `{{ .Labels.xxx }}` / `{{ .Annotations.xxx }}`**，不用 `{{ if }}` 条件判断（中文翻译写在 labels 里如 `severity: ⚠️ 警告`，不写在模板）
4. **cost 指标不复用 provider 自带的 `usage.cost`**，依赖手工维护的价格表——换模型要同步改
5. **多环境共用一套监控栈时，跨 compose 网络抓不到**——把要被抓的 exporter 端口映射到宿主，Prometheus 用 `host.docker.internal:<port>` 抓，每个环境一个 job + 加 `environment=xxx` label 区分

## 公网访问模式（如果走公网）

OpenClaw 的做法（同样可借鉴）：

```
公网 :8443/grafana/  →  Nginx 反代  →  127.0.0.1:3000 (Grafana)
```

- Grafana 端口本地监听，不直接暴露
- 端口 `:8443` 自身的 ACL 走**云安全组 / 上游防火墙 IP 白名单**，Nginx 层不做（运维负担转移到云控制台）
- 当前用 admin 本地账号；规划对接 Azure AD SSO

## 适用性评估（给 #311 当参考）

| 维度 | OpenClaw 方案 | FFAI #311 场景 | 评估 |
|---|---|---|---|
| 监控对象 | 同机容器 + 宿主 | 跨 5 台 + Gitea + Runner + 备份机 | ⚠️ 需要拆"中心化采集"还是"每机本地一套"——OpenClaw 单机模式直接抄不通 |
| 部署位置 | 跟应用同机 | issue #311 要求"不能跟 Gitea 同机"（1.9GB 容不下）| ⚠️ 部署位置需独立决策 |
| 告警通道 | Teams Webhook | Discord Webhook | ✅ 仅替换 webhook URL + 消息模板 |
| 资源预算 | ~330 MiB | 同量级可接受 | ✅ |
| 业务可用性 | OTel + 自研 sidecar | 接 `/api/v1/health/detailed`（PR #306）| ✅ 思路一致——sidecar 暴露 `/metrics` 给 Prometheus 抓 |
| OOM killer / PM2 crash 监控 | 未明确覆盖 | issue #311 明确要求 | ❌ OpenClaw 方案不含，#311 需额外加 textfile collector 或 systemd-exporter |

## 不直接照抄的点

- **跨机采集**：OpenClaw 是单机栈，FFAI 是 5+ 机器，必须有一台"中心 Prometheus"通过 `scrape_configs` 跨网拉，或每机本地一套 + 远程读
- **OOM killer 触发**：node_exporter 的 `node_vmstat_oom_kill` 默认开，但需要 alert rule（OpenClaw 文档没列）
- **PM2 进程级**：cadvisor 看容器，FFAI 直接跑 PM2 在宿主上，需要 `process-exporter` 或 PM2 metrics
- **Gitea 1.9G 内存限制**：监控栈不能放在 Gitea 主机；候选位置是 dedicated-runner-1 或独立监控机
- **Discord 消息渲染**：Grafana 没有原生 Discord contact point，要么用 Webhook 配 Discord-compatible 模板，要么走 alertmanager + custom receiver

## 参考文件清单（OpenClaw 仓库）

- 整体方案：`docs/enterprise-plan/solution/operations/monitoring-plan.md`
- 部署资产：`docs/enterprise-plan/deployment-assets/monitoring/`
  - `docker-compose.monitoring.yml`
  - `prometheus.yml`
  - `otel-collector.yaml`
  - `grafana/provisioning/` + `grafana/dashboards/`
- 自研 exporter：`docs/enterprise-plan/deployment-assets/monitoring/usage-stats-exporter/exporter.mjs`（~300 行，Node 原生）
- OTel 扩展：`extensions/diagnostics-otel/`（仅 15 行 plugin 注册）
- Plugin 形态示例：`extensions/meeting-minutes-stats/`（OTel pipeline 复用）
