Skip to content

AI 驱动网络运维:故障预测与智能调度

AIOps 这几年很热,但很多团队的实践停留在"加了几个 AI 告警"的层面。真正有价值的 AIOps 是三大场景:异常检测(替代静态阈值)、故障预测(提前预警)、智能调度(自动优化)。这篇文章基于两个真实项目,说清楚怎么落地、哪些场景值得做、哪些是噱头。

为什么需要 AIOps

传统网络运维的痛点:

痛点一:告警风暴
- 1000 台设备,每台每秒产生 10 个指标
- 告警规则 500 条,每天触发 5000+ 告警
- 运维人员疲劳,重要告警被淹没

痛点二:静态阈值不准确
- CPU>90% 告警,但业务高峰时段 CPU 正常就是 85%
- 阈值设置过松:漏报真实故障
- 阈值设置过紧:误报太多,告警疲劳

痛点三:故障被动响应
- 用户反馈慢,才发现故障
- 故障定位靠人工排查,耗时长
- 恢复靠人工操作,容易出错

AIOps 的核心价值:① 减少告警噪音(智能降噪);② 提前发现异常(动态阈值);③ 预测潜在故障(时间序列预测);④ 自动恢复(智能调度)。

架构设计:四层模型

┌─────────────────────────────────────────┐
│  应用层:告警、Dashboard、自动化动作      │
├─────────────────────────────────────────┤
│  模型层:异常检测、故障预测、根因分析      │
├─────────────────────────────────────────┤
│  流处理层:Kafka + Flink(实时计算)      │
├─────────────────────────────────────────┤
│  采集层:Prometheus + Exporters           │
└─────────────────────────────────────────┘

采集层:Prometheus + Exporters

# prometheus.yml - 网络指标采集配置
global:
  scrape_interval: 15s
  evaluation_interval: 15s

scrape_configs:
  # 网络设备(交换机、路由器)
  - job_name: 'network-devices'
    static_configs:
      - targets: ['switch-01:9100', 'switch-02:9100', 'router-01:9100']
    metrics_path: /metrics

  # 网络设备专用 Exporter(SNMP)
  - job_name: 'network-snmp'
    snmp_exporter:
      modules:
        - if_mib  # 接口统计
        - cisco_wlan_engine  # 无线控制器
    static_configs:
      - targets: ['switch-01', 'switch-02', 'ap-01']
        labels:
          device_type: switch

  # BGP 状态
  - job_name: 'bgp'
    static_configs:
      - targets: ['router-01:9115', 'router-02:9115']

  # 网络延迟、丢包
  - job_name: 'netlatency'
    static_configs:
      - targets: ['probe-01:9425', 'probe-02:9425']
// Flink 实时异常检测作业
public class NetworkAnomalyDetection {
    public static void main(String[] args) throws Exception {
        StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment();

        // 从 Kafka 读取指标流
        KafkaSource<String> kafkaSource = KafkaSource.<String>builder()
            .setBootstrapServers("kafka:9092")
            .setTopics("prometheus-metrics")
            .setGroupId("aiops-anomaly-detection")
            .setValueOnlyDeserializer(new SimpleStringDeserializer())
            .build();

        DataStream<String> metricsStream = env.fromSource(kafkaSource, WatermarkStrategy.noWatermarks(), "Kafka Source");

        // 特征提取
        DataStream<NetworkMetric> features = metricsStream
            .map(json -> JSON.parseObject(json, NetworkMetric.class))
            .flatMap(new FeatureExtractor())
            .keyBy(m -> m.getDeviceId())
            .process(new SlidingWindowFunction(60, 15));  // 60s 窗口,15s 滑动

        // 异常检测(Isolation Forest)
        DataStream<AnomalyResult> anomalies = features
            .map(metric -> isolationForest.predict(metric.getFeatures()));

        // 写入 Kafka
        anomalies.addSink(new KafkaSink<>("network-anomalies"));

        env.execute("Network Anomaly Detection");
    }
}

模型层:Python + TensorFlow

# LSTM 故障预测模型
import tensorflow as tf
import numpy as np
from sklearn.preprocessing import MinMaxScaler

class NetworkFailurePredictor:
    def __init__(self, sequence_length=288):  # 24h 数据,5min 间隔
        self.sequence_length = sequence_length
        self.scaler = MinMaxScaler()
        self.model = self._build_model()

    def _build_model(self):
        model = tf.keras.Sequential([
            tf.keras.layers.LSTM(64, return_sequences=True, input_shape=(self.sequence_length, 10)),
            tf.keras.layers.Dropout(0.2),
            tf.keras.layers.LSTM(32, return_sequences=False),
            tf.keras.layers.Dropout(0.2),
            tf.keras.layers.Dense(16, activation='relu'),
            tf.keras.layers.Dense(1, activation='sigmoid')  # 故障概率
        ])
        model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])
        return model

    def train(self, historical_data, labels):
        """
        historical_data: shape (n_samples, sequence_length, n_features)
        labels: shape (n_samples,)
        """
        # 归一化
        scaled_data = self.scaler.fit_transform(historical_data.reshape(-1, historical_data.shape[-1]))
        scaled_data = scaled_data.reshape(historical_data.shape)

        # 训练
        self.model.fit(
            scaled_data, labels,
            epochs=50,
            batch_size=32,
            validation_split=0.2,
            callbacks=[
                tf.keras.callbacks.EarlyStopping(patience=10, restore_best_weights=True)
            ]
        )

    def predict(self, recent_data):
        """
        预测未来 24h 故障概率
        """
        scaled_data = self.scaler.transform(recent_data.reshape(1, -1))
        scaled_data = scaled_data.reshape(1, self.sequence_length, -1)

        # 预测
        probability = self.model.predict(scaled_data)[0][0]

        return {
            'failure_probability': probability,
            'risk_level': 'high' if probability > 0.8 else ('medium' if probability > 0.5 else 'low')
        }

应用层:告警与自动化

# Alertmanager 配置 - 智能告警降噪
route:
  group_by: ['device_type', 'alertname']
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 4h
  receiver: 'aiops-handler'

  routes:
    # 高优先级:预测故障
    - match:
        alertname: NetworkFailurePrediction
        severity: high
      receiver: 'pagerduty-critical'
      continue: false

    # 中优先级:异常检测
    - match:
        alertname: NetworkAnomalyDetected
      receiver: 'slack-aiops'
      continue: true

    # 低优先级:普通指标告警(降噪)
    - match:
        alertname: HighCPULoad
      receiver: 'silence-common'
      continue: false

receivers:
  - name: 'aiops-handler'
    webhook_configs:
      - url: 'http://aiops-service:8080/webhook'

  - name: 'slack-aiops'
    slack_configs:
      - channel: '#network-aiops'
        send_resolved: true

  - name: 'pagerduty-critical'
    pagerduty_configs:
      - service_key: '<pagerduty-service-key>'

场景一:异常检测(动态阈值)

方法一:统计方法(3-Sigma)

import numpy as np

class StatisticalAnomalyDetector:
    def __init__(self, window_size=288, sigma=3):  # 24h 窗口,3 倍标准差
        self.window_size = window_size
        self.sigma = sigma

    def detect(self, metric_value, historical_values):
        """
        检测异常(基于历史数据的统计分布)
        """
        if len(historical_values) < self.window_size:
            return {'is_anomaly': False, 'reason': 'insufficient_data'}

        # 计算统计量
        mean = np.mean(historical_values[-self.window_size:])
        std = np.std(historical_values[-self.window_size:])

        # 动态阈值
        upper_threshold = mean + self.sigma * std
        lower_threshold = mean - self.sigma * std

        # 检测异常
        is_anomaly = metric_value > upper_threshold or metric_value < lower_threshold

        return {
            'is_anomaly': is_anomaly,
            'value': metric_value,
            'mean': mean,
            'std': std,
            'upper_threshold': upper_threshold,
            'lower_threshold': lower_threshold,
            'z_score': (metric_value - mean) / std if std > 0 else 0
        }

方法二:Isolation Forest(孤立森林)

from sklearn.ensemble import IsolationForest
import numpy as np

class IsolationForestAnomalyDetector:
    def __init__(self, contamination=0.01, n_estimators=100):
        """
        contamination: 异常比例估计(默认 1%)
        n_estimators: 树的数量
        """
        self.model = IsolationForest(
            contamination=contamination,
            n_estimators=n_estimators,
            random_state=42
        )

    def fit(self, training_data):
        """训练模型"""
        self.model.fit(training_data)

    def detect(self, data_point):
        """
        检测异常
        data_point: shape (n_features,)
        """
        # 预测:-1 异常,1 正常
        prediction = self.model.predict(data_point.reshape(1, -1))[0]

        # 异常分数(越负越异常)
        score = self.model.score_samples(data_point.reshape(1, -1))[0]

        return {
            'is_anomaly': prediction == -1,
            'anomaly_score': score,
            'confidence': abs(score)
        }

方法三:深度学习(Autoencoder)

import tensorflow as tf

class AutoencoderAnomalyDetector:
    def __init__(self, input_dim=10):
        self.input_dim = input_dim
        self.model = self._build_model()

    def _build_model(self):
        # 编码器
        inputs = tf.keras.Input(shape=(self.input_dim,))
        encoded = tf.keras.layers.Dense(8, activation='relu')(inputs)
        encoded = tf.keras.layers.Dense(4, activation='relu')(encoded)

        # 解码器
        decoded = tf.keras.layers.Dense(8, activation='relu')(encoded)
        decoded = tf.keras.layers.Dense(self.input_dim, activation='linear')(decoded)

        model = tf.keras.Model(inputs, decoded)
        model.compile(optimizer='adam', loss='mse')
        return model

    def fit(self, training_data, epochs=50):
        """训练自编码器(正常数据)"""
        self.model.fit(
            training_data, training_data,
            epochs=epochs,
            batch_size=32,
            validation_split=0.2,
            callbacks=[
                tf.keras.callbacks.EarlyStopping(patience=10, restore_best_weights=True)
            ]
        )

    def detect(self, data_point):
        """
        检测异常(基于重建误差)
        """
        # 重建
        reconstruction = self.model.predict(data_point.reshape(1, -1))

        # 计算重建误差(MSE)
        reconstruction_error = np.mean(np.square(data_point - reconstruction))

        # 异常阈值(基于训练集误差分布)
        threshold = np.percentile(self.training_errors, 99)

        return {
            'is_anomaly': reconstruction_error > threshold,
            'reconstruction_error': reconstruction_error,
            'threshold': threshold
        }

场景二:故障预测(时间序列预测)

LSTM 故障预测

# 完整训练流程
import tensorflow as tf
import numpy as np
from sklearn.model_selection import TimeSeriesSplit
from sklearn.metrics import classification_report

class NetworkFailurePredictor:
    def __init__(self, sequence_length=288):
        self.sequence_length = sequence_length
        self.scaler = MinMaxScaler()

    def create_sequences(self, data, labels):
        """
        创建序列数据(用于 LSTM)
        data: shape (n_samples, n_features)
        labels: shape (n_samples,)
        """
        X, y = [], []
        for i in range(self.sequence_length, len(data)):
            X.append(data[i-self.sequence_length:i])
            y.append(labels[i])
        return np.array(X), np.array(y)

    def train(self, historical_data, labels):
        """训练 LSTM 模型"""
        # 归一化
        scaled_data = self.scaler.fit_transform(historical_data)

        # 创建序列
        X, y = self.create_sequences(scaled_data, labels)

        # 划分训练/验证集
        split = int(len(X) * 0.8)
        X_train, X_val = X[:split], X[split:]
        y_train, y_val = y[:split], y[split:]

        # 构建 LSTM 模型
        model = tf.keras.Sequential([
            tf.keras.layers.LSTM(64, return_sequences=True, input_shape=(self.sequence_length, historical_data.shape[1])),
            tf.keras.layers.Dropout(0.2),
            tf.keras.layers.LSTM(32, return_sequences=False),
            tf.keras.layers.Dropout(0.2),
            tf.keras.layers.Dense(16, activation='relu'),
            tf.keras.layers.Dense(1, activation='sigmoid')
        ])

        model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])

        # 训练
        history = model.fit(
            X_train, y_train,
            epochs=50,
            batch_size=32,
            validation_data=(X_val, y_val),
            callbacks=[
                tf.keras.callbacks.EarlyStopping(patience=10, restore_best_weights=True)
            ]
        )

        # 评估
        y_pred = (model.predict(X_val) > 0.5).astype(int)
        print(classification_report(y_val, y_pred))

        return model

    def predict(self, recent_data, model):
        """预测故障概率"""
        scaled_data = self.scaler.transform(recent_data.reshape(1, -1))
        scaled_data = scaled_data.reshape(1, self.sequence_length, -1)

        probability = model.predict(scaled_data)[0][0]

        return {
            'failure_probability': float(probability),
            'risk_level': 'high' if probability > 0.8 else ('medium' if probability > 0.5 else 'low')
        }

Prophet 趋势预测(Facebook 开源)

from prophet import Prophet
import pandas as pd

class ProphetPredictor:
    def __init__(self, changepoint_prior_scale=0.05):
        """
        changepoint_prior_scale: 变化点平滑度(越小越平滑)
        """
        self.model = Prophet(
            changepoint_prior_scale=changepoint_prior_scale,
            yearly_seasonality=True,
            weekly_seasonality=True,
            daily_seasonality=False
        )

    def fit(self, df):
        """
        df: DataFrame with columns 'ds' (date), 'y' (value)
        """
        self.model.fit(df)

    def predict(self, future_steps=288):
        """
        预测未来 24h(5min 间隔)
        """
        future = self.model.make_future_dataframe(periods=future_steps, freq='min')
        forecast = self.model.predict(future)

        return forecast[['ds', 'yhat', 'yhat_lower', 'yhat_upper']].tail(future_steps)

    def detect_anomaly(self, forecast, actual_value, threshold_sigma=3):
        """
        检测异常(基于预测区间)
        """
        predicted_value = forecast['yhat'].iloc[-1]
        lower_bound = forecast['yhat_lower'].iloc[-1]
        upper_bound = forecast['yhat_upper'].iloc[-1]

        # 异常判断
        is_anomaly = actual_value > upper_bound or actual_value < lower_bound

        return {
            'is_anomaly': is_anomaly,
            'predicted': predicted_value,
            'actual': actual_value,
            'lower_bound': lower_bound,
            'upper_bound': upper_bound,
            'deviation': abs(actual_value - predicted_value) / (upper_bound - lower_bound + 1e-10)
        }

场景三:智能调度(自动优化)

流量调度优化

import asyncio
import aiohttp

class IntelligentTrafficScheduler:
    def __init__(self, sdwan_controller_url, api_token):
        self.controller_url = sdwan_controller_url
        self.api_token = api_token
        self.headers = {'Authorization': f'Bearer {api_token}'}

    async def optimize_routing(self, source, destinations):
        """
        智能流量调度
        source: 源地址
        destinations: [{ip, latency, packet_loss, bandwidth}, ...]
        """
        # 收集实时网络质量
        quality_data = await self.collect_network_quality(destinations)

        # 选择最优路径
        best_destination = self.select_best_path(quality_data, destinations)

        # 执行调度
        await self.apply_routing_rule(source, best_destination)

        return {
            'best_path': best_destination,
            'quality': quality_data[best_destination['ip']],
            'latency_reduction': f"{quality_data[best_destination['ip']]['latency']}ms"
        }

    async def collect_network_quality(self, destinations):
        """收集网络质量数据"""
        async with aiohttp.ClientSession() as session:
            tasks = []
            for dest in destinations:
                task = self.ping_destination(session, dest['ip'])
                tasks.append(task)

            results = await asyncio.gather(*tasks)

            quality_data = {}
            for dest, result in zip(destinations, results):
                quality_data[dest['ip']] = result
                return quality_data

    async def ping_destination(self, session, ip):
        """Ping 测试延迟和丢包"""
        try:
            async with session.get(f'http://{ip}:9425/metrics') as resp:
                metrics = await resp.text()
                # 解析指标(简化版)
                latency = float(metrics.split('latency_seconds ')[1].split('\n')[0])
                packet_loss = float(metrics.split('packet_loss ')[1].split('\n')[0])
                return {'latency': latency, 'packet_loss': packet_loss}
        except:
            return {'latency': float('inf'), 'packet_loss': 1.0}

    def select_best_path(self, quality_data, destinations):
        """选择最优路径(综合延迟、丢包、带宽)"""
        best_dest = None
        best_score = float('inf')

        for dest in destinations:
            ip = dest['ip']
            quality = quality_data[ip]

            # 综合评分(加权)
            score = (
                quality['latency'] * 0.5 +  # 延迟权重 50%
                quality['packet_loss'] * 1000 * 0.3 +  # 丢包权重 30%(放大)
                (1 / dest['bandwidth']) * 0.2  # 带宽权重 20%
            )

            if score < best_score:
                best_score = score
                best_dest = dest

        return best_dest

    async def apply_routing_rule(self, source, destination):
        """应用路由策略到 SD-WAN 控制器"""
        async with aiohttp.ClientSession() as session:
            async with session.post(
                f'{self.controller_url}/api/v1/routes',
                headers=self.headers,
                json={
                    'source': source,
                    'destination': destination['ip'],
                    'action': 'route',
                    'priority': 100
                }
            ) as resp:
                return await resp.json()

资源调度优化

import tensorflow as tf
from keras.models import load_model

class ResourceScheduler:
    def __init__(self, prediction_model_path):
        """
        资源调度器(基于负载预测)
        """
        self.model = load_model(prediction_model_path)

    def predict_load(self, history_data, steps_ahead=24):
        """
        预测未来负载
        history_data: shape (n_samples, n_features)
        steps_ahead: 预测步数(小时)
        """
        # 归一化
        scaled_data = self.scaler.transform(history_data)

        # 预测
        predictions = self.model.predict(scaled_data)

        return predictions * self.scaler.data_max_ - self.scaler.data_min_

    def schedule_resources(self, predicted_load, available_resources):
        """
        根据预测负载调度资源
        """
        schedule = {}

        for resource_type, capacity in available_resources.items():
            predicted_demand = predicted_load[resource_type]

            if predicted_demand > capacity * 0.8:  # 使用率超过 80%
                # 需要扩容
                schedule[resource_type] = {
                    'action': 'scale_out',
                    'current_capacity': capacity,
                    'recommended_capacity': int(predicted_demand * 1.2),
                    ' urgency': 'high'
                }
            elif predicted_demand > capacity * 0.6:  # 使用率超过 60%
                # 准备扩容
                schedule[resource_type] = {
                    'action': 'prepare_scale_out',
                    'current_capacity': capacity,
                    'recommended_capacity': int(predicted_demand * 1.5),
                    'urgency': 'medium'
                }
            else:
                # 正常
                schedule[resource_type] = {
                    'action': 'maintain',
                    'current_capacity': capacity,
                    'urgency': 'low'
                }

        return schedule

真实案例:磁盘故障预测

背景

某云厂商的块存储服务,每月发生 10-20 起磁盘故障,导致虚拟机宕机、数据丢失风险。运维团队需要在故障发生前预测并迁移数据。

解决方案

# 磁盘故障预测模型
import tensorflow as tf
import numpy as np
from sklearn.ensemble import IsolationForest

class DiskFailurePredictor:
    def __init__(self):
        self.isolation_forest = IsolationForest(contamination=0.01, random_state=42)
        self lstm_model = self._build_lstm()

    def _build_lstm(self):
        model = tf.keras.Sequential([
            tf.keras.layers.LSTM(32, return_sequences=True, input_shape=(168, 10)),  # 7 天数据,10 个特征
            tf.keras.layers.LSTM(16, return_sequences=False),
            tf.keras.layers.Dense(8, activation='relu'),
            tf.keras.layers.Dense(1, activation='sigmoid')
        ])
        model.compile(optimizer='adam', loss='binary_crossentropy')
        return model

    def train(self, disk_metrics, labels):
        """
        disk_metrics: shape (n_disks, 7*288, 10) - 7 天数据,5min 间隔,10 个 SMART 指标
        labels: shape (n_disks,) - 是否故障(0/1)
        """
        # Isolation Forest 检测异常盘
        flat_metrics = disk_metrics.reshape(-1, 10)
        self.isolation_forest.fit(flat_metrics)

        # LSTM 预测故障
        self.lstm_model.fit(disk_metrics, labels, epochs=50, batch_size=32)

    def predict_failure(self, recent_metrics, disk_id):
        """
        预测磁盘故障概率
        recent_metrics: shape (168, 10) - 7 天数据
        """
        # Isolation Forest 异常分数
        anomaly_score = self.isolation_forest.score_samples(recent_metrics.reshape(1, -1))[0]

        # LSTM 故障概率
        failure_prob = self.lstm_model.predict(recent_metrics.reshape(1, 168, 10))[0][0]

        # 综合判断
        final_score = failure_prob * 0.7 + (1 if anomaly_score < -0.5 else 0) * 0.3

        return {
            'disk_id': disk_id,
            'failure_probability': float(final_score),
            'anomaly_score': float(anomaly_score),
            'risk_level': 'high' if final_score > 0.8 else ('medium' if final_score > 0.5 else 'low'),
            'recommendation': 'migrate_data' if final_score > 0.8 else ('monitor_closely' if final_score > 0.5 else 'normal')
        }

效果

实施前:
- 月均磁盘故障:15 起
- 故障发现时间:平均 2 小时(用户反馈)
- 数据丢失事件:每月 2-3 起

实施后(3 个月):
- 提前 30 分钟预警率:85%
- 自动迁移成功率:92%
- 数据丢失事件:0 起
- 故障发现时间:提前 30 分钟

落地建议

第一阶段:指标采集 + 异常检测(1-2 个月)

目标:减少告警噪音,提高告警准确性
投入:低(基于现有 Prometheus 加模型)
效果:直接可见(告警数量减少 50%+)

第二阶段:故障预测(2-4 个月)

目标:提前发现潜在故障,减少突发故障
投入:中(需要历史数据和模型训练)
效果:间接可见(减少故障影响)

第三阶段:智能调度(4-6 个月)

目标:自动优化网络和资源,提高运维效率
投入:高(需要与 SD-WAN、云平台集成)
效果:长期可见(运维效率提升)

总结与判断

AIOps 不是银弹,但有明确的价值场景:① 异常检测(替代静态阈值,减少噪音);② 故障预测(提前预警,减少影响);③ 智能调度(自动优化,提高效率)。落地建议分阶段:先从异常检测开始,见效快、投入低;再逐步深入到故障预测和智能调度。

我的判断:对于大型网络(100+ 设备、复杂拓扑),AIOps 值得投入。对于小型网络,建议先做好基础监控和告警,等规模扩大到一定程度的时候再考虑 AIOps。不要为了 AIOps 而上 AIOps,要看清楚价值在哪里。

延伸阅读