- 即时通讯
- 后端
【免费下载链接】WuKongIM
More than just IM 不只是即时通讯(IM)
本文基于已接受的架构决策 ADR 0022(Aggregate live observability without a log stack),讲解 WuKongIM 云端仿真(Cloud Simulation)场景下的实时可观测性方案:由仿真器(simulator)统一托管 Prometheus 抓取集群与宿主机指标,结构化应用日志保持在各源节点上做有界轮转,再由 Analysis MCP 把 Prometheus 查询与既有私有日志、诊断、任务审计、workqueue、profiling 等面聚合成一个受控的实时诊断入口。读完本文,你将理解这一方案中"不引入 Grafana / Loki / ELK / 跨 Run 存储"的边界划分、各观测面的源码级实现位置,以及 metrics 保留期、日志配额与磁盘预检如何按"工作负载时长 + Analysis Grace"进行容量设定。
一、ADR 0022 的核心决策与适用边界
docs/adr/0022-aggregate-live-observability-without-a-log-stack.md 状态为accepted,其决策要点可以归纳为五条:
- 仿真器托管 Prometheus:为 WuKongIM 集群节点提供 host metrics 的抓取与保留;
- 应用日志留在源节点:结构化应用日志保持有界(bounded)并在其产生节点上轮转(rotated),不集中采集;
- Analysis MCP 做聚合者:它聚合 Prometheus 查询与既有的私有 manager log、diagnostics、task-audit、workqueue、profiling 等多个观测面;
- 仿真器服务日志走本地 journald:Analysis MCP 从仿真器本地的 journald 查询其自身托管服务的日志;
- Provisioning Workflow 自留引导失败:预置阶段的 bootstrap 失败记录保留在工作流自身的 summary 中,不进入观测面。
同时该 ADR 划定了明确的不做清单:第一版本不部署 Grafana、Loki、ELK,也不做跨 Run(cross-run)的观测数据存储;metrics 保留期、日志配额和磁盘预检均按"工作负载时长 + Analysis Grace"来设定尺寸。
这条决策并非孤立存在,它依赖一组前置决策共同成立:
- ADR 0014 决定云主机上的 WuKongIM、wkbench、Analysis MCP、Prometheus、host-observation 二进制全部以原生 systemd 服务方式运行,并内建 journald 集成(见 docs/adr/0014-run-cloud-services-as-native-systemd-units.md);正是"服务已原生接入 journald"这一点,让第 4 条决策可以用
journalctl直接查本地日志,而无需额外的日志采集器。 - ADR 0021 把"工作负载时长"与"分析宽限期"分离:可选的
30m/2h/24h/48h描述的是就绪后的有效负载时间(默认2h),而 Analysis Grace 为30m/1h/6h(默认30m),用于在负载停止后继续保留存活指标、日志、MCP 与集群供人工诊断(见 docs/adr/0021-separate-workload-duration-from-analysis-grace.md)。这正是 0022 中"按 workload duration + Analysis Grace 设定保留"的时间基准来源。 - ADR 0017 与 ADR 0028 分别从"限制活动诊断"和"暴露窄而受界的 Run 作用域工具契约"两个角度约束了聚合面本身的行为边界,0022 的聚合入口必须服从这些约束。
值得注意的一点是:仓库中docker/observability/下确实存在 Grafana 仪表盘与数据源 provisioning 文件,但它们服务于本地开发参考拓扑(Docker Compose 场景),与 ADR 0022 所指的"云仿真第一版本不部署 Grafana"并不矛盾——0014 中已说明 Docker Compose 保留为本地开发与拓扑参考。
二、Prometheus 面:仿真器托管的指标保留与抓取
2.1 本地参考配置:抓取三节点与告警规则
仓库中 docker/observability/prometheus/prometheus.yml 展示了 Prometheus 的基础配置形态:
global: scrape_interval: 15s evaluation_interval: 15s rule_files: - alerts/*.yml scrape_configs: - job_name: wukongim metrics_path: /metrics static_configs: - targets: - wk-node1:5001 - wk-node2:5001 - wk-node3:5001 labels: cluster: local-dev三个 WuKongIM 节点统一暴露在:5001/metrics,抓取间隔 15 秒,并按cluster与节点维度打标签。配套告警规则位于 docker/observability/prometheus/alerts/wukongim-channel-runtime.yml,定义了三级 channel 运行时容量告警:
| 告警名 | 触发条件 | for时长 | 级别 |
|---|---|---|---|
WKChannelRuntimeCapacityHigh | 节点wukongim_channel_active_channels占wukongim_channel_max_channels比例 > 0.8 | 10m | warning |
WKChannelRuntimeCapacityCritical | 同上比例 > 0.9 | 5m | critical |
WKChannelRuntimeActivationRejected | 5 分钟内出现wukongim_channel_activation_rejected_total{reason="too_many_channels"}增量 > 0 | 2m | warning |
这类容量告警与 ADR 0046(将资源饱和报告为容量告警而非故障)的思路一致:高利用率是有效观测信号,用于分析归因而不是直接判负。
2.2 云端部署形态:systemd 单元与保留参数
云端场景中,Prometheus 的 systemd 单元由部署模板生成。internal/usecase/clouddeploy/templates.go 中的渲染结果为:
/opt/wukongim/bin/prometheus --config.file=/etc/wukongim/prometheus.yml \ --storage.tsdb.path=/var/lib/wukongim-cloud/prometheus \ --storage.tsdb.retention.time=96h \ --storage.tsdb.retention.size=150GB即 TSDB 数据落在/var/lib/wukongim-cloud/prometheus,时间保留 96 小时、大小上限 150GB。另一套云仿真模板 internal/infra/cloudsim/deploy/templates.go 则把--storage.tsdb.retention.time参数化(%s占位由场景时长推导),并在启动参数中附带--web.external-url与--web.route-prefix=/,从源码结构看这是为了配合公共查看入口(public view)的 URL 前缀路由。
部署包渲染入口在 internal/infra/cloudsim/deploy/bundle.go 的Render方法:它校验静态二进制(wukongim、wkcli、wkanalysis、prometheus、node_exporter,启用 public view 时再加wkcloudview)齐备后,写出确定性的角色配置、config/prometheus.yml、config/target.yaml、worker 配置以及一组 systemd 单元,其中还包括scripts/wukongim-cgroup-metrics.sh这个 cgroup 指标采集脚本——与 0014 中"host-observation 二进制以原生服务运行"的描述吻合。宿主机指标侧则由node_exporter提供,其单元在 internal/usecase/clouddeploy/templates.go 中监听0.0.0.0:9100并启用 textfile collector 目录。
设计与实现的一致性还可从internal/usecase/clouddeploy/bundle_test.go中确认:测试显式断言了systemd/prometheus.service、systemd/node-exporter.service、systemd/wkanalysis.service、systemd/wkbench-host-metrics.service等单元的存在(见 internal/usecase/clouddeploy/bundle_test.go)。
三、日志策略:源节点有界轮转 + 本地 journald
3.1 应用日志:zap + lumberjack 的有界轮转
ADR 0022 的第一条实现落点是"结构化应用日志在源节点保持有界并轮转"。WuKongIM 的日志架构设计见 docs/raw/logging-architecture.md:日志在应用入口(internal/app/build.go)一处初始化,支持按级别分文件(INFO 主日志 / ERROR 错误日志 / DEBUG 调试日志),底层基于 zap + lumberjack。
轮转参数默认值在 internal/log/config.go 中定义并做了归一化:
| 参数 | 默认值 | 含义 |
|---|---|---|
MaxSize | 100 | 单个日志文件达到多少 MB 后轮转 |
MaxAge | 30 | 轮转文件最多保留多少天 |
MaxBackups | 10 | 每类日志最多保留多少个轮转文件 |
写出器封装在 internal/log/writer.go,直接构造lumberjack.Logger并传入上述三项。接口层定义在pkg/wklog/(Logger接口、Field类型、NopLogger),业务包如pkg/cluster、pkg/slot只依赖接口而非 zap 实现。这套"接口层 + 实现层"的分层,使得各节点上日志天然只落本地磁盘、尺寸有界,无需任何集中式日志管道——这正是 0022 决策得以成立的前提。
3.2 任务审计与诊断事件:内存中的配额保留
"有界"不只体现在文件日志上。Controller 的任务审计历史由internal/observability/taskaudit包实现,其保留策略在 internal/observability/taskaudit/retention.go 中:
enforceEventRetentionLocked:当单任务事件数超过MaxEventsPerTask时,按事件时间升序丢弃最旧的事件,并把对应快照标记Truncated = true;enforceTaskRetentionLocked:当快照总数超过MaxTasks时,按LastAppliedRaftIndex(相同则按 TaskID)排序,整体淘汰最旧的任务及其全部事件。
即每一类审计数据都带有明确的"每任务事件上限 + 全局任务数上限"双配额。诊断事件侧的internal/observability/diagnostics包则提供事件存储、索引与采样器(store.go、index.go、sampler.go),共同构成 Analysis MCPdiagnostics_query工具背后的"bounded retained"数据源。
3.3 仿真器服务日志与引导失败的归属
ADR 0022 还划分了两类"非应用日志"的归属:
- 仿真器自身托管的服务(wkbench、Analysis MCP、Prometheus 等 systemd 单元):其日志通过 systemd 集成进入仿真器本机 journald,Analysis MCP 直接从本地 journald 查询。由于这些服务都跑在仿真器一台主机上,"本地 journald 查询"不需要任何跨主机日志网络。
- Provisioning(预置)阶段的 bootstrap 失败:保留在 Provisioning Workflow 自身的 summary 中。这意味着"环境没起来"这一类失败不需要进入观测面就能被定位——观测面只服务"环境已起来之后的负载诊断",这与 ADR 0047(跨部署修复保留同一个 lease)中"引导/部署失败先于负载诊断"的阶段划分相互衔接。
这一设计在 docs/superpowers/specs/2026-07-14-cloud-simulation-github-actions-design.md 的 "Live Observability Plane" 一节中有对应的成文描述,与本 ADR 一一对应:仿真器托管 Prometheus 并抓取全部三个 WuKongIM 节点加四台宿主机指标导出器;WuKongIM 结构化日志在原节点轮转;Run 释放后不再支持进一步诊断。
四、聚合入口:Analysis MCP 的受界工具面
ADR 0022 的中心动作是"Analysis MCP 聚合多个观测面"。其 HTTP 端点实现于 internal/access/cloudanalysismcp/handler.go:以 run-scoped bearer token(≥32 字节随机凭据、带过期时间)做认证,绑定唯一的 Run Identity,并通过严格工具白名单注册工具。工具清单(与 ADR 0028 的窄契约一致)为:
| 工具 | 作用 | 标注 |
|---|---|---|
run_inspect | 证明精确的 Run 身份与当前 live / released 清单状态 | 只读 |
cluster_snapshot | 读取有界的集群节点与 workqueue 聚合快照 | 只读 |
workload_inspect | 运行中读有界 worker 连接诊断;完成后读最终 wkbench 汇总、阶段窗口与结构化失败 | 只读 |
metrics_query_range | 按服务器白名单的query_id查询一个低基数 Prometheus 信号的时间范围 | 只读 |
logs_search/logs_context | 在单个白名单集群节点上检索有界应用日志 / 围绕不透明游标读一页 | 只读 |
diagnostics_query/task_audits_query | 查询有界保留的诊断事件 / Controller 任务审计 | 只读 |
trace_start/trace_query | 安装一条会过期的单节点诊断跟踪规则(TTL 1–900s)/ 读取某条精确 trace 的有界事件 | 活动 |
profile_capture/profile_top/profile_list | 在单个白名单节点抓一次有界 CPU/heap/goroutine profile / 符号化 top 行摘要 / 列出 gateway 拥有的 profile 元数据 | 活动/只读 |
config_read_redacted | 读取单节点白名单且已脱敏的生效配置 | 只读 |
其中与 0022 直接相关的两个细节:
metrics_query_range不暴露任意 PromQL:请求体(见 internal/access/cloudanalysismcp/handler.go)要求提供服务器白名单标识符query_id(如投递管道的delivery_recipient_authority_resolve_rate及其有界的 item / target / group / shard / rejected / rollback / P99 伴随指标),外加 RFC3339 起止时间与step_seconds。也就是说"聚合 Prometheus 查询"落在服务器侧预定义的低基数信号集上,调用方无法构造任意查询——这与 ADR 0017 "在 Analysis MCP 内限制活动诊断" 的精神一致。- 每个 Observation 都带统一信封:
RunID、Node、Source、ObservedAt、Window、Completeness、Warnings、Data(见 internal/access/cloudanalysismcp/handler.go)。这保证了无论数据来自 Prometheus、应用日志、journald、诊断事件还是 task-audit,聚合结果都携带来源、时间窗与完整性标注,Agent 可以判断"这个证据是全量还是被截断的"。
需要区分的是,仓库里另有一套本地运维用的 Ops MCP(pkg/metrics/opsmcp.go 为其提供低基数请求/并发指标,如wukongim_ops_mcp_requests_total、wukongim_ops_mcp_request_duration_seconds),其工具集(cluster_health、slot_inspect、pprof_analyze等)与云端 Analysis MCP 不同。从源码结构看,两者是同一"受界 MCP 工具 + 结果指标化"模式在本地运维与云端仿真两个入口上的分别落地。
五、容量设定:保留期、日志配额与磁盘预检
ADR 0022 的最后一句规定了尺寸设定的方法:metrics 保留期、日志配额与磁盘预检按"工作负载时长 + Analysis Grace"取定。结合仓库证据可以把它拆开看:
- metrics 保留期。Prometheus TSDB 的
retention.time由场景推导(internal/infra/cloudsim/deploy/templates.go 中参数化渲染),云端部署模板中另设置了retention.size=150GB的硬性大小上限兜底。结合 ADR 0021 的时长档位(最长 48h 负载 + 最长 6h Grace,另有有界的 bootstrap 允许时间),可以推断保留窗口需覆盖最坏情况下的完整 Run 区间,同时受大小上限约束;仓库中 96h 的固定值出现在另一部署模板(internal/usecase/clouddeploy/templates.go),对应 chat-lifecycle 流的 96 小时正式 Cloud Lease 上限(ADR 0041)。 - 日志配额。即第三节所述的有界机制:文件日志由
MaxSize/MaxAge/MaxBackups限定(默认 100MB / 30 天 / 10 份),task-audit 由MaxEventsPerTask/MaxTasks双配额限定,诊断事件由diagnostics包的有界存储与采样器限定。 - 磁盘预检。部署侧把磁盘健康作为一等失败类别:internal/usecase/clouddeploy/deployment.go 定义了
MinimumSystemDiskBytes = 40_000_000_000(40GB 系统盘下限),并定义了FailureDiskMount(data_disk_mount_invalid)、FailureDiskCapacity(filesystem_capacity_insufficient)、FailureDiskFree(filesystem_free_space_low)三个磁盘预检失败码;实例规划阶段还会校验每台主机恰好挂载一块与角色匹配的数据盘(见 internal/usecase/clouddeploy/deployment.go)。从源码结构看,这些检查在部署阶段先行拦截"磁盘不够放 Prometheus TSDB 与轮转日志"的情形,使保留期与日志配额不会在运行中被动击穿。
六、小结:为什么这套方案"够用且便宜"
把 ADR 0022 放回整个决策族中看,它的取舍逻辑非常清晰:
- 观测需求是"单次 Run 内 + Grace 期内的实时诊断",而非长期历史分析,因此不需要 Loki / ELK 这类集中日志栈,也不需要跨 Run 存储——Run 释放后不再支持进一步诊断,证据的"保鲜期"天然有界;
- 基础设施全部原生 systemd + journald(ADR 0014),使"查服务日志"退化为仿真器本机的
journalctl查询,省掉一个采集器组件、一条传输链路和一份部署依赖; - 聚合智能放在 Analysis MCP 的窄工具面上:Prometheus 侧只有服务器白名单查询,日志侧只有单节点有界检索,诊断/审计侧只有有界快照,所有 Observation 带完整性与来源标注——聚合的复杂度被封装在服务端,调用方(含 LLM Agent)拿到的始终是受约束、可核验的证据;
- 容量维度被显式管理:保留期按负载时长 + Grace 推导、日志按配额轮转、磁盘在部署预检中先验拦截,三者共同保证"无集中日志栈"不会以失控的本地磁盘为代价。
对读者而言,如果要在本仓库中继续深入,建议的路径是:先读 docs/adr/0022-aggregate-live-observability-without-a-log-stack.md 及其直接依赖的 docs/adr/0014-run-cloud-services-as-native-systemd-units.md、docs/adr/0021-separate-workload-duration-from-analysis-grace.md、docs/adr/0017-bound-active-diagnostics-in-the-analysis-mcp.md、docs/adr/0028-expose-a-narrow-run-scoped-mcp-tool-contract.md,再看internal/access/cloudanalysismcp/、internal/observability/、internal/usecase/clouddeploy/与docker/observability/中对应的实现、模板与配置,即可完整还原这一观测面从部署渲染到诊断查询的全链路。
- 即时通讯
- 后端
【免费下载链接】WuKongIM
More than just IM 不只是即时通讯(IM)
相关推荐
WuKongIM 云仿真诊断架构解析:Analysis MCP 与 Analysis Skill 的职责分离设计
WuKongIM 云仿真诊断架构解析:Analysis MCP 与 Analysis Skill 的职责分离设计 云仿真压测(Cloud Simulation)
即时通讯后端WuKongIM 云端仿真 Analysis MCP:run-scoped 窄工具契约的设计与实现
WuKongIM 云端仿真 Analysis MCP:run scoped 窄工具契约的设计与实现 导读 本文围绕 WuKongIM 仓库中已接受的架构决策记录
即时通讯后端WuKongIM 云仿真 Analysis Access Window:MCP 分析网关的按需临时入口设计
WuKongIM 云仿真 Analysis Access Window:MCP 分析网关的按需临时入口设计 本文聚焦 WuKongIM 云仿真(Cloud Si
即时通讯后端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考