乐于分享
好东西不私藏

OpenClaw 巡检从入门到生产:三个踩坑经验,让你的 AI 巡检真正能用

OpenClaw 巡检从入门到生产:三个踩坑经验,让你的 AI 巡检真正能用

我上个月在 300+ 服务的生产集群上把 OpenClaw 巡检跑通了,从”能用”到”好用”,中间踩了三个大坑。

今天把这三个坑和你讲清楚。不讲概念,只讲我在生产环境上真刀真枪干出来的东西。

坑一:没接 OTel,你的巡检就是空中楼阁

先说结论:OpenClaw 巡检想要做好,企业必须接入 OpenTelemetry。没有例外。

我见过太多团队上来就在 OpenClaw 里写 Spec、配 Cron,搞了一周发现巡检结果跟垃圾没区别。不是 OpenClaw 不行,是你喂给它的数据就是垃圾。

巡检的本质是什么?是让 AI Agent 帮你回答一个核心问题:”我的服务现在健不健康?”

那 Agent 怎么判断健不健康?它得看到三样东西:

  • 指标(Metrics):CPU、内存、QPS、错误率、P99 延迟
  • 日志(Logs):ERROR/WARN 级别的结构化日志
  • 链路(Traces):完整的请求调用链

这三样东西散落在三个系统里——Prometheus 里有指标,Elasticsearch 里有日志,SkyWalking 里有链路。三个系统各说各话,服务名不统一,trace ID 关联不上。你让 Agent 拿着一堆互相矛盾的数据做判断,它不是在分析,是在猜。

OTel 解决的就是这个问题——用一套 SDK、一套 Collector、一套协议,把三种信号统一起来,通过 trace_id 串在一起。

接入 OTel 之后,Agent 拿到的数据长这样:

一条指标异常(order-service 错误率飙升到 15%)
  → 通过 trace_id 跳到具体链路(第 4 跳 redis.get 命令耗时异常)
    → 通过 trace_id 查到对应日志("connection pool exhausted"
      → 通过指标佐证(redis_pool_active_connections 达到上限)

三秒钟,证据链完整了。没接 OTel 的时候,这种分析人工要 8 分钟。

具体怎么接

K8s 环境下最省事的方式是用 OTel Operator 自动注入。Java、Python、Node.js、.NET 的服务加一行 Pod 注解就行,业务代码零改动:

metadata:
annotations:
instrumentation.opentelemetry.io/inject-java:"true"

Go 服务只能手动接 SDK,这个没捷径,但工作量可控——做一个内部模板,业务 copy 就行。

关键的一步:部署 OTel Collector,作为所有遥测数据的统一采集枢纽。一份基础配置:

receivers:
otlp:
protocols:
grpc:
endpoint:0.0.0.0:4317
http:
endpoint:0.0.0.0:4318

processors:
batch:
timeout:5s
send_batch_size:1024
resource:
attributes:
-key:deployment.environment
value:"prod"
action:upsert

exporters:
otlp_grpc:
endpoint:tempo:4317
prometheusremotewrite:
endpoint:prometheus:9090
elasticsearch:
endpoint:es:9200

service:
pipelines:
traces:
receivers:[otlp]
processors:[batch,resource]
exporters:[otlp_grpc]
metrics:
receivers:[otlp]
processors:[batch,resource]
exporters:[prometheusremotewrite]
logs:
receivers:[otlp]
processors:[batch,resource]
exporters:[elasticsearch]

不要让业务服务直连后端。Collector 是你的数据治理层,所有字段清洗、采样、脱敏都在这一层做。

坑二:OpenClaw 怎么知道该巡检哪些服务?service.project 这个字段救了我的命

OTel 接好了,数据流进来了,下一个问题来了:OpenClaw 怎么知道该巡检哪些服务?

一个 300+ 服务的集群,不可能每次全量巡检。有些服务属于交易链路,有些属于用户中心,有些属于内部工具。你需要按”项目”维度把服务分组,OpenClaw 按项目做巡检。

这就是 OTel Resource Attributes 里 service.project 这个字段的作用。

什么是 Resource Attributes

OTel 里每条遥测数据都有一组 Resource Attributes,用来标识”这条数据是从哪来的”。标准字段包括 service.nameservice.versiondeployment.environment 等。但 OTel 允许你加自定义字段。

我在 Collector 的 resource processor 里加了一个关键配置:

processors:
resource:
attributes:
-key:service.project
value:"trade-platform"
action:upsert

这样一来,所有流过这个 Collector 的数据都带上了 service.project=trade-platform 这个标签。Prometheus 里的指标、Tempo 里的链路、ES 里的日志,全都有了项目归属。

实际上怎么做分组

更合理的做法是按 namespace 或服务名前缀做映射,而不是所有服务硬编码一个值。用 OTel Collector 的 transform processor:

processors:
transform:
error_mode:ignore
metric_statements:
-context:resource
statements:
# 按 namespace 映射到不同的 project
-set(attributes["service.project"],"trade-platform")whereattributes["k8s.namespace.name"]=="trade"
-set(attributes["service.project"],"user-center")whereattributes["k8s.namespace.name"]=="user"
-set(attributes["service.project"],"payment")whereattributes["k8s.namespace.name"]=="payment"
-set(attributes["service.project"],"infra")whereattributes["k8s.namespace.name"]=="monitoring"orattributes["k8s.namespace.name"]=="logging"

然后在 OpenClaw 的 Spec 文件里,按 service.project 定义巡检范围:

## 巡检范围

按项目分组:
trade-platform: order-service, cart-service, inventory-service
payment: payment-service, refund-service, settlement-service
user-center: user-service, auth-service, profile-service

## 执行策略
trade-platform: 每小时巡检一次(P0 业务)
payment: 每 30 分钟巡检一次(P0 业务)
user-center: 每 2 小时巡检一次
infra: 每天巡检一次

OpenClaw 查询 Prometheus 的时候,用 service.project="trade-platform" 做 label selector,只拉属于这个项目的服务指标。不是全量扫描,是精准巡检。

这个设计解决了我一个很痛的问题:以前每次新增服务都要手动更新巡检列表,漏了就巡检不到。现在只要服务接了 OTel 并且配了 service.project,自动就会被纳入对应的巡检范围。新增服务不再需要人工登记。

坑三:巡检报告一定要变成图片,没人愿意看一坨文字

这一坑,是我自己犯的蠢,说出来你别笑。

我第一版巡检报告是这样的——OpenClaw 跑完 Spec,直接把 Markdown 文本甩到企业微信群。一段密密麻麻的文字,带几个表格,没人看。运维同学的原话是:”你这比我直接看 Grafana 还费劲。”

后来我想明白了一件事:巡检报告的受众不是机器,是人。人更容易接受图片。

一张排版精良的巡检报告图片,比一屏文字的信息密度高 10 倍。你扫一眼就知道哪些服务有问题,不用去表格里找数字。

具体怎么做:Markdown → HTML → PNG

整个 pipeline 分三步:

第一步:用 Template 控制输出结构。

在 OpenClaw 的 Spec 里引用一个 Template 文件,确保每次巡检报告的结构一致:

## 巡检报告模板

整体健康度: {{绿色 / 黄色 / 红色}}

### 指标概览
| 服务 | CPU | 内存 | QPS | 错误率 | P99延迟 | 状态 |
|------|-----|------|-----|--------|---------|------|
| {{service}} | {{cpu}}% | {{mem}}% | {{qps}} | {{err_rate}}% | {{p99}}ms | {{status}} |

### K8s 状态
| 命名空间 | Pod 总数 | 正常 | 异常 | 异常详情 |
|----------|---------|------|------|---------|
| {{ns}} | {{total}} | {{healthy}} | {{unhealthy}} | {{detail}} |

### 行动建议
| 优先级 | 问题 | 建议操作 |
|--------|------|---------|
| P0 | {{issue}} | {{action}} |

第二步:写一个 Skill 把结构化数据渲染成 HTML。

这一步用代码做,不要让 LLM 生成 HTML。大模型在确定性渲染上永远不如代码。做一个 report-renderer Skill,里面放一个 Python 脚本,读入巡检 JSON 数据 + HTML 模板 + CSS 样式,输出一份完整的 HTML 文件。

HTML 模板的设计要点:

  • 宽度定 440px,适配手机屏幕
  • 用 CSS Grid 做卡片布局
  • 健康度用颜色区分(绿/黄/红)
  • 指标异常的行加背景高亮
  • 字体用系统字体,不依赖外部加载

第三步:HTML 转 PNG。

用 Playwright 做无头截图:

from playwright.sync_api import sync_playwright

defhtml_to_png(html_path, png_path):
with sync_playwright() as p:
        browser = p.chromium.launch(headless=True)
        page = browser.new_page(
            viewport={'width'440'height'800},
            device_scale_factor=2# 二倍图,微信里放大不糊
        )
        page.goto(f'file://{html_path}')
        page.wait_for_load_state('networkidle')
        page.screenshot(path=png_path, full_page=True)
        browser.close()

三个关键参数:viewport 宽度 440 对应移动端、device_scale_factor=2 保证清晰度、full_page=True 做长截图。

如果环境没装 Playwright,做三级降级——Playwright → Selenium → wkhtmltoimage,哪个能用就用哪个。

最后把 PNG 图片推送到飞书或企微群。图片比文字的阅读完成率高太多,我们上线图片报告之后,运维团队对巡检结果的关注度从”偶尔扫一眼”变成了”每天主动看”。

一个关键补充:基础指标缺失的时候,必须明确告诉用户

我在跑巡检的过程中还碰到过一种情况:OpenClaw 开始巡检了,查 Prometheus 的时候发现某些服务的基础指标根本没上报。CPU、内存、K8s Pod 状态全是空的。

这个时候 OpenClaw 不能假装没看到。我在 Spec 里加了一条硬性规则:

## 前置检查(必须第一个执行)

在执行任何巡检之前,先验证基础指标是否完整。对每个服务检查以下指标是否存在:

1. 
CPU 使用率:container_cpu_usage_seconds_total
2. 内存使用率:container_memory_working_set_bytes
3. K8s Pod 状态:kube_pod_status_phase
4. Pod 重启次数:kube_pod_container_status_restarts_total

如果任何一个服务的上述指标缺失,在报告顶部用醒目样式标注:

"警告:以下服务的基础 OTel 上报数据缺失,巡检结果可能不完整:
xxx-service:缺失 CPU、内存指标(疑似未接入 kubelet cAdvisor 或 OTel Host Metrics Receiver)
yyy-service:缺失 Pod 状态指标(疑似未部署 kube-state-metrics)"

缺失基础指标的服务,后续巡检步骤自动跳过,避免用不完整的数据得出错误结论。

这不是一个可选的功能,是必须做的。基础指标缺失意味着你的观测面有盲区,Agent 在盲区里做推理,得出的结论不可信。宁可明确告诉用户”这些服务我看不到”,也不要用不完整的数据编一个看起来合理的结论。

这两种做法的区别,就是”专业”和”业余”的区别。

整体架构长这样

把上面三件事拼在一起,完整的巡检架构:

业务服务(300+)
  │ OTel SDK / Operator 自动注入
  │ 携带 resource attributes: service.name, service.project, deployment.environment
  ▼
OTel Collector
  │ transform processor: 按 namespace 映射 service.project
  │ resource processor: 补齐环境信息
  │ batch processor: 批量发送
  ▼
┌──────────┬──────────┬──────────┐
│  Tempo   │Prometheus│    ES    │
│ Traces   │ Metrics  │  Logs   │
└────┬─────┴────┬─────┴────┬────┘
     │          │          │
     └──────────┴──────────┘
                │
                ▼
     OpenClaw Agent Runtime
     ┌─────────────────────────────┐
     │ Spec: 定义巡检任务流          │
     │ Template: 控制报告输出格式    │
     │ Skill: 封装数据查询能力       │
     │ Cron: 定时触发巡检           │
     └──────────┬──────────────────┘
                │
                ▼
     巡检 JSON → HTML 渲染 → PNG 截图
                │
                ▼
     推送到飞书 / 企微 / 钉钉

写在最后

OpenClaw 巡检想要跑好,核心就三件事:

  1. 接 OTel。数据质量决定了巡检准确率的上限。没接 OTel 的巡检,等于让一个盲人给你做体检。
  2. 用 service.project 分组。让 Agent 知道哪些服务该一起巡检,新增服务自动纳入,不用手动维护列表。
  3. 报告变图片。人更容易接受图片。一张排版好的巡检 PNG,比一屏 Markdown 文字的阅读完成率高 10 倍。

这三件事我都踩过坑,都是在生产环境上跑出来的经验。巡检不是一个工具能解决的事,是数据层、编排层、呈现层三件事拼在一起才能做好。

如果你对 AI Agent 在运维场景的落地感兴趣——不只是巡检,还包括根因分析、自动修复、多 Agent 编排——我正在做一个「Agent 架构师训练营」,手把手带你从 0 搭建一套生产级的 AI Agent 运维系统。从 OTel 数据层到 Agent 编排到报告生成,全链路实战。

训练营第二期已经在筹备中了,感兴趣的同学可以关注后续动态。