4. 水位数据采集:从 Prometheus、Datadog、Grafana 等监控系统采集水位数据的标准化方法
水位数据是变更门禁与回滚判断的量化基础。Backstage 服务目录中的每个实体(Service、Component、API)都需要关联实时的资源水位与健康指标。本章定义从主流监控系统采集水位数据的标准化方法,包括数据模型、采集协议、适配器模式与异常处理策略。
4.1 水位数据模型标准化
无论后端监控系统是什么,Backstage 插件层应统一消费以下水位模型。该模型直接映射到服务目录实体的 spec.watermark 扩展字段。
| 字段 | 类型 | 说明 | 示例 |
|---|---|---|---|
entityRef |
string | Backstage 实体引用(component:default/my-svc) |
component:default/payment-svc |
metricName |
string | 指标标识符 | cpu_usage_percent |
value |
float | 当前水位数值 | 78.5 |
threshold |
float | 该指标的门禁阈值 | 90.0 |
unit |
string | 单位 | %, MB, req/s |
timestamp |
int64 | 采集时间戳(Unix 毫秒) | 1712563200000 |
source |
string | 数据来源系统 | prometheus, datadog |
status |
string | 水位状态:ok, warning, critical |
warning |
value 与 threshold 的比较结果,不关心底层查询语言差异。
4.2 采集架构与适配器模式
采用 Provider 适配器模式 实现多数据源接入。每个监控系统实现一个 WatermarkProvider 接口,Backstage 后端通过依赖注入加载已配置的 Provider 列表。
4.3 Prometheus 适配器实现
Prometheus 是最常见的水位数据源。适配器通过 HTTP API 执行即时查询(Instant Query),按服务标签过滤指标。
4.3.1 查询模板
# 查询模板:按 service_name 标签获取 CPU 使用率
GET /api/v1/query
?query=avg(rate(container_cpu_usage_seconds_total{service="%s"}[5m])) * 100
&time=<timestamp>
# 响应示例
{
"status": "success",
"data": {
"resultType": "vector",
"result": [
{
"metric": { "service": "payment-svc" },
"value": [1712563200, "78.5"]
}
]
}
}
4.3.2 适配器核心逻辑(TypeScript 伪代码)
// PrometheusWatermarkProvider.ts
import { WatermarkProvider, WatermarkData } from './types';
export class PrometheusWatermarkProvider implements WatermarkProvider {
constructor(private baseUrl: string, private queryTemplate: string) {}
async fetchWatermark(entityRef: string, metricName: string): Promise<WatermarkData> {
const serviceName = entityRef.split(':')[2]; // 从 entityRef 提取服务名
const promQL = this.queryTemplate.replace('%s', serviceName);
const url = `${this.baseUrl}/api/v1/query?query=${encodeURIComponent(promQL)}`;
const response = await fetch(url);
const json = await response.json();
if (json.status !== 'success' || json.data.result.length === 0) {
throw new Error(`Prometheus query returned no data for ${entityRef}`);
}
const value = parseFloat(json.data.result[0].value[1]);
return {
entityRef,
metricName,
value,
threshold: 90.0, // 从配置读取
unit: '%',
timestamp: Date.now(),
source: 'prometheus',
status: value >= 90.0 ? 'critical' : value >= 75.0 ? 'warning' : 'ok',
};
}
}
spec.watermark.thresholds 中定义,适配器运行时读取。
4.4 Datadog 适配器实现
Datadog 适配器使用 Metrics API v2,通过 + Application Key 认证。查询时间范围固定为最近 5 分钟,聚合方式为 avg。
| 参数 | 值 | 说明 |
|---|---|---|
filter[query] |
avg:system.cpu.user{service:payment-svc} |
按服务标签过滤 |
filter[from] |
now-5m |
最近 5 分钟 |
filter[to] |
now |
当前时间 |
options[timezone] |
UTC |
时区统一 |
// DatadogWatermarkProvider.ts
export class DatadogWatermarkProvider implements WatermarkProvider {
async fetchWatermark(entityRef: string, metricName: string): Promise<WatermarkData> {
const serviceName = entityRef.split(':')[2];
const query = `avg:system.cpu.user{service:${serviceName}}`;
const url = `https://api.datadoghq.com/api/v2/metrics/${metricName}/data`;
const response = await fetch(url, {
headers: {
'DD-': this.,
'DD-APPLICATION-KEY': this.appKey,
},
body: JSON.stringify({
filter: { query, from: Date.now() - 300000, to: Date.now() },
options: { timezone: 'UTC' },
}),
});
const data = await response.json();
const points = data.data?.attributes?.points || [];
const avgValue = points.length > 0
? points.reduce((sum: number, p: number[]) => sum + p[1], 0) / points.length
: 0;
return this.toWatermark(entityRef, metricName, avgValue);
}
}
4.5 Grafana 适配器(基于 Graphite/InfluxDB)
对于使用 Grafana 作为可视化层、后端为 Graphite 或 InfluxDB 的场景,适配器直接查询 Grafana API 的 /api/ds/query 端点,通过数据源代理获取水位数据。
// GrafanaWatermarkProvider.ts
export class GrafanaWatermarkProvider implements WatermarkProvider {
async fetchWatermark(entityRef: string, metricName: string): Promise<WatermarkData> {
const serviceName = entityRef.split(':')[2];
const grafanaQuery = {
queries: [{
refId: 'A',
datasource: { type: 'prometheus', uid: 'prometheus-uid' },
expr: `avg(rate(cpu_usage{service="${serviceName}"}[5m])) * 100`,
instant: true,
}],
from: Date.now() - 60000,
to: Date.now(),
};
const response = await fetch(`${this.baseUrl}/api/ds/query`, {
method: 'POST',
headers: { 'Authorization': `Bearer ${this.apiToken}` },
body: JSON.stringify(grafanaQuery),
});
const result = await response.json();
const frame = result.results?.A?.frames?.[0];
const value = frame?.data?.values?.[1]?.[0] ?? 0;
return this.toWatermark(entityRef, metricName, value);
}
}
4.6 采集异常处理与降级策略
水位数据采集必须考虑监控系统不可用、超时、数据缺失等异常情况。标准化异常处理流程如下:
- 超时控制:每个 Provider 的 HTTP 请求设置 5 秒超时,超时后触发降级。
- 重试机制:指数退避重试最多 2 次(间隔 1s、3s),仅对 5xx 和网络错误重试。
- 数据缺失处理:如果返回空结果,使用最近一次缓存的水位数据(TTL 60 秒)。
- 熔断:连续 5 次失败后,该 Provider 熔断 30 秒,期间返回
status: unknown,门禁插件跳过该指标判断。
// 异常处理中间件示例
async function withRetryAndFallback(
provider: WatermarkProvider,
entityRef: string,
metricName: string
): Promise<WatermarkData> {
const cacheKey = `${entityRef}:${metricName}`;
const cached = cache.get(cacheKey);
for (let attempt = 0; attempt < 3; attempt++) {
try {
const data = await provider.fetchWatermark(entityRef, metricName);
cache.set(cacheKey, data, 60_000); // 缓存 60 秒
return data;
} catch (err) {
if (attempt === 2) break;
await sleep(1000 * Math.pow(3, attempt)); // 1s, 3s
}
}
// 降级:返回缓存或 unknown 状态
if (cached) return { ...cached, timestamp: Date.now(), source: 'cache' };
return {
entityRef, metricName, value: 0, threshold: 0,
unit: '', timestamp: Date.now(), source: 'fallback',
status: 'unknown',
};
}
4.7 采集配置声明(Backstage Catalog 扩展)
水位采集配置通过 Backstage 服务目录的 catalog-info.yaml 声明,实现服务维度的水位策略绑定。
# catalog-info.yaml 水位采集配置示例
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: payment-svc
annotations:
watermark.io/provider: prometheus
watermark.io/prometheus-host: http://prometheus.internal:9090
watermark.io/metrics: |
- name: cpu_usage
query: 'avg(rate(container_cpu_usage_seconds_total{service="payment-svc"}[5m])) * 100'
threshold: 90
unit: '%'
- name: memory_usage
query: 'container_memory_usage_bytes{service="payment-svc"} / 1048576'
threshold: 2048
unit: 'MB'
spec:
type: service
lifecycle: production
owner: team-payment
4.8 多数据源聚合策略
当同一个服务同时配置了 Prometheus 和 Datadog 两个数据源时,门禁插件需要聚合判断。标准化聚合规则如下:
| 聚合策略 | 行为 | 适用场景 |
|---|---|---|
any |
任一数据源水位达到 critical 即阻断 | 安全敏感指标(错误率、延迟) |
all |
所有数据源均达到 critical 才阻断 | 资源冗余场景(多监控系统互为备份) |
majority |
超过半数数据源达到 critical 时阻断 | 多区域部署,防止单区域误报 |
weighted |
按数据源权重计算综合水位分数 | 混合精度场景(Prometheus 权重 0.7,Datadog 0.3) |
// 聚合判断逻辑
function evaluateAggregatedWatermark(
results: WatermarkData[],
strategy: 'any' | 'all' | 'majority' | 'weighted'
): 'ok' | 'warning' | 'critical' {
const criticalCount = results.filter(r => r.status === 'critical').length;
const total = results.length;
switch (strategy) {
case 'any':
return criticalCount > 0 ? 'critical' : 'ok';
case 'all':
return criticalCount === total ? 'critical' : 'ok';
case 'majority':
return criticalCount > total / 2 ? 'critical' : 'ok';
case 'weighted':
const score = results.reduce((sum, r) => {
const weight = r.source === 'prometheus' ? 0.7 : 0.3;
return sum + (r.value / r.threshold) * weight;
}, 0);
return score >= 1.0 ? 'critical' : score >= 0.8 ? 'warning' : 'ok';
default:
return 'ok';
}
}
通过上述标准化方法,Backstage 门禁插件可以统一、可靠地从 Prometheus、Datadog、Grafana 等系统采集水位数据,为后续的变更门禁判断和回滚决策提供实时、准确的数据基础。