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