
智能运维新实践:Prometheus 与 DeepSeek 联动实现告警自动分析 原创
前阵子,一位运维学员向我倾诉了他的困扰:日常工作中,Prometheus 虽能精准捕捉系统参数异常并告警,但生成的报警信息往往只是冰冷的指标数据。每次遇到异常,他都得手动把这些信息复制粘贴到 DeepSeek 里去询问分析建议,不仅操作繁琐,后续也难形成规整的存档用于技术沉淀。要是能让报警后自动生成带分析的智能报表,那该多方便高效啊!
在充分了解他的这一需求后,我给出了一套解决方案 —— 借助 Prometheus Server 与 Alertmanager 的告警机制,再结合 DeepSeek 的 API,让 AI 自动对告警信息深度分析并生成智能报表。实际验证下来,这套方案效果很不错,所以我打算把完整的思路和实验过程写成文章,分享给更多同行。
整体思路
在开始实践之前,我想先整理一下整体思路,这里我将其分为如下四个步骤,同时也用一张大图对其进行描述如下:
首先是 “生成指标数据”,它是一切的起点,为后续的监控与分析提供最基础的 “素材”,这些数据可以是系统的各项运行参数,也可以是业务运行的参数等。
接着进入 “采集数据、异常定义、异常检测、转发报警” 环节。系统会采集前期生成的指标数据,同时提前明确什么样的数据属于异常情况,之后对数据进行检测,一旦发现符合异常定义的数据,就会将其转化为告警并转发出去。
然后是 “处理报警” 环节,会对接收到的转发告警进行处理,确保告警信息能被有效承接,为后续的智能分析做好准备。
最后是 “生成智能报告” 环节,借助像 DeepSeek 这样的 AI 能力,对告警等信息进行深度分析,最终产出更具价值、能够辅助决策的智能报告。
上面的文字描述了上图中靠上方的四个实心框体的内容,并且按照箭头从左到右的顺序介绍了整个实践的执行顺序。接着,我们解释靠下方的四个虚线框,他们会完成具体操作,涉及到服务、代码、系统以及他们之间的交互。
在整个实践中,我们需要监控一个应用系统中用户登陆的情况,当发现登陆异常以后会通过 DeepSeek 分析异常信息,生成异常分析报表,方便我们分析异常以及采取下一步行动。
整个实践流程环环相扣,按照顺序执行。首先,用custom_exporter.py利用 Prometheus Client 的 SDK生成登录相关指标数据,为后续监控提供基础。接着,Prometheus 采集这些登录数据,并提前定义好登录异常的规则,一旦检测到异常,就会将告警转发给Alertmanager。随后,Alertmanager 接收来自 Prometheus 的告警并进行处理。最后,利用alert_handler.py调用 DeepSeek 大模型,基于告警信息生成智能的异常分析报表,把冰冷的监控数据转化为更易理解、能辅助决策的智能报告,让监控从单纯的告警通知,升级为具备分析和解读能力的智能运维工具。
Docker 安装 Prometheus
在实践的开始,需要安装 Prometheus 保证基本的数据采集功能。这里我们采用 Docker 安装,它能快速且一致地搭建起实验所需的运行环境。它可将 Prometheus 及其依赖打包成镜像,让我们在不同环境下都能便捷部署,无需操心复杂的依赖配置与环境差异问题,同时也便于后续对容器进行管理、版本切换以及迁移等操作。
确保系统安装 Docker ,在命令行执行如下指令
docker run --name prometheus -d -p 127.0.0.1:9090:9090 prom/prometheus
这里对指令稍加解释如下:
- docker run:创建并启动容器;
- --name prometheus:指定容器名为prometheus;
- -d:后台运行容器,不阻塞终端;
- -p 127.0.0.1:9090:9090:将容器内 9090 端口(Prometheus 默认端口)映射到宿主机 127.0.0.1 的 9090 端口,仅允许本地访问;
- prom/prometheus:使用官方 Prometheus 镜像,本地无镜像时会自动从 Docker Hub 拉取。
这里建议大家通过 Docker Desktop 的日志检查容器是否启动成功。
当然,此时也可以通过 Web UI 访问 Prometheus,不过此时还没有任何数据采集上来。
采集数据
安装好 Prometheus 之后,我们需要利用它采集指标数据。Prometheus 的设计理念是 “拉取”(Pull)数据,而非 “推送”(Push)。最常用的方式是通过各种 Exporter 将数据以 Prometheus 支持的格式(OpenMetrics)暴露出来,再配置 Prometheus 去拉取。
指标数据(Metrics)
这里需要采集的指标数据,需要做一个简要说明,在IT监控领域,指标数据(Metrics) 是系统运行状态和业务活动最核心的量化反映。指标数据的本质包含如下几个方面:
- 数值化测量:指标是数值(例如:CPU 使用率 75%、每秒处理请求数 1200、过去5分钟登录失败次数 15、当前内存占用 4.2GB)。
- 时间序列:指标数据是按时间顺序采集并存储的一系列数据点。每个点包含一个时间戳(何时采集)和一个指标值(当时的状态)。
- 核心目的:
- 监控健康状况:实时了解系统资源(CPU、内存、磁盘、网络)是否充足,服务是否可用。
- 追踪性能表现:衡量系统处理能力(吞吐量、请求速率)和响应速度(延迟)。
- 洞察业务行为:记录用户活动(如登录次数、订单量)、业务流程状态等。
- 诊断问题根源:当系统异常或性能下降时,指标的变化趋势和关联性是定位问题的关键线索。
- 支持决策与规划:基于历史趋势预测容量需求,评估优化效果。
简单来说:指标数据就是将复杂的系统运行情况和业务活动,转化为可存储、可计算、可比较、可告警的数字信号,是我们观察、理解和优化系统的“数据基石”。
关键要素预览:
- 指标名称 (Metric Name):告诉你测量的是什么(例如:cpu_usage,http_requests_total)。
- 指标值 (Value):具体的测量数值。
- 时间戳 (Timestamp):测量发生的时间点。
- 维度/标签 (Dimensions/Labels/Tags):(这是理解复杂系统的关键!)一组键值对,用于细分和丰富指标的含义。例如,一个总的“登录次数”指标,可以通过标签细分为“管理员登录成功次数”、“普通用户登录失败次数”等。标签让一个指标能描述无数种具体场景。
本例中,我们让指标数据 LOGIN_COUNT,用来描述“用户登录”的业务行为。定义内容如下:
LOGIN_COUNT = Counter(
'user_login_total', # 指标名称 (Metric Name)
'用户登录总次数及状态统计', # 指标描述 (Help Text)
['user_type', 'login_status', 'ip_region'] # 标签/维度 (Labels/Dimensions)
)
这里我们对定义的指标进行简单讲解:
1. 指标名称 (user_login_total):
这是指标的唯一标识符,代表了被测量的是什么。表明这个指标记录的是“用户登录事件”发生的总次数。它是一个 Counter(计数器)。这意味着:
- 它的值只能单调增加(或者重置为0后重新增加)。每次发生一次登录事件,这个值就会+1。
- 你通常不会直接关注它的绝对值(比如当前值是 12543),而是关注它在一段时间内的变化速率(如:rate(user_login_total[5m])表示过去5分钟内每秒的平均登录次数)或一段时间内的增量(如:increase(user_login_total[1h])表示过去1小时内的总登录次数)。
2. 指标描述 (用户登录总次数及状态统计):
为指标提供人类可读的解释,说明这个指标具体测量什么内容。这段描述明确指出,这个计数器统计的是用户登录行为发生的总次数,并且会按不同状态进行统计。
3. 标签/维度 (['user_type', 'login_status', 'ip_region']):
标签/维度为指标提供了上下文和细分维度。它们允许你将一个总的登录次数指标,切割成无数个更细粒度的、具有特定含义的时间序列。
- user_type: 标识登录用户的类型。可能的取值示例:"admin","guest"。这个标签让你能区分不同类别用户的登录行为(例如,管理员登录次数 vs 普通用户登录次数)。
- login_status: 标识登录尝试的结果状态。可能的取值示例:"success","failure"。这是你例子中特别强调的规则用到的标签。这个标签让你能清晰地看到成功登录、失败登录等不同状态的发生次数(例如,监控登录失败率)。
- ip_region: 标识登录请求来源的地理区域。
安装 Prometheus Client
在理解了 Prometheus 的数据采集模式(拉取 Pull)和采集的数据内容(指标数据)之后,我们需要在应用或者系统中安装 prometheus-client 用以数据的生成或者采集。
通过如下命令安装prometheus-client:
pip install prometheus-client
创建 Prometheus Client 应用
完成安装之后,需要利用prometheus-client 提供的 SDK 生成数据,用来模拟我们要采集的指标数据。在实际操作中可以在用户目录(选择你觉得合适的目录)下创建一个 Docker 目录,并在 Docker 目录下创建 promethues 目录,接着创建custom_exporter.py 文件。
大致的目录结构如下图所示,后面我们会在promethues 目录下面放入实践需要的脚本、服务、日志文件等信息。
创建的 custom_exporter.py 文件内容如下:
from prometheus_client import start_http_server, Counter
import random
import time
# 定义一个计数器类型的指标(累计值,适合记录次数)
# 标签说明:
# - user_type: 用户类型(普通用户/管理员)
# - login_status: 登录状态(成功/失败)
# - ip_region: IP所属地区(模拟分布式用户场景)
LOGIN_COUNT = Counter(
'user_login_total', # 指标名(遵循Prometheus命名规范:小写+下划线)
'用户登录总次数及状态统计', # 指标描述
['user_type', 'login_status', 'ip_region'] # 标签(用于多维度筛选)
)
def simulate_login():
"""模拟用户登录行为(每3秒产生一次登录事件)"""
# 模拟用户类型(70%普通用户,30%管理员)
user_type = random.choices(['normal', 'admin'], weights=[0.7, 0.3])[0]
# 模拟登录状态(90%成功,10%失败)
login_status = random.choices(['success', 'failed'], weights=[0.9, 0.1])[0]
# 模拟IP地区(国内主要城市)
ip_region = random.choice(['beijing', 'shanghai', 'guangzhou', 'shenzhen', 'hangzhou'])
# 记录一次登录事件(计数器+1)
LOGIN_COUNT.labels(
user_type=user_type,
login_status=login_status,
ip_reginotallow=ip_region
).inc() # 每次调用+1
if __name__ == '__main__':
# 启动HTTP服务,暴露指标在9091端口
start_http_server(9091)
print("用户登录监控Exporter运行在 http://localhost:9091/metrics")
# 持续模拟登录事件
while True:
simulate_login()
time.sleep(3) # 每3秒模拟一次登录
这里我们对上面代码进行简单解读,有这个代码在后面会因为测试进行修改,所以还会出现,届时再对修改部分进行进一步解释。
这段 Python 代码创建了一个模拟用户登录行为的监控数据采集器,它会持续生成登录事件数据并通过 Prometheus 格式暴露出来。以下是关键组件解析:
1. 核心功能:模拟用户登录行为
def simulate_login():
# 随机生成用户类型(70%普通用户,30%管理员)
user_type = random.choices(['normal', 'admin'], weights=[0.7, 0.3])[0]
# 随机生成登录结果(90%成功,10%失败)
login_status = random.choices(['success', 'failed'], weights=[0.9, 0.1])[0]
# 随机选择IP地区(国内主要城市)
ip_region = random.choice(['beijing', 'shanghai', 'guangzhou', 'shenzhen', 'hangzhou'])
# 记录登录事件
LOGIN_COUNT.labels(
user_type=user_type,
login_status=login_status,
ip_reginotallow=ip_region
).inc() # 计数器+1
- 每3秒模拟一次用户登录事件(通过time.sleep(3)控制)
- 使用随机数生成器创建真实场景中的用户行为分布
- 模拟了业务系统中的关键维度:用户身份、操作结果、地理位置
2. 指标定义:多维度登录计数器
LOGIN_COUNT = Counter(
'user_login_total', # 指标名称
'用户登录总次数及状态统计', # 指标描述
['user_type', 'login_status', 'ip_region'] # 三维度标签
)
- 类型:Counter(计数器),只增不减
- 名称:user_login_total(符合Prometheus命名规范)
- 三维度标签:
A.user_type:区分用户身份(普通用户/管理员)
B.login_status:记录登录结果(成功/失败)
C.ip_region:标记用户地理位置
3. 数据暴露:Prometheus 采集接口
if __name__ == '__main__':
start_http_server(9091) # 启动指标暴露服务
print("服务运行在 http://localhost:9091/metrics")
while True:
simulate_login()
time.sleep(3)
- 启动 HTTP 服务(端口 9091)
- 通过/metrics端点提供标准 Prometheus 格式数据
- 持续运行,每3秒产生一个新数据点
完成代码编写之后,执行custom_exporter.py 文件如下:
python ~/docker/prometheus/custom_exporter.py
看到如下结果:
Exporter运行在 http://localhost:9091/metrics
说明 customer exporter 开始运行,访问对应的地址得到如下结果:
# HELP python_gc_objects_collected_total Objects collected during gc
# TYPE python_gc_objects_collected_total counter
python_gc_objects_collected_total{generatinotallow="0"} 255.0
python_gc_objects_collected_total{generatinotallow="1"} 118.0
python_gc_objects_collected_total{generatinotallow="2"} 0.0
# HELP python_gc_objects_uncollectable_total Uncollectable object found during GC
# TYPE python_gc_objects_uncollectable_total counter
python_gc_objects_uncollectable_total{generatinotallow="0"} 0.0
python_gc_objects_uncollectable_total{generatinotallow="1"} 0.0
python_gc_objects_uncollectable_total{generatinotallow="2"} 0.0
# HELP python_gc_collections_total Number of times this generation was collected
# TYPE python_gc_collections_total counter
python_gc_collections_total{generatinotallow="0"} 40.0
python_gc_collections_total{generatinotallow="1"} 3.0
python_gc_collections_total{generatinotallow="2"} 0.0
# HELP python_info Python platform information
# TYPE python_info gauge
python_info{implementatinotallow="CPython",major="3",minor="9",patchlevel="18",versinotallow="3.9.18"} 1.0
# HELP my_custom_metric 自定义测试指标
# TYPE my_custom_metric gauge
my_custom_metric{label1="test",label2="normal"} 1.3487897665970805
配置 Exproter
虽然此时采集数据的服务已经启动,但是还需要在prometheus server 端对采集数据的服务进行定义,才能够拉取对应的指标数据,于是在prometheus目录下面创建 prometheus.yml 文件,如下:
global:
scrape_interval: 5s # 每15秒拉取一次数据
scrape_configs:
# 监控Prometheus自身
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
# 监控本地的自定义Exporter(关键配置)
- job_name: 'custom_exporter'
static_configs:
# 这里的IP不是localhost,而是你本地机器的实际IP(容器内访问宿主机的地址)
# 如何获取:在本地终端执行 `ifconfig` 或 `ip addr` 查看(例如192.168.x.x)
- targets: ['192.168.0.8:9091'] # 你的本地IP + Exporter端口
需要注意的是这里设置的 targets 标签定义的就是 prometheus-client 的 ip 地址和端口,也就是刚才我们编写的custom_exporter.py 所运行的ip 地址和端口。
接着,让我们重启prometheus 服务器,命令如下:
docker stop prometheus
docker rm prometheus
然后执行如下命令:
docker run --name prometheus -d -p 127.0.0.1:9090:9090 -v /Users/cuihao/docker/prometheus:/etc/prometheus prom/prometheus --config.file=/etc/prometheus/prometheus.yml
该命令需要注意的是,通过参数 -v /Users/cuihao/docker/prometheus:/etc/prometheus 的配置,将主机目录作为配置文件存储,当修改主机上的 prometheus.yml 会直接影响容器内的配置。
查看指标结果
完成上述操作之后,通过访问http://localhost:9090/query返回到 prometheus server 的 web 界面。
在查询输入框中输入如下命令:
user_login_total
此时会得到对应的指标数据:
可以看到从 192.168.0.8:9091 发送的指标数据,包含了用户的登录信息。
我们在输入框中输入的 user_login_total 实际上是,查询语言是 PromQL(Prometheus Query Language),它是用来对 Prometheus 采集到的指标数据进行查询的语言。下面通过它的几个组成部分的表格给大家展开描述:
概念 | 说明 | 示例 |
指标名 | 要查询的基础指标 | user_login_total |
标签过滤 | 用 {} 筛选特定维度 | {user_type="admin"} |
时间范围 | 用 [时间] 指定范围 | [5m] 最近5分钟 |
函数 | 数据处理和计算 | rate() , sum() |
运算符 | 数学和逻辑运算 | > , + , / |
聚合 | 按维度分组统计 | by (ip_region) |
因此,我们可以通过编写 ProQL 按照条件查询指标数据,后面在设置告警规则的时候会用到。
到这里,我们已经能够通过 promeheus-client 采集到数据,并且通过 prometheus-server 端的 ProQL 查询数据结果了。
定义 Prometheus 告警规则
接着,我们就需要针对采集的数据进行报警,这里需要先设置报警规则,当触发报警规则时通知 alertmanager 进行后续处理。
1. 创建告警规则文件
在你的本地 Prometheus 配置目录(/Users/cuihao/docker/prometheus)中,新建一个告警规则文件 alert_rules.yml,内容如下(以 “用户登录异常” 为例):
groups:
- name: login_anomaly_rules # 规则组名称
rules:
# 规则1:5分钟内登录失败率超过20%(可能是攻击或系统故障)
- alert: HighLoginFailureRate
expr: |
sum(rate(user_login_total{login_status="failed"}[5m]))
/
sum(rate(user_login_total[5m]))
> 0.2 # 失败率>20%
for: 1m # 持续1分钟触发告警
labels:
severity: critical # 告警级别(紧急)
annotations:
summary: "登录失败率过高"
description: "过去5分钟登录失败率{{ $value | humanizePercentage }},可能存在异常登录行为"
# 规则2:管理员账号1分钟内登录失败次数>5次(可能被暴力破解)
- alert: AdminLoginFailureSpike
expr: |
sum(rate(user_login_total{user_type="admin", login_status="failed"}[1m]))
> 5 # 1分钟内失败>5次
for: 30s # 持续30秒触发
labels:
severity: warning
annotations:
summary: "管理员登录失败次数突增"
description: "管理员账号1分钟内登录失败{{ $value }}次,可能存在暴力破解风险"
2. 让 Prometheus 加载告警规则
修改你的 prometheus.yml 配置文件,添加告警规则文件的路径(确保和已有配置合并):
global:
scrape_interval: 15s
rule_files:
- "alert_rules.yml" # 加载刚才创建的告警规则文件
scrape_configs:
- job_name: 'custom_exporter'
static_configs:
- targets: ['192.168.0.16:9091'] # 你的Exporter地址
这个告警规则文件定义了一个名为login_anomaly_rules的规则组,包含两个针对用户登录异常行为的监控规则:第一个规则HighLoginFailureRate会检测系统整体登录失败率,当5分钟内平均失败率超过20%并持续1分钟时触发严重级别(critical)告警,提示可能存在系统故障或恶意攻击;第二个规则AdminLoginFailureSpike专门监控管理员账号的安全状态,当检测到1分钟内管理员登录失败次数超过5次且持续30秒时触发警告级别(warning)告警,提示可能存在暴力破解尝试。两个规则都通过PromQL表达式实时计算指标数据,并在触发时提供包含具体数值的描述信息。
3. 重启 Prometheus 使配置生效
docker restart prometheus
访问 Prometheus UI 的 “Alerts” 页面(http://localhost:9090/alerts),可以看到定义的规则,当异常发生时,规则会从 “Inactive” 变为 “Pending” 再到 “Firing”(触发状态)。
部署 Alertmanager 转发告警
到此, Prometheus 的告警规则已经配置完毕,并且可以通过 Web UI 查看到规则。由于 Prometheus 本身不处理告警转发,需要用Alertmanager接收 Prometheus 的告警,然后转发到你的处理服务(后续调用 DeepSeek)。因此,接下来我们需要部署和配置 Alertmanager,并且编写 处理服务 。
1. 安装启动 Alertmanager 容器
执行如下指令,在prometheus 目录下面创建alertmanager 目录,该目录下会存放报警处理的文件。并且通过 docker run 命令启动alertmanager 的容器服务。
# 先创建Alertmanager配置目录
mkdir -p /Users/cuihao/docker/prometheus/alertmanager
# 启动容器(映射9093端口,挂载配置目录)
docker run --name alertmanager -d -p 127.0.0.1:9093:9093 -v /Users/cuihao/docker/prometheus/alertmanager:/etc/alertmanager prom/alertmanager
2. 配置 Alertmanager
在 alertmanager 目录下创建配置文件 alertmanager.yml,通过对该文件的配置,转发告警到处理服务。
route:
group_by: ['alertname'] # 按告警名称分组
group_wait: 10s # 组内等待10秒再发送
group_interval: 10s # 组内间隔10秒发送
repeat_interval: 1h # 重复告警间隔1小时
receiver: 'webhook_handler' # 转发到名为webhook_handler的接收者
receivers:
- name: 'webhook_handler'
webhook_configs:
- url: 'http://192.168.0.8:5008/alert' # 你的处理服务地址(本地5000端口)
send_resolved: true # 告警解决后也发送通知
请注意这里我们定义的服务地址是 http://192.168.0.8:5008/alert, 也就是后续需要创建的处理服务需要工作的地址。也就是需要从 docker 容器alertmanager 去访问本机的 http://192.168.0.8:5008/alert 服务。
3. 重启 Alertmanager 生效
更新完配置文件需要重启alertmanager 的容器实例。
docker restart alertmanager
4. 将 Prometheus 和 Alertmanager 加入同一网络
alertmanager 配置完毕之后,还有一个重要步骤,就是要保证 Prometheus 服务和Alertmanager 服务在同一个网络中。由于我们的实验一直使用的 docker 容器进行部署,所以需要通过如下操作进行 docker 的网络设置。
(1)停止运行中的容器
docker stop prometheus alertmanager
(2)创建一个共享网络(若不存在)
docker network create prometheus-network
(网络名称可自定义,如monitoring-network,保持统一即可)。
(3)将两个容器加入同一网络
# 将Prometheus加入网络
docker network connect prometheus-network prometheus
# 将Alertmanager加入网络
docker network connect prometheus-network alertmanager
(4)重启容器,使网络配置生效
docker start prometheus alertmanager
(5)验证网络是否生效
进入 Prometheus 容器,测试能否解析alertmanager容器名:
# 进入Prometheus容器
docker exec -it prometheus /bin/sh
# 测试DNS解析(若容器内有nslookup)
nslookup alertmanager
# 若没有nslookup,用ping测试(能ping通说明解析正常)
ping alertmanager
5. 让 Prometheus 连接 Alertmanager
完成网络设置之后,我们再回头到Prometheus 配置告警通知,修改 prometheus.yml,添加 Alertmanager 地址如下:
alerting:
alertmanagers:
- static_configs:
- targets:
- 'alertmanager:9093'
这里的alertmanager:9093 就是Alertmanager 服务所在的地址和端口号。
最后,重启 Prometheus:
docker restart prometheus
编写 DeepSeek 分析代码
到这里,我们完成了文章开头所说的前三步操作,如下图所示。我们将整体思路做一个回顾,已经完成生成中指标数据,采集数据,处理报警的工作,最后需要“生成智能报告”。
编写alert_handler.py代码如下:
from flask import Flask, request, jsonify
import os
import sys
import datetime
import json
from openai import OpenAI
from dotenv import load_dotenv # 用于加载环境变量
# 初始化Flask应用
app = Flask(__name__)
# 加载环境变量(从.env文件读取DEEPSEEK_API_KEY)
try:
# 尝试加载环境变量,并添加日志
load_result = load_dotenv() # 默认读取当前目录的.env文件
script_dir = os.path.dirname(os.path.abspath(__file__))
env_path = os.path.join(script_dir, ".env")
# 配置日志和报告路径(使用脚本所在目录)
error_log = os.path.join(script_dir, "error.log") # 错误日志路径
report_dir = os.path.join(script_dir, "alert_reports") # 分析报告保存目录
# 记录环境变量加载情况
with open(error_log, "a", encoding="utf-8") as f:
if load_result:
f.write(f"[{datetime.datetime.now()}] 成功加载环境变量文件: {env_path}\n")
else:
f.write(f"[{datetime.datetime.now()}] 未找到环境变量文件,使用系统环境变量: {env_path}\n")
# 确保报告目录存在
os.makedirs(report_dir, exist_ok=True)
with open(error_log, "a", encoding="utf-8") as f:
f.write(f"[{datetime.datetime.now()}] 报告目录准备就绪: {report_dir}\n")
except Exception as e:
# 记录初始化错误
error_msg = f"初始化文件系统时出错: {str(e)}"
print(f"[{datetime.datetime.now()}] {error_msg}") # 控制台也输出,方便调试
# 尝试写入错误日志
try:
with open("error.log", "a", encoding="utf-8") as f:
f.write(f"[{datetime.datetime.now()}] {error_msg}\n")
except:
pass
raise # 抛出错误,终止启动
def init_deepseek_client():
"""初始化DeepSeek客户端(兼容OpenAI SDK)"""
try:
# 验证环境变量
api_key = os.environ.get("DEEPSEEK_API_KEY")
with open(error_log, "a", encoding="utf-8") as f:
if api_key:
f.write(f"[{datetime.datetime.now()}] 成功获取DEEPSEEK_API_KEY (部分隐藏): {api_key[:4]}****\n")
else:
f.write(f"[{datetime.datetime.now()}] 未在环境变量中找到DEEPSEEK_API_KEY\n")
if not api_key:
raise ValueError("未在环境变量中找到DEEPSEEK_API_KEY,请检查.env文件")
# 初始化客户端(DeepSeek兼容OpenAI SDK格式)
client = OpenAI(
api_key=api_key,
base_url="https://api.deepseek.com/v1" # DeepSeek API基础地址
)
# 验证客户端连接
try:
# 发送一个简单的测试请求验证连接
response = client.chat.completions.create(
model="deepseek-chat",
messages=[{"role": "user", "content": "test"}],
max_tokens=1
)
with open(error_log, "a", encoding="utf-8") as f:
f.write(f"[{datetime.datetime.now()}] DeepSeek客户端初始化成功,API连接正常\n")
except Exception as e:
with open(error_log, "a", encoding="utf-8") as f:
f.write(f"[{datetime.datetime.now()}] DeepSeek API连接测试失败: {str(e)}\n")
raise
return client
except Exception as e:
# 记录初始化错误
error_msg = f"DeepSeek客户端初始化失败:{str(e)}"
with open(error_log, "a", encoding="utf-8") as f:
f.write(f"[{datetime.datetime.now()}] {error_msg}\n")
raise # 抛出错误,避免服务启动后无法调用API
# 初始化DeepSeek客户端(服务启动时执行)
try:
with open(error_log, "a", encoding="utf-8") as f:
f.write(f"[{datetime.datetime.now()}] 开始初始化DeepSeek客户端...\n")
client = init_deepseek_client()
except Exception as e:
with open(error_log, "a", encoding="utf-8") as f:
f.write(f"[{datetime.datetime.now()}] 初始化DeepSeek客户端失败,服务无法启动:{str(e)}\n")
raise
def call_deepseek_analysis(alert_info):
"""调用DeepSeek分析告警信息,返回分析结果和报告路径"""
try:
with open(error_log, "a", encoding="utf-8") as f:
f.write(f"[{datetime.datetime.now()}] 开始处理告警分析...\n")
# 1. 验证并提取告警关键信息
if not isinstance(alert_info, dict):
raise ValueError(f"告警信息格式错误,预期字典类型,实际为: {type(alert_info)}")
# 验证必要字段
required_fields = ["labels", "annotations"]
for field in required_fields:
if field not in alert_info:
raise ValueError(f"告警信息缺少必要字段: {field}")
alert_name = alert_info["labels"].get("alertname", "未知告警")
severity = alert_info["labels"].get("severity", "未知级别")
description = alert_info["annotations"].get("description", "无描述")
start_time = alert_info.get("startsAt", datetime.datetime.now().isoformat())
labels = alert_info["labels"]
with open(error_log, "a", encoding="utf-8") as f:
f.write(f"[{datetime.datetime.now()}] 提取到告警信息 - 名称: {alert_name}, 级别: {severity}\n")
# 2. 构造提示词(系统角色+用户输入)
system_prompt = """
你是一名资深运维与安全分析师,擅长分析业务系统异常告警。
请根据提供的登录监控告警信息,生成结构化分析报告,包含:
1. 告警基本信息(名称、级别、时间、关键标签)
2. 可能的原因分析(结合业务场景,如攻击、系统故障、用户行为异常等)
3. 具体处理建议(分步骤说明,可操作)
4. 预防措施(如何避免类似问题再次发生)
报告风格需专业、简洁,重点突出。
""" user_prompt = f"""
告警详细信息如下:
- 告警名称:{alert_name}
- 严重级别:{severity}
- 告警描述:{description}
- 发生时间:{start_time}
- 关联标签:{json.dumps(labels, ensure_ascii=False)} # 包含用户类型、地区等维度
请基于以上信息分析并生成报告。
"""
# 3. 调用DeepSeek模型
with open(error_log, "a", encoding="utf-8") as f:
f.write(f"[{datetime.datetime.now()}] 开始调用DeepSeek API进行分析...\n")
response = client.chat.completions.create(
model="deepseek-chat", # 使用的模型名称
messages=[
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_prompt}
],
stream=False,
max_tokens=1000 # 限制最大返回长度
)
# 4. 提取分析结果
if not response.choices or len(response.choices) == 0:
raise ValueError("DeepSeek API返回结果为空")
analysis_content = response.choices[0].message.content
with open(error_log, "a", encoding="utf-8") as f:
f.write(f"[{datetime.datetime.now()}] 成功获取DeepSeek分析结果,长度: {len(analysis_content)}\n")
# 5. 生成分析报告文件
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
report_filename = f"analysis_{alert_name}_{timestamp}.txt".replace(" ", "_")
report_path = os.path.join(report_dir, report_filename)
with open(report_path, "w", encoding="utf-8") as f:
f.write(f"=== 告警分析报告 ===\n")
f.write(f"生成时间:{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
f.write(f"告警名称:{alert_name}\n")
f.write(f"严重级别:{severity}\n")
f.write(f"发生时间:{start_time}\n\n")
f.write("=== 分析内容 ===\n")
f.write(analysis_content)
with open(error_log, "a", encoding="utf-8") as f:
f.write(f"[{datetime.datetime.now()}] 分析报告已保存至: {report_path}\n")
return analysis_content, report_path
except Exception as e:
# 记录调用错误
error_msg = f"DeepSeek分析调用失败:{str(e)}"
with open(error_log, "a", encoding="utf-8") as f:
f.write(f"[{datetime.datetime.now()}] {error_msg}\n")
return f"分析失败:{str(e)}", None
@app.route('/alert', methods=['POST'])
def handle_alert():
"""接收Alertmanager的Webhook告警,调用分析并返回结果"""
try:
with open(error_log, "a", encoding="utf-8") as f:
f.write(f"[{datetime.datetime.now()}] 收到新的告警请求\n")
# 1. 解析Alertmanager发送的JSON数据
try:
alert_data = request.json
except Exception as e:
raise ValueError(f"解析JSON数据失败: {str(e)}")
if not alert_data:
return jsonify({"status": "failed", "reason": "未收到有效数据"}), 400
with open(error_log, "a", encoding="utf-8") as f:
f.write(f"[{datetime.datetime.now()}] 成功解析告警数据,包含字段: {', '.join(alert_data.keys())}\n")
# 2. 提取第一个触发的告警
if not alert_data.get("alerts"):
return jsonify({"status": "failed", "reason": "告警数据中无alerts字段"}), 400
if not isinstance(alert_data["alerts"], list) or len(alert_data["alerts"]) == 0:
return jsonify({"status": "failed", "reason": "alerts字段不是有效的列表或为空"}), 400
first_alert = alert_data["alerts"][0]
alert_name = first_alert['labels'].get('alertname', '未知告警')
print(f"[{datetime.datetime.now()}] 收到告警:{alert_name}") # 控制台输出
with open(error_log, "a", encoding="utf-8") as f:
f.write(f"[{datetime.datetime.now()}] 处理第一个告警: {alert_name}\n")
# 3. 调用DeepSeek分析
analysis_content, report_path = call_deepseek_analysis(first_alert)
# 4. 返回结果
return jsonify({
"status": "success",
"analysis": analysis_content,
"report_path": report_path
})
except Exception as e:
# 记录请求处理错误
error_msg = f"告警处理失败:{str(e)}"
with open(error_log, "a", encoding="utf-8") as f:
f.write(f"[{datetime.datetime.now()}] {error_msg}\n")
return jsonify({"status": "failed", "reason": str(e)}), 500
# 添加一个健康检查接口,方便测试服务是否正常运行
@app.route('/health', methods=['GET'])
def health_check():
try:
return jsonify({
"status": "healthy",
"timestamp": datetime.datetime.now().isoformat(),
"services": {
"deepseek": "initialized" if 'client' in globals() else "not initialized",
"report_dir": report_dir,
"report_dir_writable": os.access(report_dir, os.W_OK)
}
})
except Exception as e:
return jsonify({
"status": "unhealthy",
"reason": str(e)
}), 500
if __name__ == '__main__':
try:
with open(error_log, "a", encoding="utf-8") as f:
f.write(f"[{datetime.datetime.now()}] 开始启动alert_handler服务...\n")
f.write(f"[{datetime.datetime.now()}] 服务将监听端口: 5008\n")
# 启动服务(监听所有IP的5008端口)
app.run(host='0.0.0.0', port=5008, debug=True)
except Exception as e:
error_msg = f"服务启动失败:{str(e)}"
print(f"[{datetime.datetime.now()}] {error_msg}")
with open(error_log, "a", encoding="utf-8") as f:
f.write(f"[{datetime.datetime.now()}] {error_msg}\n")
sys.exit(1)
上面这段 Python 代码构建了一个智能告警分析服务,它通过 Flask 框架接收 Prometheus Alertmanager 发送的告警通知,利用 DeepSeek 大模型进行智能分析并生成专业报告。服务启动时会从 .env 文件加载 API 密钥并初始化 DeepSeek 客户端,在接收到告警后自动提取关键信息(如告警名称、严重级别和描述),构造专业提示词调用 AI 接口进行深度分析,最终生成包含原因诊断、处理建议和预防措施的结构化报告保存到本地目录。整个流程配备完善的错误处理和日志记录,通过 /health 端点提供实时服务状态监控,将传统告警升级为智能诊断系统,帮助运维人员快速定位复杂问题根源。
由于 alertmanager 发送报警信息需要基于 Prometheus 官方报警格式,所以我们需要对写好的代码进行测试,如下命令执行代码:
python alert_handler.py
通过命令行测试服务。
curl -X POST "http://localhost:5008/alert" \
-H "Content-Type: application/json" \
-d '{
"version": "4",
"groupKey": "{}:{alertname=\"HighLoginFailureRate\"}",
"status": "firing",
"receiver": "webhook_handler",
"groupLabels": {
"alertname": "HighLoginFailureRate"
},
"commonLabels": {
"alertname": "HighLoginFailureRate",
"severity": "critical"
},
"commonAnnotations": {
"description": "登录失败率过高,当前失败率为30%,超过阈值20%",
"summary": "登录失败率异常升高"
},
"externalURL": "http://localhost:9093",
"alerts": [
{
"status": "firing",
"labels": {
"alertname": "HighLoginFailureRate",
"severity": "critical"
},
"annotations": {
"description": "登录失败率过高,当前失败率为30%,超过阈值20%",
"summary": "登录失败率异常升高"
},
"startsAt": "2025-08-14T08:30:00.000Z",
"endsAt": "0001-01-01T00:00:00Z",
"generatorURL": "http://localhost:9090/graph?g0.expr=sum(rate(user_login_total%7Blogin_status%3D%22failed%22%7D%5B5m%5D))+%2F+sum(rate(user_login_total%5B5m%5D))+%3E+0.2&g0.tab=1"
}
]
}'
测试功能
按以下步骤逐步验证,每一步确认无误后再进行下一步:
终端窗口 | 命令(操作) | 作用 |
窗口 1 | 启动 Exporter(带控制接口)cd /存放/custom_exporter.py的目录python custom_exporter.py | 生成模拟登录数据, 暴露 9091 端口指标, 9092 端口控制接口 |
窗口 2 | 启动处理服务(alert_handler)cd /存放/alert_handler.py的目录python alert_handler.py | 监听 5000 端口, 接收告警并调用 DeepSeek |
窗口 3 | 启动 Prometheus 容器docker start prometheus | 采集数据、检测异常 |
窗口 4 | 启动 Alertmanager 容器docker start alertmanager | 转发告警到处理服务 |
安装上述操作之后,观察如下图的 alert_reports 目录,这个目录下面保存了生成分析报告信息。如下图所示:
作者介绍
崔皓,51CTO社区编辑,资深架构师,拥有18年的软件开发和架构经验,10年分布式架构经验。
