当你的排查助手变成了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-gateway

Step 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 context

Step 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-gateway

AI诊断输出

## 可能根因(按概率从高到低)

### 根因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明确指出了"首次排查方向"。

四、实践经验与局限

做对了的

  1. 结构化提示词:让AI按固定格式输出,便于后续自动处理
  2. 多步推理取代单次问答:先关联分析再根因推理,准确率提升约40%
  3. 约束输出范围: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驱动运维智能化和故障自愈体系建设

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐