资讯动态

EKS告警拦截器实战:用大模型把Alertmanager通知翻译成人话

发布时间:2026/10/21 12:32:58 来源:尧图企业网站定制
凌晨两点十五分手机在床头柜上震了第三遍。我眯着眼划开屏幕一条来自 Alertmanager 的告警赫然写着KubePodCrashLooping - payment-svc-687d8f9c7b-xxx - crash loop back-off这种告警K8s 管理员和 SRE 应该都不陌生。可问题是这条消息除了告诉我有个 Pod 在崩溃重启什么都没说。为什么崩溃影响的是哪个上游调用方处理到哪一步了日志里有什么异常我盯着这条告警还得再花十分钟去 EKS 控制台、EFK 里翻日志、看监控才能判断今晚能不能继续睡。我一直在想一个问题AIOps 的口号喊了这么多年能不能先把告警翻译成人话这种最朴素、最痛苦的需求解决掉于是就有了这个项目——在 EKS 集群里搭一个智能拦截器把 EFK 体系产生的各种原始告警截住用业务语义重新解释顺带去重、聚合、分级最后让每条告警像是值班老手帮你分析过一遍再推给你。这篇文章就是整个项目的完整复盘从架构选型到代码实现从部署方式到踩坑记录适合正在做云原生告警治理、受够了告警轰炸的运维和 SRE 同学参考。1. 项目全貌EKS 上 EFK 的告警为什么需要拦截与翻译1.1 一个足够真实的夜间告警现场先复盘一下没有拦截器的时候一条告警从产生到处理要经历什么。我们的监控体系是经典的 Prometheus Alertmanager日志链路用的是 EFK 三件套Elasticsearch 存索引、Fluentd 采集转发、Kibana 做查询和可视化。告警规则一堆覆盖了 CPU 高水位、内存压力、Pod 重启、PVC 容量、5xx 响应等常规场景。问题在于告警通知本身是机器生成的格式固定、字段稀疏、上下文缺失。像最前面那条KubePodCrashLooping它在 Alertmanager 里的完整内容大概是这样{ status: firing, alerts: [ { labels: { alertname: KubePodCrashLooping, namespace: prod-payment, pod: payment-svc-687d8f9c7b-xxx, severity: critical }, annotations: { description: Pod prod-payment/payment-svc-687d8f9c7b-xxx has restarted 5 times in the last 15 minutes. }, startsAt: 2025-01-15T02:03:20.000Z } ] }这条 JSON 里没有任何信息能告诉你这个服务是干嘛的、它依赖哪些下游、5 次重启意味着什么、EFK 里对应的日志有没有 OOM 或致命异常。你只能人肉去查。更何况夜里收到的往往不止一条而是同一个工作负载的兄弟 Pod 们一起崩溃群消息瞬间变成覆盖式轰炸。这也是为什么很多团队的 SRE 明明做了告警闭环还是在群里被拉着通宵——工具链是通的信息链是断的。1.2 智能拦截器到底是干什么的我做的这个智能拦截器名字听着玄乎本质上是放在 Alertmanager 和通知渠道之间的一层中间服务。它不改动你现有的监控体系和日志链路只是把发出通知这个环节接管过来完成四件事第一标准化。把不同告警源Prometheus、云监控、自定义探针的数据统一成一张有业务含义的表单提取命名空间、工作负载、Pod、严重级别、触发时间、对应的日志关键词。第二上下文补全。收到告警后主动去 EKS API 查工作负载状态去 EFK 拉最近一段时间的日志摘要拿到根因候选用的素材。第三智能翻译。把标注化的告警和日志摘要丢给大模型让它按照预设的提示词模板输出一条有人话解释、有影响面判断、有根因推测、有建议动作的通知。第四降噪收敛。在调用大模型之前做去重、聚合、抑制把 50 条重复告警折叠成 1 条群通知避免告警风暴连带烧掉 Token 预算。把它放在拦截这个位置还有一层用意它天然是一个转发代理可以根据严重级别、服务分组、值班时间把告警分流到钉钉、飞书、Slack 或者工单系统。保留普通告警进群抠出 critical 级别单独电话或强提醒这一层逻辑以前写在 Alertmanager 路由规则里现在可以做得更灵活。1.3 为什么把第一站放在 EKS选 EKS 作为 AIOps 落地场景因为托管 Kubernetes 集群各有各的抽象层和割裂感。EC2 时代你排查问题还有相对固定的链路上了 EKS 之后网络、存储、调度、托管组件层层叠加问题面更分散但另一方面EKS 自带的控制面 API、节点组标签、云厂商的 LB、存储卷、日志采集器之间的关系都相对结构化特别适合做告警 → 上下文检索 → 知识注入这套 AIOps 闭环。对我们来说最直接的原因是痛感最强。我们大概有四十多个命名空间三百多个工作负载每天的日志量在 200GB 量级。一旦某个公共组件出问题告警会跨命名空间串联一夜之间能攒下几百条。人肉一条条看根本不现实但跳过告警直接全链路日志分析又太重。在 EKS 上做一层轻量智能拦截器恰好卡在成本、效果、落地难度三者交界的位置。2. 智能拦截器架构设计与技术选型2.1 告警处理的完整链路整个系统的数据流是单方向的我梳理成六个环节Prometheus 的告警规则触发交给 Alertmanager 做最基础的静默、分组、抑制Alertmanager 把通知发送到拦截器的 Webhook 端点拦截器解析告警计算去重 key查询 Redis 做聚合状态比对拦截器异步去 EKS API 和 EFK 拉取上下文信息标签、Pod 状态、最近日志摘要调用大模型生成自然语言通知根据预设路由规则把生成好的通知发送到钉钉群、飞书机器人或工单系统。链路看起来不复杂但每个环节都有取舍。比如第 2 步为什么用 Webhook 而不是让拦截器定期去 Alertmanager API 拉因为 Webhook 是事件驱动的告警产生即推送延迟最低不会因为轮询间隔错过瞬时告警。缺点是 Alertmanager 会重试所以拦截器必须设计成幂等同一告警重复收到也不能重复生成通知。2.2 核心模块划分代码结构按职责拆成了五个模块这也是后来维护时觉得比较顺手的划分方式Ingress 模块负责接收 Alertmanager 和其他来源的告警统一做 schema 校验、字段映射、签名验证。Core 模块告警的聚合去重引擎里面有滑动窗口计数器、告警指纹计算、抑制规则匹配。Context 模块调用 EKS API 和 EFK 的客户端带缓存和超时控制负责把告警从一条 JSON丰富成一份包含上下文的事故快照。LLM 模块封装大模型 API 调用包括提示词模板渲染、请求重试、响应校验、Token 计数。Dispatch 模块通知渠道适配层。钉钉、飞书、Slack、邮件、Webhook 各有一个适配器统一接口。模块之间通过事件队列解耦。收到告警后Ingress 先把原始数据写入队列Core 做实时处理Context 和 LLM 都属于耗时操作放在消费者里异步执行。这样做有一个明显好处告警到达的瞬间拦截器可以先立刻回给 Alertmanager 一个 200告诉它这条我收了避免它重发真正耗时的上下文补全和翻译在后台慢慢做不阻塞通知通路。2.3 技术栈选型为什么是 Python FastAPI整个服务用 Python 3.11 FastAPI 写的。原因有三。第一生态最省心告警解析、Webhook 签名、钉钉飞书 SDK、大模型 SDKPython 全都有现成组件不需要像 Go 那样自己去写不常用的适配器。第二异步支持好FastAPI 基于 asyncioWebhook 接收和 LLM 调用都是天然的 IO 密集型操作协程能把并发撑得很高而不用引入一堆线程池管理代码。第三团队维护成本低——我们是运维团队Python 是大家最熟练的语言这决定了后续有人能接手。有人会问高并发场景用 Go 不是更香吗但拦截器的瓶颈从来不在 Web 框架本身而在下游依赖EKS API 有 QPS 限制EFK 查询有时间开销大模型 API 有并发上限。这些 IO 等待让 Python 异步的优势完全够用实测中单实例 4 核 8G 能扛住每分钟 600 条告警的峰值对绝大多数团队来说已经足够了。2.4 大模型接入的二选一方案大模型部分我们认真对比过两条路线一是直接调用托管的大模型 APIOpenAI 兼容接口或国内大模型平台的 API二是私有化部署开源模型比如在 EKS 里跑一个量化后的 Llama 或 Qwen。直接调 API 的优势是省事、效果好尤其是中文表达和推理能力商业模型比开源小模型强太多。劣势是网络出公网有延迟、有费用、有数据合规的问题需要做敏感信息脱敏。私有化部署则反过来数据不出内网合规压力小但部署和调优一套推理服务对运维团队是额外负担而且 7B 以下量级的模型想把告警翻译成人话做稳定效果确实要差一些。我们最后选了混合方案常规告警走关键词模板和人话模板只有需要根因推测和影响面分析的高危告警才调用大模型 API。另外在 LLM 调用前做了一层脱敏把 Pod 名、IP、命名空间里的业务敏感关键词替换成代号防止日志摘要里的用户信息外泄。这个决定同时控住了成本和合规风险后面会展开讲。3. 关键细节与核心逻辑拆解3.1 告警数据统一模型拦截器能不能说人话首先取决于内部数据模型是否足够表达事故快照。我设计的核心结构是一个 flat 化的告警事件模型字段分三层基础层alertname、namespace、workload、pod、severity、status、startsAt、endsAt、duration。指标层触发指标名、阈值、当前值、持续时间。这个非常重要不带上数值的告警等于没告警。上下文层k8s 工作负载类型、副本数、副本就绪数、镜像版本、节点名以及从 EFK 拉到的最近 30 条日志摘要。每个进来的原始告警都先映射到这个模型。字段缺失的规则直接标记为低质量告警在聚合时降权避免后续大模型被缺字段告警影响判断。举个具体的字段映射例子。Alertmanager 的告警 label 里通常只有pod但你很难只靠 pod 名去 EKS 查工作负载。我们在 Ingress 层写了一个解析函数从 pod 名反推 deployment 名去掉最后一段随机字符串再调 EKS API 确认这个 pod 属于哪个 deployment/statefulset最后把 workload 类型和副本状态写进上下文层。这一手让后面所有逻辑都方便了给告警分类、做路由、查日志都是基于 workload 级别的信息而不是 pod 级别碎片化的信息。3.2 提示词工程说人话的命根子整个项目里投入产出比最高、也最需要反复调试的就是提示词模板。第一版我写得很简单效果惨不忍睹——大模型确实把告警翻译得通顺了但说了一大堆车轱辘话没有给出任何可操作的建议。后来我把提示词模板结构化分成了四个区块这里直接放出我们验证过效果较好的版本你是一名有 10 年经验的 SRE 值班工程师负责 EKS 生产集群的稳定性。 现在有一条告警需要你转化为面向值班人员的中文通知要求如下 1. 用不超过 80 个字的一句话说明发生了什么以及用户可能感知到什么。 2. 列出最可能的前 3 个根因每个根因不超过 20 个字必须结合给定的日志摘要和监控指标不得编造日志中没有的信息如果无法判断写暂无法定位需人工查看日志。 3. 给出下一步处理动作建议必须是可执行的 SRE 操作例如查看此 Pod 的完整日志: kubectl logs ... --tail200、“扩容或摘流量: kubectl scale deployment ... --replicas3”或回滚上一个镜像版本中的一个。 4. 如果日志摘要中出现 OOMKilled、Cannot allocate memory、connection refused、EOF 等明确关键词请在根因中优先解释。 告警数据如下 --- 告警名称: {{alertname}} 命名空间: {{namespace}} 工作负载: {{workload}} 告警级别: {{severity}} 监控指标: {{metric_name}} 当前值 {{current_value}} 阈值 {{threshold}} 关键日志摘要: {{log_snippet_output}} --- 最终输出格式必须是纯文本不要输出 Markdown 标题不要输出任何解释性前缀。这个模板有两个关键设计。第一给模型限定了一个SRE 值班工程师的视角它输出的措辞就会从该 Pod 发生故障变成更贴近实操的话。第二明确禁止它编造日志里没有的信息。大模型天然有补全倾向你不勒住它它就敢给你编一个疑似内存泄漏导致的 OOM哪怕日志摘要里根本没有 OOMKey 字样。我们还用 few-shot 示例做校准在模板下方附了两组标准的输入输出对。这招很管用尤其是对输出长度和格式的稳定性提升明显。后来我算过一笔账每调用一次大模型大概消耗 800~1200 token加上 few-shot 示例的固定 token单条成本在 0.01~0.03 元左右后面加上缓存和前置规则整体成本完全可以接受。3.3 去重、聚合、抑制在调用大模型之前先省一半钱大模型调用再便宜也扛不住告警风暴。我们处理告警风暴靠三招去重对每个告警计算指纹key 由alertname namespace workload 核心label 触发时间窗口组成。同一个 key 在 30 分钟窗口内只保留第一次触发时生成的上下文后续重复到达的告警直接递增计数器不重复调用大模型。聚合同一工作负载在 10 分钟内产生的多种告警比如 CrashLoop 和 5xx 同时报合并成一条该工作负载当前存在 N 类告警的总通知附上各类告警的数量和首个触发时间。抑制利用 Alertmanager 已有的抑制规则拦截器里再做一道。规则很简单如果同一个 workload 已经有 critical 级别告警在 firing那么后续到达的 warning、info 级别告警就不再生成独立通知只在聚合通知里追加一条另有 N 条低级别告警。这套机制上线后最直观的变化是之前一次故障大概触发 120~200 条群消息现在压缩到 2~6 条而且每条都是能看懂、能操作的内容。这在心理上是巨大的解脱。3.4 上下文补全把 EFK 日志摘要喂给大模型大模型要说人话光靠告警 label 是不够的它需要日志里的蛛丝马迹。我们写了 Context Module收到告警后向 EFK 发起一次查询。查询逻辑是这样的先根据告警里的 pod 名拼出索引前缀比如filebeat-*然后用 Elasticsearch 的bool query组合三个过滤条件——namespace精确匹配、pod关键字匹配、时间范围设为告警触发前 5 分钟到当前时刻。排序按timestamp倒序取前 30 条日志。取回后用正则把堆栈帧、时间戳、IP 地址这类冗长内容截断或剥离最终生成一个不超过 1500 字的日志摘要文本。这个摘要不是通用的日志压缩而是带着目的性的提取。我们会额外把出现频次高的单词和错误码挑出来插入到摘要末尾的关键词字段。因为大模型对高频词的敏感度比人工一行行看日志高得多像OutOfMemoryError、connection reset、deadline exceeded这种词一旦高频出现根因方向基本就锁定了。EFK 查询也不是每次都现查。同一个 workload 在五分钟内的重复告警会直接复用第一次查询结果给 EFK 和 ES 减负。这里用 Redis 做缓存key 是workload 小时级时间戳避免跨小时把过期上下文带进来。4. 手把手落地从代码到 EKS 部署4.1 拦截器主服务代码骨架核心服务代码并不复杂我用 FastAPI 写了一个接收 Webhook 的入口。这里放出关键部分完整代码逻辑可以在我们自己仓库里看但主要思路下面就是全貌。import hashlib import json import asyncio from fastapi import FastAPI, Request, HTTPException from pydantic import BaseModel app FastAPI(titlealert-interceptor) class AlertmanagerPayload(BaseModel): status: str alerts: list[dict] class Interceptor: def __init__(self): self.redis None # 实际用 redis.asyncio 初始化 def fingerprint(self, alert: dict) - str: labels alert.get(labels, {}) core ( labels.get(alertname, ), labels.get(namespace, ), labels.get(workload, ), labels.get(severity, ) ) return hashlib.sha256(json.dumps(core).encode()).hexdigest() async def should_generate(self, fp: str) - bool: # 滑动窗口计数防止同一指纹在窗口内重复生成 count await self.redis.get(fp) return count is None interceptor Interceptor() app.post(/webhook/alertmanager) async def receive_alert(payload: AlertmanagerPayload): if payload.status ! firing: return {ok: True, skipped: resolved} for alert in payload.alerts: fp interceptor.fingerprint(alert) if await interceptor.should_generate(fp): # 异步执行上下文查询 LLM 通知发送 asyncio.create_task(process_alert(alert)) return {ok: True} async def process_alert(alert: dict): # 1. 查询 EKS 获取 workload 状态 # 2. 查询 EFK 获取日志摘要 # 3. 组装请求数据 # 4. 调用大模型接口生成自然语言通知 # 5. 根据 severity 路由到对应渠道 pass这个骨架有几个细节值得注意。第一收到告警后立即返回{ok: true}不管后面的慢操作是否完成这样 Alertmanager 不会因为超时就重试。第二用asyncio.create_task把耗时操作丢到后台Webhook 接口本身始终保持极快响应。但这里一定要配合信号量asyncio.Semaphore做并发限制不然告警风暴时后台任务会瞬间堆积几千个协程把 8G 内存打满。我们最终的并发上限是 50。LLM 调用的部分我们对 OpenAI 兼容接口做了一层封装超时设置 30 秒失败自动重试两次重试使用指数退避。async def call_llm(messages: list[dict]) - str: async with semaphore: for attempt in range(3): try: resp await client.chat.completions.create( modelgpt-4o-mini, # 根据成本和效果选择 messagesmessages, temperature0.2, # 低温度降低随机性 max_tokens512, timeout30 ) return resp.choices[0].message.content except Exception as e: await asyncio.sleep(2 ** attempt) # 最后一次失败则降级为模板通知 return render_template_notification(alert)这里非常重要的是temperature0.2。告警翻译这种任务是确定性优先不是创意写作把随机性压得越低越好。另外必须有一个降级路径大模型 API 挂了不能连告警通知都跟着挂必须回退到静态模板。这个降级我们专门用混沌演练验证过确保 LLM 模块宕机不影响告警基本通知能力。4.2 Alertmanager Webhook 配置拦截器上线后需要让 Alertmanager 把它当成一个 receiver。Alertmanager 的配置是 YAML 格式核心是加一个 webhook 类型的 receiver然后路由到对应的告警规则。route: receiver: ai-interceptor group_by: [alertname, namespace, workload] group_wait: 30s group_interval: 5m repeat_interval: 4h receivers: - name: ai-interceptor webhook_configs: - url: https://interceptor.internal.svc.cluster.local/webhook/alertmanager send_resolved: true这里有一个容易被忽略的配置group_wait: 30s。它的意思是 Alertmanager 收到一批同组告警后会等 30 秒再发送把该窗口内到达的同类告警打包成一个请求。这个参数对我们的成本控制帮助非常大——相当于在进入拦截器之前Alertmanager 已经帮我们做了一层初级聚合。send_resolved: true这个选项是让告警恢复时也向拦截器发一条 resolved 消息。不要以为恢复消息是噪音它其实非常重要。有了恢复消息我们可以在群里发一条故障已恢复持续时长 45 分钟真正做到告警闭环。这一条不加值班人员看到故障结束还得自己去确认。4.3 EKS 上的部署清单拦截器本身是一个无状态服务部署在 EKS 里非常简单。我们用一个 Deployment Service 承载主要清单如下apiVersion: apps/v1 kind: Deployment metadata: name: alert-interceptor namespace: ops spec: replicas: 2 selector: matchLabels: app: alert-interceptor template: metadata: labels: app: alert-interceptor spec: containers: - name: interceptor image: registry.internal/ops/alert-interceptor:1.4.2 ports: - containerPort: 8000 name: http env: - name: REDIS_URL valueFrom: secretKeyRef: name: ops-redis key: url - name: LLM_API_KEY valueFrom: secretKeyRef: name: llm-secret key: api_key resources: requests: cpu: 500m memory: 512Mi limits: cpu: 2 memory: 2Gi readinessProbe: httpGet: path: /healthz port: http livenessProbe: httpGet: path: /healthz port: http --- apiVersion: v1 kind: Service metadata: name: alert-interceptor namespace: ops spec: selector: app: alert-interceptor ports: - port: 80 targetPort: 8000有个细节很值得说一下Service 我们用的是普通的 ClusterIP而不是 LoadBalancer。因为 Alertmanager 是集群内服务通过http://alert-interceptor.ops.svc.cluster.local访问即可完全不需要暴露到公网安全性和网络延迟都更优。如果要接入 EKS 的云上监控直接在 service 上加一个prometheus.io/scrape: true注解就能把这个拦截器自身也纳入监控。这是我们自己特别看重的一点——做告警治理的工具必须先把自己管理好。每个告警处理耗时、LLM 调用延迟、Token 消耗量、上游 EFK 查询失败率都打了指标后续能看到这个拦截器本身是不是在稳定工作。4.4 成本与性能参数实测上线运行两个月后我拉了几组数据可以给打算复刻的同学一个参考基准。单实例 4 核 8G 的配置日常 CPU 使用率不到 10%峰值约 55%日均处理原始告警约 3000~8000 条经过去重聚合后实际发送到群里的通知日均 30~120 条大模型 API 的日均消耗约 2 万~6 万 token月度成本不到一百元相比人工排查省下的时间完全划算单条告警从 Alertmanager 发出到群内收到通知平均延迟 2~4 秒主要开销在 EFK 查询和大模型生成上。这个性能是在没有做过多预计算的情况下达到的。如果你现在才要开始做建议直接按这个配置起步不需要一次性把机器开到很大。AIOps 工具最重要的不是算力而是数据质量和逻辑设计——先把告警拦好、合并好大模型才有用武之地。5. 实战中的坑与排查技巧5.1 网络联调告警没到达拦截器上线第一天我们遇到的最典型问题就是 Alertmanager 的 Webhook 请求没到拦截器。检查思路按链路一步一步来先看 Alertmanager 日志里有没有Error sending webhook再看拦截器接入日志有没有收到请求然后确认两个服务之间的网络策略。EKS 环境里最容易被忽略的是 NetworkPolicy。如果集群启用了网络策略默认拒绝所有跨命名空间流量那么ops命名空间里的拦截器根本收不到来自monitoring命名空间 Alertmanager 的请求。排查时先在拦截器 Pod 里手动 curl 一下自己的/healthz再从一个测试 Pod 里 curl 拦截器的 ClusterIP逐步缩小范围。最终我们放行了monitoring到ops的 80 端口流量问题解决。另外一个小问题是 webhook URL 的路径写错。有人会习惯性写成/webhook但我们定义了/webhook/alertmanager路径不匹配时 Alertmanager 会收到 404然后按失败处理无限重试。这种低级错误几乎每个人都会犯一次建议在拦截器里做一个请求日志中间件把每次进来的 URL 和 header 全打出来联调时一目了然。5.2 告警风暴时 Token 被打爆最惨烈的一次事故发生在某核心服务批量重启。Prometheus 一次性产生了 300 多条 firing 告警Alertmanager 虽然做了分组但拦截器这边还没扛住——那批告警几乎同一秒涌入asyncio.create_task因为没限流瞬间创建了上千个后台任务每个任务都要查 EFK、调大模型。结果 EFK 先被打到查询超时紧接着大模型 API 返回 429 限流很多任务都走了降级模板然后 Redis 连接池也爆了整个服务 OOM 重启。这次事故给我最大的教训是所有外部依赖的并发量都必须显式控制不能指望应用自己扛。后来我们做了三处修改给后台任务加上asyncio.Semaphore(50)给 EFK 查询增加一个进程内令牌桶每秒最多 30 次查询给大模型调用单独限 10 并发。同时在告警入口处加了一个最简单的漏斗如果一分钟内收到的告警数量超过 500 条后面进来的告警直接合并进一条当前存在大规模告警风暴请查看 Prometheus 面板的通知不再逐条处理。5.3 大模型开始编造根因大模型提示词里明明写了不得编造日志中没有的信息但我们还是遇到过它一本正经地编根因的情况。最典型的一次一条 CPU 高负载告警日志摘要里什么都没有它却在根因里写疑似存在死循环代码逻辑建议排查应用层锁竞争——这个结论完全没有任何数据支撑。原因有两个一是 few-shot 示例给得太具体模型学会了模仿那种给出准确根因的结构却没学会判断信息不足时怎么办二是我们日志摘要给得太短它没有足够材料时就会自行脑补。解决办法是对提示词做了一次大的修订单独加了一个判断分支明确写如果日志摘要为空或没有明显异常关键词根因列表只输出暂无法定位需人工查看日志禁止使用可能疑似之外的推测性词汇。另外把日志摘要的最低长度提到 500 字宁可截取更长时间范围的日志也不让它拿到过于稀疏的上下文。这里我再提醒一点大模型生成内容需要加一道外挂校验。我们会用正则先扫一遍生成结果如果里面出现OOM但日志摘要里根本没有 OOM 字样就强制降级为模板通知不信任它的推理。宁可让人工查也不能让它用编造的结论误导值班判断。5.4 Elasticsearch 查询超时EFK 查询超时也是一个高频坑。出问题的场景是告警来了拦截器去 ES 查日志但查询的时间范围太宽5 分钟索引前缀太宽ES 在大量分片上做扫描耗时会超过我们设定的 3 秒超时。这直接导致上下文为空大模型拿不到日志素材。后来我们优化了查询方式。第一把索引按天拆分filebeat-2025.01.15精确命中当天索引避免跨索引扫描。第二加了一个超时上限 2 秒宁可在高峰期用较短的日志摘要也不能阻塞告警处理链路。第三把原本取最近 30 条日志改成了先按错误级别过滤再取 30 条这样在日志量很大的情况下拿到的 30 条更有信息密度。还要注意 ES 的max_result_window默认是 10000如果你用from size方式分页会踩坑但我们只取前 30 条不会碰到这个限制。真正麻烦的是聚合查询比如你要统计某个错误码在 5 分钟内的出现次数需要合理的bucket_size这块我建议直接做成一个可配置项不同工作负载的日志密度差异极大用一个默认值肯定不合适。5.5 常见问题速查表故障现象可能原因排查步骤解决方案Alertmanager 日志显示发送失败网络策略阻断 / 路径错误检查 Alertmanager 日志中的 HTTP 状态码curl 测试目标地址放通对应端口流量修正 webhook 路径群里没有通知但有告警拦截器服务 OOM / LLM 接口降级查看拦截器 Pod 的LAST STATE和日志调整内存 limit优化并发控制大量重复告警group_by 配置过细 / 去重窗口过短查看 Alertmanager 的发送日志调整group_wait和拦截器去重窗口大模型生成内容与事实不符日志摘要不足 / 提示词缺少约束打印模型输入检查摘要长度和关键词增加摘要内容增加禁止编造约束加正则校验延迟超过 10 秒EFK 查询超时 / LLM 串行调用看拦截器链路中的各阶段耗时调整 EFK 查询超时LLM 并发拉高加缓存大模型 API 费用暴涨告警风暴 / 缺少缓存与聚合查看 Token 消耗和告警数量趋势显式限制并发增加缓存命中率前置规则过滤恢复告警没有通知send_resolved未开启检查 Alertmanager 配置加上send_resolved: true这套东西做下来我个人在实际操作中最深的体会是AIOps 不是装个模型就完事真正的功夫全在告警进到模型之前和出来之后的那些工程细节上。告警标准化、上下文补全、去重聚合、降级兜底这些看起来没那么AI的环节恰恰决定了最终体验是存客观感还是花架子。如果你也在做类似的事情我的建议是先把中间件拆小、把链路指标打好再谈模型效果。最后再分享一个小技巧给拦截器的每个出站通知都加一个原始告警的 source URL比如 Prometheus 的 generatorURL 或者 Kibana 的 Discover 深度链接。这样即使大模型翻译得再好值班同事也能一键跳回原始监控画面核实大家才会真正信任这套系统。这个细节比再好的提示词都能解决问题。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑