为 Prometheus 告警规则增加 UI 管理能力
为 Prometheus 告警规则增加 UI 管理能力
在现代云原生架构中,Prometheus 作为核心监控系统,其告警规则配置通常依赖 YAML 文件管理。这种方式虽然灵活,但缺乏可视化界面,容易导致配置错误,且难以实现动态管理。本文将循序渐进地探讨如何为 Prometheus 告警规则增加 UI 管理能力,从基础概念到高级实现,完整展示构建过程。## 1. 基础概念:Prometheus 告警规则与 UI 需求### 1.1 Prometheus 告警规则的本质Prometheus 告警规则通过 YAML 文件定义,格式如下:yamlgroups:- name: example rules: - alert: HighCpuUsage expr: node_cpu_seconds_total{mode="user"} > 0.8 for: 5m labels: severity: critical annotations: summary: "CPU usage high"### 1.2 为什么需要 UI 管理?-易用性:无需直接编辑 YAML 文件-动态性:实时添加、修改、删除规则-可视化:预览规则效果,避免语法错误-审计:记录规则变更历史## 2. 基础实现:构建简单 UI 与后端### 2.1 后端 API 设计我们使用 Python Flask 实现一个简单的 REST API,用于管理告警规则:python# backend/app.pyfrom flask import Flask, jsonify, requestimport yamlapp = Flask(__name__)RULES_FILE = 'alerts.yaml'def load_rules(): """从文件加载告警规则""" with open(RULES_FILE, 'r') as f: return yaml.safe_load(f)def save_rules(data): """保存告警规则到文件""" with open(RULES_FILE, 'w') as f: yaml.dump(data, f)@app.route('/rules', methods=['GET'])def get_rules(): """获取所有告警规则""" rules = load_rules() return jsonify(rules)@app.route('/rules', methods=['POST'])def add_rule(): """添加新告警规则""" new_rule = request.json rules = load_rules() rules['groups'][0]['rules'].append(new_rule) save_rules(rules) return jsonify({'status': 'success', 'rule': new_rule})@app.route('/rules/<int:rule_id>', methods=['DELETE'])def delete_rule(rule_id): """删除指定告警规则""" rules = load_rules() if rule_id < len(rules['groups'][0]['rules']): del rules['groups'][0]['rules'][rule_id] save_rules(rules) return jsonify({'status': 'deleted'}) return jsonify({'status': 'error', 'message': 'Rule not found'}), 404if __name__ == '__main__': app.run(debug=True)### 2.2 前端 UI 基础使用 HTML + JavaScript 构建简单界面:html<!-- frontend/index.html --><!DOCTYPE html><html><head> <title>Prometheus 告警规则管理</title> <style> body { font-family: Arial; margin: 20px; } .rule-card { border: 1px solid #ddd; padding: 10px; margin: 10px 0; } button { margin: 5px; } </style></head><body> <h1>告警规则列表</h1> <div id="rules-container"></div> <h2>添加新规则</h2> <form id="add-rule-form"> <input type="text" id="alert-name" placeholder="告警名称" required> <input type="text" id="expr" placeholder="表达式 (如: node_cpu > 0.8)" required> <button type="submit">添加规则</button> </form> <script> const apiBase = 'http://localhost:5000'; async function loadRules() { const response = await fetch(`${apiBase}/rules`); const data = await response.json(); const container = document.getElementById('rules-container'); container.innerHTML = ''; data.groups[0].rules.forEach((rule, index) => { const card = document.createElement('div'); card.className = 'rule-card'; card.innerHTML = ` <h3>${rule.alert}</h3> <p>表达式: ${rule.expr}</p> <p>持续时间: ${rule.for || 'N/A'}</p> <button onclick="deleteRule(${index})">删除</button> `; container.appendChild(card); }); } async function deleteRule(index) { await fetch(`${apiBase}/rules/${index}`, { method: 'DELETE' }); loadRules(); } document.getElementById('add-rule-form').addEventListener('submit', async (e) => { e.preventDefault(); const name = document.getElementById('alert-name').value; const expr = document.getElementById('expr').value; await fetch(`${apiBase}/rules`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ alert: name, expr: expr, for: '5m', labels: { severity: 'warning' }, annotations: { summary: `${name} triggered` } }) }); loadRules(); }); loadRules(); </script></body></html>## 3. 进阶实现:完整的 CRUD 与验证### 3.1 增强后端功能增加规则验证和更新功能:python# backend/advanced_app.pyfrom flask import Flask, jsonify, requestimport yamlimport refrom datetime import datetimeapp = Flask(__name__)RULES_FILE = 'alerts.yaml'def validate_rule(rule): """验证告警规则格式""" required_fields = ['alert', 'expr'] for field in required_fields: if field not in rule: return False, f"缺少必填字段: {field}" # 简单表达式验证 if not rule['expr'].strip(): return False, "表达式不能为空" return True, ""@app.route('/rules/<int:rule_id>', methods=['PUT'])def update_rule(rule_id): """更新指定规则""" updated_rule = request.json valid, msg = validate_rule(updated_rule) if not valid: return jsonify({'status': 'error', 'message': msg}), 400 rules = load_rules() if rule_id < len(rules['groups'][0]['rules']): rules['groups'][0]['rules'][rule_id] = updated_rule save_rules(rules) return jsonify({'status': 'updated', 'rule': updated_rule}) return jsonify({'status': 'error', 'message': 'Rule not found'}), 404@app.route('/rules/history', methods=['GET'])def get_history(): """获取规则变更历史(简化版)""" try: with open('history.log', 'r') as f: history = f.readlines() return jsonify({'history': history[-50:]}) # 返回最近50条 except FileNotFoundError: return jsonify({'history': []})### 3.2 前端增强:添加编辑和历史功能javascript// advanced_frontend.js (片段)async function updateRule(index) { const name = prompt('输入新告警名称:'); const expr = prompt('输入新表达式:'); if (name && expr) { const response = await fetch(`${apiBase}/rules/${index}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ alert: name, expr: expr, for: '5m', labels: { severity: 'warning' }, annotations: { summary: `${name} triggered` } }) }); if (response.ok) { loadRules(); loadHistory(); } else { alert('更新失败'); } }}## 4. 高级实现:集成 Prometheus 热加载### 4.1 自动重载配置高级版本需要自动通知 Prometheus 重新加载规则:python# advanced_backend.py (片段)import requestsPROMETHEUS_URL = 'http://localhost:9090'def reload_prometheus(): """触发 Prometheus 热加载""" try: response = requests.post(f'{PROMETHEUS_URL}/-/reload') return response.status_code == 200 except requests.exceptions.RequestException as e: print(f"重载失败: {e}") return False@app.route('/rules', methods=['POST'])def add_rule_with_reload(): """添加规则并重载""" new_rule = request.json valid, msg = validate_rule(new_rule) if not valid: return jsonify({'status': 'error', 'message': msg}), 400 rules = load_rules() rules['groups'][0]['rules'].append(new_rule) save_rules(rules) # 记录变更 with open('history.log', 'a') as f: f.write(f"{datetime.now()}: 添加规则 {new_rule['alert']}\n") # 触发重载 if reload_prometheus(): return jsonify({'status': 'success', 'reloaded': True}) else: return jsonify({'status': 'success', 'reloaded': False, 'warning': '请手动重载'})### 4.2 高级 UI:批量操作与预览javascript// advanced_ui.js (片段)async function batchDelete() { const rules = document.querySelectorAll('.rule-card input[type="checkbox"]:checked'); const ids = Array.from(rules).map(cb => parseInt(cb.dataset.id)); for (const id of ids) { await fetch(`${apiBase}/rules/${id}`, { method: 'DELETE' }); } loadRules();}async function previewRule(expr) { // 调用 Prometheus API 预览 const response = await fetch(`http://localhost:9090/api/v1/query?query=${encodeURIComponent(expr)}`); const data = await response.json(); if (data.status === 'success') { alert(`查询结果: ${JSON.stringify(data.data.result)}`); } else { alert('表达式无效'); }}## 5. 总结通过本文,我们从 Prometheus 告警规则的基础概念出发,逐步构建了完整的 UI 管理能力:1.基础阶段:我们理解了告警规则的 YAML 结构,并构建了简单的 Flask 后端和 HTML 前端,实现了规则的读取和添加功能。2.进阶阶段:增加了规则验证、更新功能和变更历史记录,提高了系统的健壮性和可用性。3.高级阶段:集成了 Prometheus 热加载机制,实现了配置变更的自动生效,并添加了批量操作和表达式预览功能。这种 UI 管理方式不仅降低了运维成本,还让团队能够更灵活地调整告警策略。在实际生产环境中,还可以添加用户认证、权限管理、规则分组、告警模板等功能,构建更完善的告警管理平台。核心思想是保持 Prometheus 原生配置格式的同时,通过中间层实现动态管理和可视化操作。