Skip to content

K8sGPT 飞书通知适配器(k8sgpt-feishu-adapter)详解与最佳实践

组件:k8sgpt/feishu-adapter.yaml(Secret + ConfigMap 脚本 + Deployment + Service,单文件交付) 链路:K8sGPT CR sink(cloudevents) → k8sgpt-feishu-adapter:8080 → 飞书群机器人

1. 为什么需要它

K8sGPT operator 的 sink 只支持三种类型:slack / mattermost / cloudevents没有飞书。 而飞书自定义机器人只认自己的消息协议(msg_type: text / interactive / ...)。 把 sink webhook 直接指向飞书地址会返回:

{"code": 19002, "msg": "params error, msg_type need"}

适配器就是中间的协议转换层:HTTP 收 cloudevents → 解析 → 润色/翻译 → 组装飞书互动卡片 → 转发。

选型考量:python 标准库单文件http.server + urllib,零三方依赖), 镜像复用内部 Harbor 的 python:3.12-alpine(约 50MB),requests 仅 10m/32Mi, 不引入 flask/fastapi 等框架——通知链路越简单越可靠。

2. 报文格式(最容易踩的坑)

2.1 sink 真实载荷 ≠ Result CR

直觉上会以为 sink 把 Result CR 原样 POST 出来,实际不是。operator v0.2.x 的 cloudevents sink 发送的是 slack 风格结构(源码 pkg/sinks/cloudevents.go):

json
{
  "specversion": "1.0",
  "id": "7b3da893-...",
  "source": "https://github.com/k8sgpt-ai/k8sgpt-operator",
  "type": "com.github.k8sgpt-ai.k8sgpt-operator.sinks.cloudevents",
  "datacontenttype": "application/json",
  "time": "2026-08-01T06:49:28Z",
  "data": {
    "text": ">*[k8sgpt] K8sGPT analysis of the Pod mysql/mysql-primary-0*",
    "attachments": [
      {"type": "mrkdwn", "color": "danger", "title": "Report",
       "text": "Error: MySQL容器因内存不足被系统强制终止(OOMKilled)。\nSolution: 1. ..."}
    ]
  }
}

要点:

  • 资源标识在 data.text(自然语言句式),需正则提取 analysis of the <Kind> <ns/name>
  • AI 报告在 data.attachments[].text,格式固定为 Error: ...\nSolution: ...
  • 按 Result CR(data.spec.kind/name/error/details)解析会字段全空—— 这就是"飞书收到通知但没有内容"的根因(本项目实测踩坑)。

2.2 适配器的双格式兼容

parse() 按优先级识别两种载荷:

优先级判定条件来源
1dataattachments 或(含 text 且不含 speccloudevents sink 真实格式
2data.spec 存在Result CR 直发(防 operator 后续版本改行为)

解析抛异常时不吞错、不丢消息:把原始报文截断塞进卡片发出(⚠️ 报文解析失败), 保证"永远有内容、永远可追溯"。

3. 处理流水线

POST body
  → parse()            双格式解析,提取 (kind, name, report)
  → ensure_chinese()   报告无中文字符 → 调 AI 翻译成简体中文(失败降级原文)
  → md_report()        Error:/Solution:/错误:/建议: → **❌ 问题**/**🛠 修复建议** 排版
  → build_card()       飞书互动卡片(红头 + 分栏 + markdown + 注释)
  → post()             POST webhook,15s 超时,响应打印日志

3.1 中文兜底翻译(ensure_chinese)

K8sGPT 侧已配 language: 简体中文(注意:填 chinese 实测 qwen-plus 仍回英文, 因为提示词模板本身是英文且权重更高)。但 anonymize 打码内容偶尔仍会诱导模型输出英文, 适配器做最后一道保险:

  • 报告主体一个中文字符都没有(正则 [一-鿿] 不匹配)才触发翻译;
  • 翻译复用 k8sgpt-backend Secret 里的 AI Key(env AI_KEYoptional: true 引用), 提示词要求保留 Error:/Solution: 格式与命令/资源名;
  • 翻译失败静默降级为原文——通知可达性永远优先于美观性;
  • 日志出现 translated report to Chinese 即兜底生效。

3.2 卡片设计(build_card)

区块内容
标题栏template: red(异常=红色)+ 🔍 K8sGPT 发现集群异常
分栏字段资源类型 / 资源名称(等宽字体)
正文❌ 问题 + 🛠 修复建议,markdown(命令/反引号渲染等宽)
底部注释K8sGPT AI 诊断 · 每 10 分钟巡检 · 数据脱敏后分析

正文截断 2500 字符(飞书卡片元素有长度限制,AI 报告一般 < 2000 字符)。

3.3 配置项(env)

变量来源说明
FEISHU_URLSecret k8sgpt-feishu/webhook-url飞书机器人 webhook(必须放 Secret,不入库不入文档
AI_KEYSecret k8sgpt-backend/openai-api-key(optional)兜底翻译用,缺失则自动跳过
AI_BASE默认 DashScope compatible-mode切内网 vLLM 时改这里
AI_MODEL默认 qwen-plus翻译模型

4. 可观测性

适配器所有关键节点都打印 stdout 日志(此前为"安静"关过日志,踩坑后证明日志必须留):

bash
kubectl -n k8sgpt logs deploy/k8sgpt-feishu-adapter
日志含义
recv N B: {...}收到报文(前 500 字符),可核对真实 payload 结构
translated report to Chinese兜底翻译生效
translate failed (use original)翻译失败,已降级发原文
parse failed: ...报文结构异常,已发原始内容卡片
feishu resp: {"code":0,...}飞书受理成功
forward failed: ...飞书不可达/返回错误,给 sink 回 502

飞书常见错误码:19002 消息格式错误、19056 触发关键词校验、19024 触发频率限制。

5. 最佳实践总结

  1. 协议转换层保持极简:标准库单文件、无框架、无状态、单副本。 通知丢了由 operator/Alertmanager 的重试机制兜底,适配器短暂故障最多导致延迟。
  2. 永远不让消息为空:解析失败 → 发原始报文卡片;翻译失败 → 发原文。 通知系统的第一要务是"到得了、看得懂",美观其次。
  3. 按真实载荷写解析,不按想象写:mock 报文验证不了生产格式, 上线前用真实 sink 事件端到端验证一次(删一条 Result 等下一轮分析即可)。
  4. 留日志recv 摘要 + 下游响应,是排查"收不到/没内容/格式错"的第一现场。
  5. 敏感信息全部入 Secret:webhook、AI Key 不进 ConfigMap/文档/git; 本地副本 .secrets.env chmod 600,站点发布前 sanitize 脱敏。
  6. AI 调用全部走 optional 引用:Key 缺失时功能降级而非 Pod 起不来。
  7. 控制通知频率:飞书机器人限 100 条/分钟;K8sGPT 侧靠 interval: "10m"
    • 结果去重(同一问题不重复产出 Result)天然限流。
  8. 机器人若设关键词校验,卡片标题含"异常"类关键词,或在飞书后台把关键词加白, 否则报 19056
  9. 换群/换机器人kubectl -n k8sgpt edit secret k8sgpt-feishu 改 webhook-url, 再 rollout restart deploy/k8sgpt-feishu-adapter
  10. operator 升级后回归验证:sink payload 结构是 operator 内部实现细节, 跨版本可能变化——升级后删一条 Result 触发真实通知,核对适配器 recv 日志。

6. 手动验证链路

bash
# 在集群内发一条真实形态的测试报文(格式与 operator sink 一致)
kubectl -n k8sgpt run feishu-test --rm -i --restart=Never \
  --image=192.168.122.156:30000/k8sgpt/python:3.12-alpine --command -- python -c '
import json,urllib.request
ev={"data":{"text":">*[k8sgpt] K8sGPT analysis of the Pod test/demo*",
  "attachments":[{"text":"Error: 容器反复重启\nSolution: 1. 检查 command/args","title":"Report"}]}}
print(urllib.request.urlopen(urllib.request.Request(
  "http://k8sgpt-feishu-adapter:8080/",data=json.dumps(ev).encode())).read().decode())'

返回 {"code":0,"msg":"success"} 且群里出现红色卡片即链路正常。

7. 故障排查速查

现象排查
群里没有消息适配器日志无 recv → sink 未触发(看 operator 日志/有无新 Result);有 recvfeishu resp → 网络/webhook 失效
消息有但没有内容recv 的实际 payload 结构是否变化(operator 升级?),适配器兜底应发原始内容
报告是英文正常:兜底翻译会自动处理;检查日志是否有 translated;CR 的 language 是否为 简体中文
飞书报 19056机器人关键词校验,卡片标题加白或调整关键词
飞书报 19024通知频率超限,检查是否有 Result 抖动反复产生
翻译不生效AI_KEY 是否注入(kubectl -n k8sgpt exec deploy/k8sgpt-feishu-adapter -- env | grep AI_);DashScope 额度