跳到主要内容

Docker + Prometheus + Grafana

2024年12月19日
柏拉文
越努力,越幸运

一、认识


基于 Docker 实现 Node.js 服务监控,主要关注以下几个方面的指标:

  • 性能指标

    • 事件循环延迟 (Event Loop Delay):监控 Node.js 事件循环的延迟,评估系统性能。事件循环延迟是监控 Node.js 性能的一个关键指标,直接反映了系统是否出现性能瓶颈。使用 perf_hooks.monitorEventLoopDelay 来获取事件循环延迟的详细统计(需要 Node.js 10.10 以上版本)。如果版本不支持,可以使用 process.hrtime() 来手动计算事件循环延迟。

    • 垃圾回收 (GC):监控垃圾回收的行为和对应用性能的影响。使用 perf_hooks.PerformanceObserver 监控垃圾回收的持续时间、频率和类型。监控 GC 可帮助我们理解内存清理和优化的需要。

    • 内存使用情况:包括堆内存和非堆内存的使用,帮助监控内存泄漏和资源消耗。使用 process.memoryUsage() 来获取内存使用情况(rss, heapUsed, heapTotal, external)。使用 v8.getHeapStatistics() 获取 V8 堆的信息。

    • CPU 使用率:监控 CPU 的整体使用情况,识别是否存在 CPU 密集型操作。使用 os.cpus()process.cpuUsage() 监控 CPU 使用率。可以计算每个核的使用情况,获取 CPU 使用时间,帮助识别 CPU 密集型任务。

    • CPU 负载:通过 CPU 负载来判断系统资源是否充足,避免过载。通过 Node.js 中的 os.loadavg() 方法获取系统的负载平均值(即 1 分钟、5 分钟和 15 分钟的负载平均值)

  • 服务状态指标

    • 平均响应时间 (ART):监控服务的响应速度,及时发现性能瓶颈。使用中间件来测量每个请求的响应时间, 帮助定位慢请求

    • 每秒事务数 (TPS):衡量系统的处理能力,反映应用的吞吐量。使用中间件来监控请求数,计算每秒事务数(TPS)。

    • 每秒请求数 (QPS):用于监控服务的请求量,及时发现流量异常。使用中间件来监控请求数,计算每秒请求数(QPS)。

    • 真实请求数 (Real QPS):仅统计成功的请求数,用于评估服务的实际处理能力。使用中间件来监控请求数,计算每秒真实请求数(QPS)。

  • 系统资源监控

    • 句柄数 (Handlers):监控系统打开的文件句柄和网络连接数,确保资源不会被耗尽。通常指的是进程中打开的各种资源,如文件描述符、TCP/UDP 套接字、进程的 I/O 等等。每个句柄都占用一些内存,并且可以影响系统的性能。process._getActiveHandles() 返回当前 Node.js 进程的所有活动句柄(如 TCPUDP、定时器、请求等)

其中, prom-client 调用 collectDefaultMetrics({ register }) 收集默认指标, 它会收集 CPU 基础指标 process_cpu_user_seconds_totalprocess_cpu_system_seconds_totalprocess_cpu_seconds_total; 内存基础指标 nodejs_heap_size_total_bytesnodejs_heap_size_used_bytesnodejs_external_memory_bytes; GC 垃圾回收基础指标 nodejs_gc_duration_seconds、 事件循环延迟基础指标 nodejs_eventloop_lag_seconds 、句柄基础指标 nodejs_active_handles 等。在此基础上,基于 Counter 指标类型定义了 QPS 基础指标, 每次成功的请求会调用 qpsCounter.inc() 来增加 nodejs_qps 指标。在 Prometheus 中查询 QPS 主要依赖 rate() 函数,它能够计算指定时间范围内的增量变化, 比如根据 Prometheus 抓取周期(假设为 15 秒)来计算 QPS 为: rate(nodejs_qps[15s]), 即过去 15 秒内的 QPS(每秒请求数的平均值)。基于 Histogram 指标类型定义了 ART 基础指标, 它会根据预定义的时间桶来统计请求的响应时间,并提供一些有用的统计信息(如请求的平均时间、百分位数等)。

通过使用 prom-client 库收集这些关键指标,并将其暴露给 PrometheusPrometheus 会周期性地拉取并高效存储这些数据。结合 Grafana 进行数据可视化,帮助开发团队实时监控应用性能、健康状况以及资源使用情况。同时,利用 Alertmanager 配置告警策略,确保在关键指标异常时能及时通知相关人员进行处理。

二、Node.js 服务


2.1 metric.js

const {
register,
Counter,
Histogram,
collectDefaultMetrics,
} = require("prom-client");

collectDefaultMetrics({ register });

const qpsCounter = new Counter({
name: "nodejs_qps",
help: "Total number of successful requests",
labelNames: ["method", "path", "httpStatus"],
});

const artHistogram = new Histogram({
name: "nodejs_active_request_duration_seconds",
help: "Histogram of the duration of requests in seconds",
labelNames: ["method", "path", "httpStatus"],
buckets: [0.001, 0.01, 0.1, 0.5, 1, 2, 5, 10],
});

module.exports = {
qpsCounter,
artHistogram,
};

2.2 index.js

const Koa = require("koa");
const client = require("prom-client");
const KoaRouter = require("koa-router");
const { qpsCounter, artHistogram } = require("./metric");

const PORT = 3000;
const app = new Koa();
const router = new KoaRouter();

async function artMiddleware(ctx, next) {
const end = artHistogram.startTimer();
await next();
end({
path: ctx.path,
method: ctx.method,
httpStatus: ctx.status,
});
}

async function qpsMiddleware(ctx, next) {
try {
await next();
qpsCounter.inc({
path: ctx.path,
method: ctx.method,
httpStatus: ctx.status,
});
} catch (error) {
ctx.status = 500;
}
}

const list = [];

router.get("/", artMiddleware, qpsMiddleware, (ctx) => {
list.push(new Array(1000).fill("柏拉文"));
ctx.body = { code: 200, message: "成功!" };
});

router.get("/metrics", async (ctx) => {
try {
ctx.set("Content-Type", client.register.contentType);
ctx.body = await client.register.metrics();
} catch (error) {
ctx.status = 500;
ctx.body = "Error fetching metrics";
}
});

app.use(router.routes());

app.listen(PORT, () => {
console.log(`Koa Server 启动成功,监听端口:${PORT}`);
});

三、Docker 相关部署


四、Grafana 可视化配置


4.1 ART

要查询 ART 的平均响应时间,可以使用 rate() 函数计算每秒的请求总时长和请求总次数的增量,进而计算平均响应时间。

rate(nodejs_active_request_duration_seconds_sum[1m]) / rate(nodejs_active_request_duration_seconds_count[1m])
  • nodejs_active_request_duration_seconds_sum:表示总请求时间的累计值(所有请求的响应时间之和)。

  • nodejs_active_request_duration_seconds_count:表示总请求数(请求的次数)。

  • rate():计算每秒的增量,即每秒请求时长和请求数的平均值。

计算过去 1 分钟内的 ART 平均响应时间:

rate(nodejs_active_request_duration_seconds_sum[1m]) / rate(nodejs_active_request_duration_seconds_count[1m])

4.2 QPS

要查询某一时间段内的 QPS,通常使用 rate() 函数。例如,查询过去 1 分钟内的 QPS(每秒请求数的平均值):

rate(nodejs_qps[1m])
  • nodejs_qps 是计数器指标,它会累积成功请求的数量。

  • rate(nodejs_qps[1m]) 计算过去 1 分钟内的增量速率,即每秒成功请求数的平均值。

过去 15 秒内的 QPS:

rate(nodejs_qps[15s])

查询特定路径、方法或状态码的 QPS(例如,查询 / 路径的 GET 请求成功数):

rate(nodejs_qps{method="GET", path="/"}[1m])

4.3 CPU 负载

4.4 CPU 使用率