3、画像数据模型设计:如何设计一个通用的服务画像数据模型(JSON Schema示例)
服务画像的数据模型是整个画像体系的骨架。一个设计良好的数据模型,需要同时满足三个核心诉求:标准化(跨团队统一理解)、可扩展(适应不同技术栈和业务场景)、可消费(能被监控系统、值班台、告警引擎直接解析)。
本节将基于 JSON Schema 定义一套通用的服务画像数据模型,并拆解其核心字段、分层结构和设计原则。
3.1 数据模型分层架构
画像数据模型采用三层结构,从静态元数据到动态运行时状态,逐层收敛信息:
分层原则:
- 基础层(Base):服务标识、负责人、Git 仓库、部署环境等不可变或低频变更信息。
- 运行时层(Runtime):实例数、CPU/内存水位、QPS、错误率、延迟分位数等高频变化指标。
- 治理层(Governance):SLO 目标、告警规则、变更记录、故障历史、值班交接状态。
3.2 JSON Schema 完整定义
以下 JSON Schema 覆盖三层核心字段,并包含必要的校验规则和默认值。实际落地时可根据团队需求裁剪或扩展 extensions 字段。
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://sre.team/service-profile/v1",
"title": "ServiceProfile",
"description": "SRE 值班台服务画像通用数据模型",
"type": "object",
"required": ["base", "runtime", "governance"],
"properties": {
"base": {
"type": "object",
"description": "基础层:服务元数据",
"required": ["serviceName", "owner", "repoUrl", "environments"],
"properties": {
"serviceName": {
"type": "string",
"pattern": "^[a-z0-9-]{3,64}$",
"description": "服务唯一标识,小写字母、数字、连字符"
},
"displayName": {
"type": "string",
"maxLength": 128,
"description": "服务展示名称"
},
"owner": {
"type": "object",
"required": ["team", "email"],
"properties": {
"team": { "type": "string" },
"email": { "type": "string", "format": "email" },
"slackChannel": { "type": "string" }
}
},
"repoUrl": {
"type": "string",
"format": "uri",
"description": "主代码仓库地址"
},
"environments": {
"type": "array",
"items": {
"type": "string",
"enum": ["dev", "staging", "prod", "dr"]
},
"minItems": 1
},
"techStack": {
"type": "object",
"properties": {
"language": { "type": "string" },
"framework": { "type": "string" },
"runtime": { "type": "string" }
}
},
"tags": {
"type": "array",
"items": { "type": "string" },
"description": "自定义标签,用于分类和搜索"
}
}
},
"runtime": {
"type": "object",
"description": "运行时层:当前服务状态",
"required": ["instances", "health"],
"properties": {
"instances": {
"type": "integer",
"minimum": 0,
"description": "当前运行实例数"
},
"health": {
"type": "string",
"enum": ["healthy", "degraded", "unhealthy", "unknown"]
},
"metrics": {
"type": "object",
"properties": {
"qps": { "type": "number", "minimum": 0 },
"errorRate": { "type": "number", "minimum": 0, "maximum": 1 },
"latencyMs": {
"type": "object",
"properties": {
"p50": { "type": "number", "minimum": 0 },
"p95": { "type": "number", "minimum": 0 },
"p99": { "type": "number", "minimum": 0 }
}
},
"cpuUsage": { "type": "number", "minimum": 0, "maximum": 100 },
"memoryUsage": { "type": "number", "minimum": 0, "maximum": 100 }
}
},
"lastUpdated": {
"type": "string",
"format": "date-time",
"description": "运行时数据采集时间戳"
}
}
},
"governance": {
"type": "object",
"description": "治理层:SLO、告警与变更",
"required": ["slo", "alerts"],
"properties": {
"slo": {
"type": "object",
"properties": {
"target": {
"type": "number",
"minimum": 0,
"maximum": 1,
"description": "SLO 目标值,如 0.999"
},
"window": {
"type": "string",
"enum": ["28d", "30d", "90d", "custom"]
},
"currentBurnRate": {
"type": "number",
"description": "当前燃烧速率"
}
}
},
"alerts": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": { "type": "string" },
"severity": {
"type": "string",
"enum": ["critical", "warning", "info"]
},
"status": {
"type": "string",
"enum": ["firing", "resolved", "acknowledged"]
},
"startedAt": { "type": "string", "format": "date-time" }
}
}
},
"changeHistory": {
"type": "array",
"items": {
"type": "object",
"properties": {
"changeId": { "type": "string" },
"type": {
"type": "string",
"enum": ["deploy", "config", "rollback", "scale"]
},
"timestamp": { "type": "string", "format": "date-time" },
"actor": { "type": "string" }
}
}
},
"incidentHistory": {
"type": "array",
"items": {
"type": "object",
"properties": {
"incidentId": { "type": "string" },
"severity": { "type": "string" },
"startedAt": { "type": "string", "format": "date-time" },
"resolvedAt": { "type": "string", "format": "date-time" },
"summary": { "type": "string" }
}
}
},
"oncall": {
"type": "object",
"properties": {
"primary": { "type": "string" },
"secondary": { "type": "string" },
"rotationSchedule": { "type": "string" }
}
}
}
},
"extensions": {
"type": "object",
"description": "扩展字段,用于团队自定义属性",
"additionalProperties": true
},
"schemaVersion": {
"type": "string",
"default": "v1",
"description": "数据模型版本号,用于向后兼容"
}
}
}
3.3 核心字段设计要点
| 字段路径 | 设计要点 | 常见陷阱 |
|---|---|---|
base.serviceName |
使用小写连字符命名,全局唯一;禁止包含环境信息 | 命名中包含环境(如 order-prod),导致跨环境复用困难 |
runtime.metrics |
延迟分位数必须包含 P50/P95/P99;错误率使用小数(0~1) | 只采集平均值,掩盖长尾问题;错误率用百分比导致计算混乱 |
governance.slo.target |
统一使用小数表示(如 0.999),与 Prometheus/OpenTelemetry 对齐 | 使用“三个九”“四个九”等非标准表述,无法直接参与计算 |
governance.alerts |
每个告警必须携带 startedAt 时间戳,便于值班台排序 |
缺少时间戳,告警列表无法按时间线正确展示 |
extensions |
开放给团队自定义,但必须遵守 additionalProperties: true 约束 |
过度使用扩展字段替代标准字段,导致跨团队数据无法互通 |
3.4 数据模型与值班台的映射关系
画像数据模型最终要服务于值班台的三个核心场景:服务总览、告警上下文、值班交接。以下映射关系确保模型字段能被值班台直接消费:
3.5 数据模型版本管理与兼容性
版本策略:使用
schemaVersion 字段标记模型版本。当新增字段时,仅递增 minor 版本(如 v1 → v1.1);当删除或重命名字段时,递增 major 版本(如 v1 → v2)。消费端必须同时兼容当前版本和上一个 major 版本。
推荐在画像存储层(如 etcd / Redis / 配置中心)中维护一个 模型版本注册表,记录每个服务当前使用的 schema 版本,便于值班台在渲染时做字段适配。
3.6 数据模型校验与自动化
将 JSON Schema 集成到 CI/CD 流水线中,确保每次服务画像变更都通过校验:
- 提交时校验:使用
ajv(JavaScript)或jsonschema(Python)对画像 JSON 文件执行 schema 校验。 - 字段完整性检查:确保必填字段非空,枚举值在允许范围内。
- 跨服务一致性检查:扫描所有服务的
base.serviceName,发现重复或命名不规范的服务。 - 运行时数据新鲜度检查:如果
runtime.lastUpdated超过 10 分钟未更新,标记为“数据过期”。
避坑提醒:不要将运行时指标直接嵌入画像 JSON 中存储。运行时数据应通过 API 实时查询,画像只保留最近一次快照的引用或缓存。否则会导致存储膨胀和实时性下降。