当你的排查助手变成了AI:大模型辅助根因分析在线上故障排查中的应用
当你的排查助手变成了AI:大模型辅助根因分析在线上故障排查中的应用
周一早上9点15分,我刚端起咖啡,告警群就炸了——用户服务持续报502,上游三个业务线投诉说登录功能挂了。
传统排查流程大概是:看Grafana → 查日志 → 看APM → 看变更 → 推理 → 定位。这一套下来,熟练工也要15-30分钟。
但今天不一样。我打开内部排查平台(我们叫它OpsCopilot),输入故障服务名,AI已经自动聚合好了异常指标、关联日志和最近变更。三分钟后,它给出了分析建议。
这篇文章就聊聊大模型在线上故障排查中的落地实践——不是画饼,是真的在生产环境跑了大半年的方案。
一、运维排查的信息过载困境
线上故障排查最大障碍不是"没数据",而是"数据太多"。
一次普通的P1故障,你可能需要看:
| 数据来源 | 数据量级 | 关键信息占比 |
|---|---|---|
| Prometheus指标 | 2000+时间序列 | <1% |
| Elasticsearch日志 | 500万条/小时 | <0.1% |
| Jaeger链路追踪 | 10万Span/分钟 | <0.5% |
| K8s事件 | 50条/分钟 | 约5% |
| 变更记录 | 日均50次 | 约10% |
人工从这些数据中找到关键线索,就像大海捞针。
二、AI辅助排查的核心设计
总体思路:思维链+多步推理
我们不是简单地把数据丢给大模型问"怎么回事",而是设计了一套思维链(Chain-of-Thought)的排查流程:
Step 1: 故障定界 → 这个故障影响谁?入口?出口? Step 2: 数据聚合 → 异常指标有哪些?错误日志什么内容? Step 3: 关联分析 → 异常指标和最近变更有关吗? Step 4: 根因推理 → 给出Top 3可能根因 Step 5: 排查指引 → 下一步应该查什么?Step 1:故障定界器
# fault_boundary.py — 故障定界 class FaultBoundaryDetector: """确定故障的影响边界""" def detect_boundary(self, service_name: str, start_time: str) -> dict: # 查询服务拓扑,确定上下游依赖 upstream = self.get_upstream_services(service_name) downstream = self.get_downstream_services(service_name) # 检查各服务的健康状态 boundary = { 'entry_point': service_name, 'affected_upstream': [], 'affected_downstream': [], 'unaffected': [] } for svc in upstream + downstream: status = self.check_service_health(svc, start_time) if status == 'degraded': boundary['affected_downstream' if svc in downstream else 'affected_upstream'].append(svc) else: boundary['unaffected'].append(svc) return boundary # 示例输出 boundary = { 'entry_point': 'user-service', 'affected_upstream': ['api-gateway'], 'affected_downstream': ['user-db'], 'unaffected': ['order-service', 'payment-service'] } # 推理:故障在下游数据库,影响了user-service,进而影响了api-gatewayStep 2:上下文聚合器
# context_aggregator.py — 多源数据聚合 class ContextAggregator: """聚合故障上下文,供LLM分析""" def aggregate(self, service_name: str, boundary: dict, time_range: tuple) -> dict: context = { 'service': service_name, 'boundary': boundary, 'time_window': { 'start': time_range[0].isoformat(), 'end': time_range[1].isoformat() }, 'clues': [] } # 1. 提取异常指标(只提取有异常的,减少噪声) anomaly_metrics = self.get_anomaly_metrics(service_name, time_range) for metric in anomaly_metrics: context['clues'].append({ 'type': 'metric', 'source': 'prometheus', 'summary': f"{metric['name']} 从 {metric['baseline']} 突变为 {metric['current']}", 'severity': metric['severity'] }) # 2. 提取关键错误日志(去重,只保留前5种模式) error_patterns = self.cluster_error_logs(service_name, time_range) for pattern in error_patterns[:5]: context['clues'].append({ 'type': 'log', 'source': 'elasticsearch', 'summary': f"发现{pattern['count']}条同类错误: {pattern['sample']}", 'severity': 'high' }) # 3. 检查最近变更 recent_changes = self.get_recent_changes(service_name, time_range[0] - timedelta(hours=2)) for change in recent_changes: context['clues'].append({ 'type': 'change', 'source': 'cmdb', 'summary': f"{change['operator']} 在 {change['time']} 执行了 {change['action']}", 'severity': change.get('risk', 'low') }) return contextStep 3-5:LLM多步推理引擎
# llm_reasoning.py — LLM推理引擎 import json from openai import AsyncOpenAI class OpsCopilotEngine: """运维AI助手核心推理引擎""" def __init__(self): self.client = AsyncOpenAI( base_url='http://llm-service:8000/v1', api_key='sk-xxxx' ) self.model = 'qwen2-72b-instruct' async def run_diagnosis(self, context: dict) -> str: """执行完整的诊断链路""" # 第一步:关联分析 correlation_prompt = f"""你是一位SRE工程师,正在排查线上故障。 请分析以下线索之间的关联关系: 故障服务:{context['service']} 影响边界:{json.dumps(context['boundary'], ensure_ascii=False)} 时间窗口:{context['time_window']} 发现的线索(按时间排序): {json.dumps(context['clues'], ensure_ascii=False, indent=2)} 请分析: 1. 哪些线索可能是因果链中的一环? 2. 哪些线索可能是独立事件? 3. 线索之间是否存在时间上的先后关系? """ correlation = await self._llm_call(correlation_prompt) # 第二步:根因推理 root_cause_prompt = f"""基于以下关联分析结果和原始线索,给出Top 3可能的根因: 关联分析:{correlation} 线索清单: {json.dumps(context['clues'], ensure_ascii=False, indent=2)} 请按以下格式输出: ## 可能根因(按概率从高到低) ### 根因1:[名称] - 概率:XX% - 证据:[引用线索编号] - 推理过程:[因果链描述] ### 根因2:[名称] - 概率:XX% - 证据:[引用线索编号] - 推理过程:[因果链描述] ### 根因3:[名称] - 概率:XX% - 证据:[引用线索编号] - 推理过程:[因果链描述] ## 推荐排查步骤 1. [第一步做什么] 2. [第二步做什么] 3. [第三步做什么] """ return await self._llm_call(root_cause_prompt) async def _llm_call(self, prompt: str) -> str: """调用大模型""" response = await self.client.chat.completions.create( model=self.model, messages=[ {'role': 'system', 'content': '你是资深SRE工程师,基于数据做严谨推理。'}, {'role': 'user', 'content': prompt} ], temperature=0.1, max_tokens=2000 ) return response.choices[0].message.content三、真实案例:用户服务502故障
故障定界输出
入口: api-gateway(异常) ↓ 目标: user-service(异常) ↓ 依赖: user-db(异常) redis-cluster(正常) 初步结论:故障在下游数据库,影响user-service后传导到api-gatewayAI诊断输出
## 可能根因(按概率从高到低) ### 根因1:数据库连接池耗尽 - 概率:75% - 证据:① user-db活跃连接数从50飙升至200;② user-service日志出现 "cannot acquire JDBC connection";③ 该服务近期未上线新版本 - 推理过程:连接数持续上升 → 达到连接池上限 → 新请求获取不到连接 → 请求阻塞超时 → upstream服务感知到502 ### 根因2:慢SQL导致连接长时间占用 - 概率:15% - 证据:① user-db慢查询数从2/min升至120/min;② 某SQL执行时间从5ms升至2.3s - 推理过程:某SQL执行计划变化 → 全表扫描 → 长时间占用连接 → 有效连接数减少 → 等效于连接池耗尽 ### 根因3:上游突发流量 - 概率:10% - 证据:① api-gateway入口流量突增300%;② 其他下游服务无异常 - 推理过程:突发流量 → 数据库连接数飙升 → 超过连接池阈值 → 部分连接超时 ## 推荐排查步骤 1. 立即执行:kubectl exec -it user-db-0 -- mysql -e "SHOW PROCESSLIST" 查看当前数据库连接状态 2. 定位慢SQL:检查mysql-slow.log中的TOP SQL 3. 如果确认连接池耗尽:临时增大数据库的 max_connections 和应用的连接池大小 4. 长期修复:添加数据库连接数的HPA告警和限流措施实际结果
按照排查指引,第一步执行SHOW PROCESSLIST后,发现大量Waiting for table metadata lock的连接。进一步定位发现是凌晨的一次DDL变更(ALTER TABLE)未提交事务,阻塞了后续所有查询。根因是变更操作未规范执行,这是根因列表中没出现的新线索——AI明确指出了"首次排查方向"。
四、实践经验与局限
做对了的
- 结构化提示词:让AI按固定格式输出,便于后续自动处理
- 多步推理取代单次问答:先关联分析再根因推理,准确率提升约40%
- 约束输出范围:temperature设为0.1,减少幻觉
踩过的坑
坑1:AI过度自信 - 现象:明明证据不足,LLM也会强行给出分析 - 解决:在Prompt中增加"如果证据不足,请明确指出不确定项" 坑2:上下文过长 - 现象:全量日志和指标数据远超LLM上下文窗口 - 解决:先做聚类和摘要,只把关键线索送入LLM 坑3:延迟不稳定 - 现象:LLM API偶尔延迟到30s+ - 解决:增加超时处理和降级策略,超时则只展示聚合数据五、落地效果
过去6个月的生产数据统计:
| 指标 | 使用AI前 | 使用AI后 |
|---|---|---|
| MTTR(平均修复时间) | 28min | 12min |
| 首次排查正确率 | 62% | 78% |
| 需要升级专家的故障比例 | 35% | 18% |
| 值班工程师满意度 | 6.5/10 | 8.5/10 |
结语
大模型辅助排查不是要把运维工程师"优化掉"。它做的是把信息搜索和初步分析的时间从20分钟压缩到2分钟,让你能把更多精力花在"判断"和"决策"上。
就像自动驾驶一样——L2级别的辅助,仍然需要你手握方向盘、时刻关注路况。但有了辅助,你会开得更轻松、更安全。
本文作者:侯万里(万里侯),云原生运维工程师,专注于AI驱动运维智能化和故障自愈体系建设
