凌晨两点半,手机告警把我们组的值班群炸醒。监控面板上整整一屏的红色,Prometheus 的告警规则里堆着十来个 firing,最显眼的一条是 kube-apiserver 探针失败,后面的日志只有一句孤零零的报错:the api server is not healthy after 4m0.00747357s。我第一反应是打开 Grafana 看 apiserver 的延迟和错误码,然后又跳到 Kibana 翻 kube-system 的日志,再切到终端用 kubectl get nodes 挨个看状态,最后还得翻前天谁合进了什么配置变更。等我把“大概是被某个控制器打爆了”这个结论拼出来的时候,已经过去快一个小时。这种事在 K8s 生产环境里太常见了,手动排障的每一分钟都在烧钱,也在烧人。
后来我们团队决定认真做一件事:把生成式 AI 引到 K8s 运维链路里,从“人看告警、人查日志、人猜结论”变成“机器先做一轮诊断、人通过对话确认和推进”。这套平台跑起来之后,同一类 API server 不健康的故障,我们可以在五分钟内拿到候选根因和处置建议。这篇东西不是产品宣传,而是我们踩坑、拆解、取舍后的完整记录,给同样被 K8s 排障折磨的运维、SRE、平台工程师一个可参考的落地路径。
1. 传统 K8s 排障为什么慢:从“告警隧道”到“日志大海捞针”
先说清楚我们当初要解决的真实痛点。K8s 的生产故障从来不是“单个组件坏了”这么简单,它更像连锁反应:某个节点负载异常会触发 Pod 驱逐,Pod 驱逐又导致 Redis 集群失去多数派,Redis 不可用进一步拖垮业务接口,监控告警像雪崩一样涌过来。手动排障的最大问题不是不懂命令,而是信息获取路径太长,我们得在十几个系统之间来回跳,才能拼出完整现场。
1.1 我经历过的最典型的 K8s 生产故障
那次 Redis 集群故障就很典型。业务监控先报了“缓存命中率骤降”,紧接着是“订单服务超时”,然后才是 K8s 层面的“Pod 频繁重启”。三个告警来自三个不同系统:业务监控在 SkyWalking,K8s 事件在 Control Plane 的 audit log 里,节点负载在 Prometheus。我当时的排查顺序是:
- 看缓存监控,确认 Redis 确实有节点失联;
- 用
kubectl get pods -n redis -o wide看哪些副本在重启,发现 3 节点 Redis 集群只剩 2 个 Ready; - 查看 Redis Pod 的 Previous Container 日志,看到
# Server started和# Cluster state changed这种无关痛痒的标准输出,真正有用的 OOM 或磁盘满信息反而被日志淹没了; - 再看节点监控,发现其中一台节点的 /var/lib/docker 分区使用率 98%,才定位到根因。
算一下时间,Redis 的定位花了两小时,但真正“动手修”只用了三分钟——清掉孤儿镜像、腾出空间、Pod 恢复。大多数 K8s 故障都是这种“诊断十分钟、修复三秒钟”的形态,所以提升诊断效率才是值钱的事。
1.2 手动排障步骤拆解:拿到告警后的前 30 分钟
把手动排障的流程拆开看,前 30 分钟基本固定是这样的套路:
- 确认告警真实性:先刷新监控,看看是一次性抖动还是持续故障,这一步经常被忽略,因为很多告警是 Flapping(抖动)状态,等你切过去它自己恢复了。
- 圈定故障半径:用
kubectl get events --all-namespaces扫一遍全局事件,看有没有明显的 FailedScheduling、BackOff、Unhealthy 之类的异常事件,这一步非常粗,但能快速判断是个别节点还是全局性故障。 - 看核心链路状态:apiserver 是否健康、etcd 是否健康、kubelet 是否还在上报节点心跳。这属于“怀疑基础设施”,因为如果 Control Plane 有问题,下面排查全是白费力气。
- 翻关键服务日志:根据服务名去 Kibana 或 Loki 里过滤
error级别日志,有时候还要查 Previous 日志,因为重启后的容器日志会丢失现场。 - 看资源水位:CPU、内存、磁盘、网络,尤其注意磁盘 IO 和 inode 使用率,这两个经常是隐形杀手。
- 关联变更记录:最后还得看最近有没有发布新版本、调整过配置、扩缩过容,很多问题其实是某个人改坏了什么东西。
这些问题链路每一步都要去不同的平台操作,而且每一步依赖前一步的“人的判断”。新手根本不知道先后顺序,老师傅则是脑子里有经验,但说不清是怎么得出的——这种隐性知识恰恰是后面做 GenAI 平台时最难复制的东西。
1.3 手动排障的瓶颈:信息分散与认知断层
真正让我下定决心做平台化改造的,是看到了两个无法靠“加人”解决的瓶颈。
第一个瓶颈是信息分散导致的上下文缺失。告警是一条链,但工具是孤岛。K8s 事件、Prometheus 指标、应用日志、链路追踪、CMDB 里的 Pod 归属关系,这些信息在人工排查时是割裂的。我们曾经遇到过kubectl describe pod里明明显示Liveness probe failed,但是应用日志里完全没报错,因为真正原因是节点上的 conntrack 表满了,长连接建立失败,而 conntrack 的问题在/var/log/messages里。这种跨层级的关联,单靠人眼去翻,效率极低。
第二个瓶颈是认知断层导致的经验失传。团队里最会排障的老 SRE 一旦休假,大家就只能退回“重启大法”。不是大家不努力,而是故障模式太多了:镜像拉取失败可能是网络、可能是凭证过期、可能是 Docker 镜像仓库限流;Pod Pending 可能是资源不足、可能是污点、可能是调度器 bug。每一种模式的排查路径都不一样,没有系统性的沉淀,经验就只能留在个人脑子里。GenAI 平台的一个潜在价值,就是把这些排障路径结构化、可复用,让整个团队的诊断能力拉平到接近老师傅的水平。
2. 平台定位与整体架构:不是“聊天机器人”,而是“诊断工作台”
刚开始我们差点把产品做歪——很多人一听“对话诊断”,第一反应是“做一个像 ChatGPT 一样的东西,大家去问它”。但实际跑下来你会发现,纯问答形态根本无法接进真实的运维流程。我们最后的定位是:这是一个带有诊断能力的“工作台”,对话只是交互入口,背后是完整的数据管道、工具链和知识库。
2.1 我们想解决的问题边界
必须承认,GenAI 不是万能的,尤其在 K8s 这种强逻辑、强状态、强上下文的环境里,我们不能让模型凭空产生答案。所以一开始就给平台划了边界:
- 能解决:基于已有监控、日志、事件、配置数据,快速给出候选根因;解释现象之间的关联;建议可执行但需要人工确认的排查命令;沉淀排障经验。
- 不解决:自动擅自修改集群配置、自动扩容缩容、自动重启服务——这些动作需要人工审批,而且在当前阶段,平台的定位是“提高人的判断速度”,不是替代人做决策。
这个边界非常重要。它决定了我们在数据接入上必须做得足够扎实,而不是把希望全押在模型推理上。
2.2 总体架构:数据接入层、索引层、知识层、Agent 编排层、交互层
我们最终落地的架构分成了五层,每层职责单一,方便独立扩容和维护:
| 层级 | 职责 | 核心组件 |
|---|---|---|
| 数据接入层 | 从 K8s API Server、Prometheus、日志平台、操作审计等来源采集原始数据 | kube-state-metrics、Prometheus Remote Write、Filebeat、Audit Log Webhook |
| 索引与关联层 | 对数据做清洗、归一化、标签关联,构建时间线上的“故障现场” | OpenSearch、Prometheus TSDB、关系图谱 |
| 知识层 | 存放文档、历史故障记录、runbook、变更后的经验总结,供模型检索 | 向量数据库 + 文档切分 + 知识索引 |
| Agent 编排层 | 接收对话意图,调用工具获取实时数据,把上下文喂给大模型,推理出结论 | 自研 Orchestrator + Function Calling + LLM Gateway |
| 交互层 | 对话界面、诊断报告、确认按钮、执行记录 | Web 前端、钉钉/飞书机器人 |
其中最关键的设计决策,是把“实时数据获取”和“模型推理”解耦。模型不能直接从集群里抓数据,它只能通过 Agent 编排层调用我们封装好的工具,比如kubectl_get_pods、promql_query、get_events、get_node_info。这样模型的输出可追踪、可审计,避免模型产生不存在的 Pod 或指标——它必须基于真实数据说话。
2.3 为什么选择 RAG 而非直接用大模型裸答
这个被问了很多次,我直接说结论:生产级诊断不能接受模型“凭印象”回答。如果直接用大模型裸答,它会把一些常见的 K8s 错误模式背出来,比如“API server 不健康可能是 etcd 慢”,但这个回答没有结合你集群当前的 etcd 延迟、没有结合最近的配置变更,价值非常有限。
RAG(检索增强生成)解决的是“把知识变成上下文”的问题。我们的知识库里存了三大类东西:
- 官方文档和原理知识:K8s 调度原理、etcd 工作机制、CSI 插件常见问题等,这部分帮模型建立基础认知;
- 团队历史故障记录:每次故障复盘后,我们会整理成结构化文档,包括现象、排查链路、根因、修复动作,这些是模型“借鉴经验”的材料;
- Runbook 和操作手册:类似“如何安全驱逐节点”“如何扩 etcd 磁盘”这种操作步骤,模型检索到之后,可以直接生成分步指南。
RAG 的检索质量决定了诊断质量。我们的做法是把每个历史故障文档切分成若干 chunk,每一块带上时间戳、集群版本、故障域标签,查询时先用当前故障的粗粒度特征(告警类型、受影响资源、命名空间)做召回,再交给模型综合推理。这套组合下来,模型的回答不再是教科书式背书,而是“结合当前现场和历史经验的判断”。
3. 核心数据资产管理:打通监控、日志、事件与资源拓扑
数据层是整个平台的底层底座。这里有个反直觉的经验:我们花在模型上的精力,远不如花在数据清洗上的精力。没有干净、关联、实时的数据,再强的模型也是“巧妇难为无米之炊”。
3.1 四类数据源的正确接入方式
K8s 排障需要的数据基本是四类:监控指标、日志、事件、资源状态。每一类接入时都有坑,我逐个说。
监控指标:最标准的方式是用 Prometheus + kube-state-metrics + node_exporter,重要的是设置好采集周期和保留策略。诊断平台要能查“过去 5 分钟的 apiserver 请求延迟 P99”,这就要求指标在 TSDB 里至少保留 15 天以上,我们最终把 Prometheus 的存储扩展到了 30 天。另外,必须把 PromQL 查询能力封装成工具接口,大模型才能实时取数。类似sum(rate(apiserver_request_total{code=~"5.."}[5m]))这种查询,有时候模型生成的表达式会漏掉标签或写错聚合方式,所以我们做了一个 PromQL 模板库,让模型优先调用模板,而不是自由生成。
日志:日志是排障信息量最大的来源,但也是最难治理的。K8s 容器日志默认是不落盘的,如果用 JSON 格式输出,字段名可能千奇百怪。我们的做法是统一接入 Loki 或 OpenSearch,按 namespace/pod/container 建立索引,同时把标准输出日志和容器崩溃前的 Previous 日志分开存储。这样排查异常重启时,可以直接让模型调用get_previous_logs工具拿到上一版容器的错误栈,而不必在界面里手动切换。
事件:K8s 事件是宝藏,但很容易被忽略。重点要关注Warning级别的事件,比如BackOff、FailedScheduling、Evicted、Unhealthy。我们通过kubectl get events --all-namespaces采集后写入索引层,再转成结构化的 JSON,字段包括事件类型、涉及对象、reason、message、时间戳。部分事件还会带上“关联对象的状态变化”,这个信息对模型判断根因非常有用。
资源状态:除了指标和日志,还需要实时的资源清单。我们每天定时把节点、命名空间、Deployment、StatefulSet、Pod 的 spec 和 status 同步到平台侧的数据湖。诊断时如果模型想知道“某 Pod 的 resource requests 是多少”,可以直接查这份快照,而不必每次实时调用 API Server,既快又省。
3.2 kube-state-metrics 与资源拓扑的构建
光有数据源还不够,关键是建立关联关系。我们做了个轻量级资源拓扑图,类似 CMDB 的作用,但更偏动态关系。
例如,一个 Redis 集群的三个 Pod,分别跑在 node-01、node-02、node-03 上,每个节点又关联着若干系统组件:node-02 上还有 kube-proxy、日志采集器、CSI 插件等。当一个节点异常,受影响的不只是 Redis,而是所有落在该节点的负载。平台拓扑层要能回答这类问题:“哪些 Pod 共享同一个节点?” “这个节点的宿主机有什么硬件或内核状态?”
我们基于 kube-state-metrics 暴露的标签和 OwnerReference,定期重建资源关系图谱,存成图数据模型。诊断时 Agent 拿到一个对象名,可以快速向上查“它属于哪个 StatefulSet”“它调度到哪个节点”“它对应的 PVC 底层存储卷状态”,不用模型去猜。
3.3 把 Bash/Kubectl 变成“可执行函数”
这一步是平台最具工程价值的部分。K8s 排障中很多动作是高频的,我们把它封装成一个个可被模型调用的函数,例如:
get_pod_info(namespace, pod):返回 Pod 状态、容器状态、重启次数、节点、IP、QoS 等级;get_node_conditions(node):返回节点 Ready、MemoryPressure、DiskPressure、PIDPressure 等状态;get_events(namespace, resource_type):返回最近 1 小时的相关事件;promql_query(expr, duration):执行 PromQL 并返回时序数据;get_deployment_rollout_history(deployment):返回最近几次发布的变更记录;get_recent_logs(namespace, pod, container, lines):返回最近 N 行日志,支持过滤关键字。
封装函数的时候有两条铁律。第一条是所有命令必须基于只读原则,写操作单独拆出来,走人工审批通道。第二条是输出必须限长,比如日志只取最后 200 行,PromQL 结果返回聚合后的 JSON,避免上下文过长导致模型“遗忘”。做完这层,Agent 编排层就能像人类一样“先查事件、再看日志、然后看指标、最后综合判断”,整个推理链条每一步都有实据。
4. 对话诊断的关键链路:外部化推理、工具调用与人工确认
平台能不能真正省时间,就看推理链路设计得对不对。我们的经验是:不要让模型在一个巨大的 prompt 里完成所有推理,而是让它分步走,每一步都能被人工查看和介入。这样既利用了 GenAI 的综合判断力,又保留了人对关键决策的控制权。
4.1 一次真实场景复盘:API server 不健康告警
用开头提到的那条告警来完整走一遍诊断链路。用户(值班运维)在对话界面粘贴了报错:kube-apiserver is not healthy after 4m0.00747357s,并附上“集群心跳丢失,部分用户访问异常”。
平台的处理流程是这样的:
- 意图识别:Orchestrator 识别出这是“API Server 健康诊断”请求,进入对应诊断模板;
- 数据采集:自动并行调用多个函数——查 apiserver Pod 状态、查 etcd 集群健康、查最近 1 小时 apiserver 错误码统计、查控制面节点的 load 和磁盘;
- 上下文组装:把工具返回的实时数据,加上从知识库检索到的“API server 不健康常见原因”(etcd 慢、证书过期、CPU 饥饿、并发过高、网络分区等),一起拼成“诊断现场”;
- 模型推理:大模型基于这些信息生成带概率原因的结论,每条结论后面都附上“支持证据”和“待验证项”;
- 人工确认:推送诊断报告到值班群,同时列出建议执行的下一步动作,比如“检查 etcd leader 切换次数”“重启 kube-apiserver 实例(需审批)”。
那次实际走下来,模型给出的首要候选是“etcd fsync 延迟升高导致 API Server 健康检查超时”,证据链是:etcd 的backend commit延迟从 5ms 飙到 300ms,同时 etcd 所在节点磁盘 IO 使用率 95%。第二条候选是“kube-apiserver 所在节点 CPU 被组调度任务占满”,因为节点 load 5 分钟均值达到了 18。这两条建议我们手动kubectl top nodes和对 etcd 做etcdctl endpoint health验证后,确认了前者。整个流程从提交告警到拿到诊断报告,用了不到 4 分钟,比人工翻日志快了十倍不止。
4.2 大模型如何利用知识库+实时数据生成候选根因
这一节是我觉得最值得分享的核心设计。很多人以为“让模型生成一个原因”就够了,但实际上生产环境故障往往是多种原因交织的。我们让模型输出的是一个候选根因列表,每个根因都带三个字段:
- 原因描述:例如“etcd 磁盘 IO 延迟升高导致心跳超时”;
- 证据:模型在实时数据中看到的支持点,比如“etcd fsync 延迟 300ms”“磁盘 IO util 95%”;
- 验证方法:告诉值班人员接下来应该执行什么命令或看什么指标来确认。
这样做的价值在于:模型不一定每一步都对,但它的候选和建议能极大压缩排查范围。即使第一条候选不是根因,值班人员顺着“验证方法”去查第二条,也比从头看日志高效。
对于生成候选根因的 prompt,我们也做了大量调优。一开始我们喂的信息太少,模型只会说“可能网络有问题”“建议重启”,后来逐步加入了知识库检索结果和工具返回的原始数据,模型回答才具备了针对性。另外,我们给模型做了“限轮次”设计:一次对话最多执行 5 次工具调用,防止模型陷入无意义的循环查询。如果 5 次之内仍没收敛,就要求值班人员介入,而不是让模型一直“试”。
4.3 安全护栏:诊断指令自动审批与最小权限执行
GenAI 平台能不能上生产环境,关键看安全边界。我们做了三层护栏:
- 工具权限最小化:模型能调用的函数全部是只读操作,写操作例如
kubectl drain、kubectl delete pod、kubectl scale都以“建议”的形式输出,不直接执行。需要执行时,由人工在界面上点击“应用”,并二次输入操作原因,前端再调用一个独立的执行模块,这个模块有单独的 RBAC,并记录完整审计日志。 - 上下文隔离:模型的 Prompt 中只包含与当前故障相关的命名空间和资源数据,同时开启日志脱敏,用户密码、Token 等敏感字段不会进入模型上下文。
- 敏感行为关键词识别:如果模型输出内容包含“删除”“驱逐”“清理”“关闭”等危险动词,系统会在界面上弹出红色确认框,并且这类操作必须由具备 cluster-admin 权限的人审批。
这三层护栏让我们敢把诊断报告直接推给一线值班同学。他们看到建议后,可以放心地去执行验证命令,不会因为害怕模型乱操作而不敢用。
5. 部署与调优中遇到的坑,以及我的取舍
最后写一些部署和调优的真实体会。这个平台从原型到上生产,我们踩了很多坑,挑几个最有代表性的讲讲。
5.1 模型选型与 K8s 上运行 LLM 的资源成本
模型选型不能只看效果,还要看你们的算力预算。我们初期试过两套方案:一套是直接调用云端大模型 API,另一套是在自建 GPU 节点上部署开源模型。
调用 API 的方式省心,但有两类问题:一是数据出域,集群的监控指标和日志经过脱敏后仍然让人不放心,合规团队审了很久;二是网络延迟和限流,排障高峰时大家都在调用,API 响应慢会让值班人员失去耐心。
自建 GPU 节点跑开源模型(例如 Llama 系列、Qwen 系列)的话,成本和运维压力都不小。一个 7B 参数模型做推理大概需要 16GB 显存,我们考虑到并发请求最多 4 个,最后采购了 2 张 24GB 显存的卡,配合 vLLM 做推理服务,基本能稳定支撑。我们的经验是:先不要追求最大的模型,而是在“能跑起来的模型”里选推理效果最好的那个。比如在工具调用这一类任务上,不少 13B 级别的开源模型已经够用。不要看到 benchmark 就上头,上了生产你才会发现,响应速度和稳定性往往更重要。
5.2 索引和缓存设计:让诊断速度不再是瓶颈
刚开始我们没做缓存,每次诊断都实时查询、实时推理,结果一个诊断要三四十秒,很影响体验。后来优化分了两步。
第一步是指标缓存。对于近 10 分钟内的告警,平台会自动把相关指标快照缓存到内部存储,后续再遇到类似诊断就不用重复请求 Prometheus。第二步是诊断结果缓存:如果同一类告警在 30 分钟内再次发生,平台会直接返回上一次的诊断报告,只标注“这是重复告警,详见历史报告”。这两步把 80% 的重复诊断从三四十秒降到了 3 秒内。
同时我们优化了知识库检索的召回速度。向量检索之前用的 HNSW 索引,但文档量上来之后,每次召回要 60~100ms,虽然不致命,但我们还是加了粗排层:先用告警类型和资源标签做一次布尔过滤,再在过滤后的小集合里做向量检索。效果很明显,召回质量不但没降,还因为“范围更聚焦”而变得更准了。
5.3 从“事后诊断”到“主动巡检”:GenAI 的下一步
现在这套平台已经稳定运行了三个多月,但它还只是“事后诊断”。我个人的下一步方向,是想把它从“接告警”升级成“主动巡检”——每天固定时间生成集群健康巡检报告,让大模型基于指标趋势,标记出可能恶化的项。比如它发现某个节点的磁盘使用率一周内从 60% 涨到 85%,就会提前生成一条警告,并附上清理建议。这本质上是把“排障经验”用在“隐患发现”上,价值更大。
另外,我也在试着把“对话诊断”扩展成“协同会话”。比如多个值班同学可以同时在一个诊断会话里提问,模型能记住上下文,还能把某个人贴的一段日志纳入后续推理依据。这个功能做出来之后,排障会从“一个人孤军奋战”变成“一个团队共享同一份智能”。但这些都是长期规划了,目前最让我欣慰的,还是从手动翻日志到对话拿结论这个过程真正跑通了,而且每天都有新故障在“喂养”知识库,让系统越用越聪明。
在做这个平台的整个过程中,我个人最大的收获其实是重新理解了运维这个职业。GenAI 不会取代运维,但它确实能替我们承担掉那些机械、重复、需要跨系统拼凑信息的脏活。过去我们花一小时在告警、日志、监控之间反复横跳,现在这一个小时被压缩成了五分钟的人机协作——人负责判断和决策,模型负责检索和归纳。这种工作方式改变之后,团队里的新同学也能像老 SRE 一样,接到一条陌生告警时不慌不乱、有理有据地说出下一步该做什么。我想,这就是智能运维平台最大的价值所在。