研究Aiops有一段时间了,目前手里有不少可落地的方案了,接下来会把这些方案全部整理到我的大模型课程里。同时,欢迎大家把你遇到的场景在评论区留言。我会在能力范围内给你提供思路和建议。
今天收到一个同学的需求,想着做一个用来巡检的智能体。

1、数据采集层:
node_exporter,它会暴露机器的硬件和操作系统指标(CPU、内存、磁盘等)。2)Prometheus Server 定期从所有 node_exporter 拉取数据并存储。2、自动化与报告层:
1)定时触发: 每天早上(例如 8:00)通过 Cron 节点自动启动工作流。n8n 通过 HTTP 请求调用 Prometheus 的 API,查询所有虚拟机的关键指标。n8n 将查询到的数据进行处理,并根据预设的阈值判断各项指标是否正常。n8n 将处理后的数据动态生成一个 HTML 表格,对异常指标进行标红或标黄。n8n 通过邮件(或其他方式如 Slack、钉钉)将生成的 HTML 报告发送给指定的收件人。
节点 1: Cron Trigger (定时触发器)
这是工作流的入口,负责每天早上自动启动。
-
节点类型: Schedule Trigger -
规则:每天早上 8 点触发:0 8 * * *
节点 2: HTTP Request (获取所有实例列表)
我们首先需要知道当前有哪些虚拟机在监控中。通过查询 Prometheus 的 up 指标可以轻松获取。
-
节点类型: HTTP Request -
配置: -
Method: GET -
URL: http://<你的PrometheusIP>:9090/api/v1/query -
Query Parameters: -
query: up{job="vms"} (这里的 vms 就是 prometheus.yml 中定义的 job_name)
-
Options: 确保 Response Format 为 JSON。
节点 3: Set (处理实例列表)
上一个节点返回的数据比较复杂,我们只需要提取出虚拟机的实例名(通常是 IP:端口)。
-
节点类型: Set
-
配置:
-
勾选 Keep Only Set,替换掉原有数据。
-
Set Key:
instance -
Value:
{{ $json.metric.instance }}(这会从每个返回的数据项中提取instance字段) -
Additional Fields: 可以添加一个
job字段,值为{{ $json.metric.job }},方便后续使用。
节点 4: Split In Batches (分批处理)
我们需要对每一台虚拟机分别查询其详细指标。这个节点可以帮我们遍历实例列表。
-
节点类型: Split In Batches -
配置: -
Batch Size: 1 (每次处理一台虚拟机) -
Options: 勾选 Reset,这样每次工作流运行都会从头开始处理。
节点 5: HTTP Request (查询单台VM的所有指标)
这是核心查询节点。它在循环中为每一台虚拟机执行一次。
-
节点类型: HTTP Request -
配置: -
query: 我们将使用一个复杂的 PromQL 查询一次性获取所有需要的指标。这个查询会返回一个向量,包含 CPU、内存、磁盘使用率等。 -
Method: GET -
URL: http://<你的PrometheusIP>:9090/api/v1/query -
Query Parameters: query: 我们将使用一个复杂的 PromQL 查询一次性获取所有需要的指标。这个查询会返回一个向量,包含 CPU、内存、磁盘使用率等。
( label_replace( label_replace( label_replace( label_replace( label_replace( (100 - (avg by (instance) (irate(node_cpu_seconds_total{mode="idle",instance="{{ $node["Set"].json["instance"] }}"}[5m])) * 100)), "cpu_usage", "$1", "instance", "(.*)" ), "memory_usage", "$1", "instance", "(.*)" ), "disk_usage", "$1", "instance", "(.*)" ), "load1", "$1", "instance", "(.*)" ), "read_iops", "$1", "instance", "(.*)")or( (1 - (node_memory_MemAvailable_bytes{instance="{{ $node["Set"].json["instance"] }}"}} / node_memory_MemTotal_bytes{instance="{{ $node["Set"].json["instance"] }}"}})) * 100)or( 100 - ((node_filesystem_avail_bytes{instance="{{ $node["Set"].json["instance"] }}",fstype!~"tmpfs|rootfs"} / node_filesystem_size_bytes{instance="{{ $node["Set"].json["instance"] }}",fstype!~"tmpfs|rootfs"}) * 100))or( node_load1{instance="{{ $node["Set"].json["instance"] }}"})or( irate(node_disk_reads_completed_total{instance="{{ $node["Set"].json["instance"] }}"}}[5m]))or( irate(node_disk_writes_completed_total{instance="{{ $node["Set"].json["instance"] }}"}}[5m]))
or 将多个查询组合在一起,并用 label_replace 将结果统一命名。{{ $node["Set"].json["instance"] }} 是 n8n 的动态变量,会自动替换为当前循环的虚拟机实例名。节点 6: Code (整合数据并生成HTML报告)
当所有虚拟机都查询完毕后(Split In Batches 节点执行完),这个节点会收集所有结果,并生成最终的 HTML 表格。
-
节点类型: Code (Javascript)
-
配置: 将以下 JavaScript 代码粘贴到代码框中。
// 从上一个节点获取所有虚拟机的数据const allVmData = $input.all();// --- 阈值定义 ---const THRESHOLDS = {cpu_usage: { warning: 70, critical: 90 },memory_usage: { warning: 80, critical: 95 },disk_usage: { warning: 80, critical: 95 },load1: { warning: 2, critical: 5 }, // 负载阈值需根据CPU核心数调整read_iops: { warning: 100, critical: 500 }, // IOPS阈值需根据磁盘性能调整write_iops: { warning: 100, critical: 500 }};// --- 辅助函数:根据值和阈值返回带样式的HTML ---function getStyledCell(value, metricName) {const numValue = parseFloat(value);const threshold = THRESHOLDS[metricName];if (!threshold) return `<td>${value}</td>`; // 如果没有阈值,直接返回let style = '';if (numValue >= threshold.critical) {style = 'color: white; background-color: red; font-weight: bold;';} else if (numValue >= threshold.warning) {style = 'color: black; background-color: yellow; font-weight: bold;';}// 格式化数字,保留两位小数const formattedValue = isNaN(numValue) ? value : numValue.toFixed(2);return `<td style="${style}">${formattedValue}</td>`;}// --- 生成HTML表格 ---let htmlTable = `<head><style>body { font-family: Arial, sans-serif; }table { border-collapse: collapse; width: 100%; }th, td { border: 1px solid #dddddd; text-align: left; padding: 8px; }th { background-color: #f2f2f2; }</style></head><body><h2>虚拟机每日巡检报告 - ${new Date().toLocaleDateString('zh-CN')}</h2><table><tr><th>实例</th><th>CPU使用率(%)</th><th>内存使用率(%)</th><th>磁盘使用率(%)</th><th>1分钟平均负载</th><th>读IOPS</th><th>写IOPS</th></tr>`;// 循环处理每台VM的数据for (const vm of allVmData) {const instance = vm.json.metric.instance;const data = vm.json.metric;// 从查询结果中提取值,如果不存在则为 'N/A'const cpuUsage = data.cpu_usage || 'N/A';const memoryUsage = data.memory_usage || 'N/A';const diskUsage = data.disk_usage || 'N/A';const load1 = data.load1 || 'N/A';const readIops = data.read_iops || 'N/A';const writeIops = data.write_iops || 'N/A';htmlTable += `<tr><td>${instance}</td>${getStyledCell(cpuUsage, 'cpu_usage')}${getStyledCell(memoryUsage, 'memory_usage')}${getStyledCell(diskUsage, 'disk_usage')}${getStyledCell(load1, 'load1')}${getStyledCell(readIops, 'read_iops')}${getStyledCell(writeIops, 'write_iops')}</tr>`;}htmlTable += `</table><hr><p><small>阈值说明: 黄色(警告), 红色(严重)</small></p></body>`;// 返回生成的HTML,供下一个节点使用return [{ json: { htmlReport: htmlTable } }];
Split In Batches 节点的 Reset 选项是勾选的,并且 Code 节点连接在 Split In Batches 节点的 "Done" 输出端口上。节点 7: Send Email (发送邮件报告)
最后一步,将生成的 HTML 报告通过邮件发送出去。
-
Subject: 虚拟机每日巡检报告 - {{ $now.format('YYYY-MM-DD') }} -
Email Format: HTML -
Message: {{ $node["Code"].json["htmlReport"] }}(引用上一个节点生成的HTML)


