3、画像数据模型设计:如何设计一个通用的服务画像数据模型(JSON Schema示例)

服务画像的数据模型是整个画像体系的骨架。一个设计良好的数据模型,需要同时满足三个核心诉求:标准化(跨团队统一理解)、可扩展(适应不同技术栈和业务场景)、可消费(能被监控系统、值班台、告警引擎直接解析)。

本节将基于 JSON Schema 定义一套通用的服务画像数据模型,并拆解其核心字段、分层结构和设计原则。

3.1 数据模型分层架构

画像数据模型采用三层结构,从静态元数据到动态运行时状态,逐层收敛信息:

分层原则:
  • 基础层(Base):服务标识、负责人、Git 仓库、部署环境等不可变或低频变更信息。
  • 运行时层(Runtime):实例数、CPU/内存水位、QPS、错误率、延迟分位数等高频变化指标。
  • 治理层(Governance):SLO 目标、告警规则、变更记录、故障历史、值班交接状态。
服务画像数据模型 — 三层架构 治理层(Governance) SLO 目标 | 告警规则 | 变更记录 | 故障历史 | 值班交接 更新频率:事件驱动 + 每日快照 运行时层(Runtime) 实例数 | CPU/内存 | QPS | 错误率 | P50/P95/P99 延迟 更新频率:1~5 分钟轮询 / 实时流式 基础层(Base) 服务名称 | 负责人 | Git 仓库 | 部署环境 | 技术栈 | 标签 更新频率:低频(变更时更新) 数据流向:基础层 → 运行时层 → 治理层,上层依赖下层提供上下文

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 数据模型与值班台的映射关系

画像数据模型最终要服务于值班台的三个核心场景:服务总览、告警上下文、值班交接。以下映射关系确保模型字段能被值班台直接消费:

画像数据模型 → 值班台场景映射 画像数据模型 基础层 服务名 / 负责人 / 环境 运行时层 实例 / 指标 / 健康状态 治理层 SLO / 告警 / 变更 / 值班 值班台消费场景 服务总览 展示基础信息 + 健康状态 告警上下文 关联指标 + 变更 + 故障历史 值班交接 值班人 + 待处理告警 + 变更 每个值班台场景至少依赖两个数据层的信息,避免单层数据孤岛

3.5 数据模型版本管理与兼容性

版本策略:使用 schemaVersion 字段标记模型版本。当新增字段时,仅递增 minor 版本(如 v1 → v1.1);当删除或重命名字段时,递增 major 版本(如 v1 → v2)。消费端必须同时兼容当前版本和上一个 major 版本。

推荐在画像存储层(如 etcd / Redis / 配置中心)中维护一个 模型版本注册表,记录每个服务当前使用的 schema 版本,便于值班台在渲染时做字段适配。

3.6 数据模型校验与自动化

将 JSON Schema 集成到 CI/CD 流水线中,确保每次服务画像变更都通过校验:

  1. 提交时校验:使用 ajv(JavaScript)或 jsonschema(Python)对画像 JSON 文件执行 schema 校验。
  2. 字段完整性检查:确保必填字段非空,枚举值在允许范围内。
  3. 跨服务一致性检查:扫描所有服务的 base.serviceName,发现重复或命名不规范的服务。
  4. 运行时数据新鲜度检查:如果 runtime.lastUpdated 超过 10 分钟未更新,标记为“数据过期”。
避坑提醒:不要将运行时指标直接嵌入画像 JSON 中存储。运行时数据应通过 API 实时查询,画像只保留最近一次快照的引用或缓存。否则会导致存储膨胀和实时性下降。