Files
Qiniu/xinfra/decision-record.md

22 KiB
Raw Permalink Blame History

tags, create time
tags create time
xinfra
requirements
decision
2026-07-10 10:30

XINFRA PRD 优化决策记录

概述

本文档记录 requirements 各优化点的方案对比与决策依据,供团队同步使用。每个修改点均以 方案 A(原设计)vs 方案 B(最终选择) 的形式呈现。

所有优化的共同方向:让 PRD 精确描述"平台开发团队需要交付什么",把"依赖什么"和"不需要做什么"从需求列表中剥离出来。 这不是缩小项目范围——基础设施仍然会建、监控仍然会用、网络仍然会通——只是把不属于平台开发团队的工作归还给对应的团队,让 PRD 的读者对实际开发工作量有准确的预期。


1. 统一认证模型

方案 A:1:1:1 用户映射(原设计)

主系统自建用户表,维护 LDAP 账号 ↔ 主系统用户 ↔ 子系统用户的三者一一对应关系。主系统管身份(Authentication),子系统管授权(Authorization)。用户首次登录主系统时自动在子系统中创建对应账号,后续通过映射关系同步状态。

方案 B:LDAP SSO 跳转(最终选择)

主系统只做 LDAP SSO 跳板 + 导航入口。用户通过主系统门户选择目标子系统后,系统通过 LDAP 认证代入身份,直接跳转到子系统。各子系统直接对接企业 LDAP 做认证,权限仍由各子系统自行管理。

对比

维度 方案 A(1:1:1 映射) 方案 B(SSO 跳转)
主系统开发量 需自建用户注册/注销接口、用户表 CRUD、映射关系维护、子系统账号同步 Worker 只需一个 LDAP 登录页 + 子系统跳转链接,无用户表
子系统改造量 每个子系统需新增"接收主系统同步账号"的 API,并处理账号冲突、状态覆盖等边界情况 零改造——Wayne / CloudDM / CacheCloud 均已原生支持 LDAP,直接配置即可
新增子系统成本 每接入一个子系统都要开发同步接口 + 映射逻辑 + 联调测试 子系统配置 LDAP 连接即完成接入,无额外开发
典型故障场景 用户在 LDAP 离职后,主系统映射表残留 → 子系统仍显示该用户;用户改名后三方数据不一致;子系统同步失败导致"主系统能登录但子系统报权限不存在" 无映射层,LDAP 是唯一身份源,LDAP 账号失效后所有子系统自然无法登录
数据一致性 需定时对账三张用户表(LDAP / 主系统 / 子系统),发现漂移后人工或自动修复 天然一致——每次登录实时读 LDAP,无缓存映射
现有基础设施 需评估 Wayne / CloudDM / CacheCloud 是否暴露了账号管理 API 供主系统调用 三个子系统均已支持 LDAP Bind 认证,配置 LDAP Server 地址和 Base DN 即可
运维复杂度 多一个数据库表 + 一个同步服务需要监控和维护 无额外运维组件

决策理由

Wayne、CloudDM、CacheCloud 三者都原生支持 LDAP 认证——这不是需要开发的能力,而是各子系统开箱即用的配置项。在子系统已经能直接对接 LDAP 的前提下,主系统在中间再加一层用户映射,本质是在没有问题的地方制造问题。

方案 A 的真正代价不在于初次开发(建表 + 同步接口大约 1-2 周),而在于长期维护:每接入一个新子系统都要对接映射接口;用户在 LDAP 侧发生变更(离职、改名、部门调动)时需要一套同步机制保证三方一致;映射表本身也是一个需要备份、监控、排障的有状态组件。这些工作量会随着子系统数量线性增长。

方案 B 将 LDAP 作为唯一的身份源(Single Source of Truth),主系统只做跳转入口。这不仅消除了映射不同步的故障模式,也让"新增子系统"这件事从"开发同步接口 + 联调"降级为"配置 LDAP 连接"——一个运维操作,零开发成本。


2. 主系统面板范围

方案 A:统一 DashBoard(原设计)

主系统自建一个全功能面板,同时聚合四类数据:

面板模块 数据来源 技术实现
审计日志 子系统通过 HTTP Webhook 上报 自建接收 + 存储 + 查询
监控指标 通过 PromQL 查询 Prometheus 需实现 PromQL 查询引擎 + 图表渲染
安全事件 WAF 拦截事件 + 异常登录检测 需接入 WAF 日志流 + 登录行为分析
告警概览 各子系统告警汇总,按严重级别分组 需对接各子系统告警 API 或 Prometheus Alertmanager

方案 B:审计面板 + Grafana 复用(最终选择)

主系统只自建审计日志聚合:

面板模块 处理方式 理由
审计日志 主系统自建(Webhook 接收 + 存储 + 按业务字段检索) 结构化业务数据,Grafana 不擅长
监控指标 直接复用 Grafana Dashboard Grafana + Prometheus 已原生支持
安全事件 在审计面板中作为审计日志的一个子类展示 WAF 拦截事件本质也是"操作审计",无需独立模块
告警概览 直接复用 Grafana + Alertmanager Grafana 已原生支持告警汇总和分组

对比

维度 方案 A(全自建) 方案 B(审计 + Grafana)
前端开发量 4 个面板模块(审计表格、监控图表、安全事件列表、告警卡片),每个需要独立的筛选器、图表组件、权限控制 1 个模块(审计日志查询页),监控/告警零前端开发
后端开发量 审计 Webhook 接收 + PromQL 查询代理 + Alertmanager API 代理 + 安全日志解析 + 4 套查询接口 审计 Webhook 接收 + 存储 + 查询,共 1 套接口
运维组件 需维护自建面板的数据库、缓存、前端静态资源 只需维护审计日志的存储(可复用主系统已有数据库)
数据一致性 自建面板的监控数据与 Grafana 来自同一 Prometheus,但刷新间隔/聚合方式可能不同 → 用户看到两个"不一样的 CPU 使用率" Grafana 是唯一监控源,不存在分歧
Grafana 能力覆盖 监控指标展示(已支持)、告警汇总(已支持)、多数据源(已支持)、RBAC(已支持)——重复建设 100% 复用已有能力
审计日志特殊性 — 审计日志是结构化业务数据(操作人、操作类型、工单号、时间范围),需要按业务字段组合检索,Grafana 的 LogQL/SQL 面板不适合这类场景,确实需要自建
用户体验 用户在主系统看监控 → 发现异常 → 去 Grafana 看详情 → 两套系统数据对不上 → 困惑 用户在主系统看审计,去 Grafana 看监控,职责清晰不混淆

决策理由

方案 A 的核心问题是用自研代码重新实现了 Grafana 已经做好的事。具体来说:

  • 监控指标面板:Prometheus → Grafana 的链路已经是行业标准,Grafana 原生支持 PromQL 查询、变量下钻、告警规则、多数据源聚合。自建一套 PromQL 查询引擎 + 图表渲染,开发量至少 2-3 周,且最终效果大概率不如 Grafana。
  • 告警概览:Alertmanager 已经提供了按严重级别分组、静默、路由的能力,Grafana 的 Alerting 模块可以直接展示。自建等于维护两套告警查看入口。
  • 安全事件:WAF 拦截事件本质上也是一种"操作审计"(某个 IP 在某个时间被拦截了某个请求),完全可以归入审计日志体系,按事件类型分类展示,不需要独立建一个安全事件模块。

审计日志是唯一 Grafana 做不好的部分。审计日志的典型查询是"张三在 7 月 10 号对 production 数据库执行了哪些操作",这需要按操作人、操作类型、目标资源、时间范围等业务字段做组合筛选——这是结构化数据检索的场景,Grafana 面向时序数据的设计不适合。因此主系统自建审计面板是合理的,但把监控和告警也自建就是重复劳动。


3. CI/CD 引擎选型

方案 A:双引擎架构(原设计)

PRD 标题为"双引擎架构",支持 GitLab CI 和另一引擎(如 Jenkins)。这意味着需要:

需要做的事 具体内容
抽象流水线 DSL 设计一套统一的 Pipeline 配置格式,能翻译为 GitLab CI YAML 和 Jenkinsfile
引擎适配层 分别对接 GitLab CI API 和 Jenkins API,处理两者的差异(认证方式、触发机制、状态回调格式、日志获取方式)
统一查看界面 自建流水线状态页,聚合两个引擎的执行记录,因为 GitLab CI 的 Pipeline 页面只能看到自己的流水线
配置管理 同一套项目在两个引擎中的配置同步(变量、Secret、Runner 配置)

方案 B:GitLab CI 单引擎(最终选择)

明确只用 GitLab CI 一个引擎。CI/CD 全部通过 .gitlab-ci.yml 定义,直接调用 GitLab CI API 触发 Pipeline。流水线执行状态和日志查看直接复用 GitLab CI 原生 Pipeline 页面,平台不自建查看界面。

对比

维度 方案 A(双引擎) 方案 B(单引擎)
开发工作量 DSL 抽象层 + 两套 API 适配 + 统一查看界面 + 配置同步,预估 4-6 周 直接调用 GitLab CI REST API,复用 .gitlab-ci.yml,预估 1 周
DSL 抽象复杂度 需要定义一套比 GitLab CI YAML 和 Jenkinsfile 更通用的格式,同时不能丢失任一引擎的特有功能(如 GitLab 的 rules: / Jenkins 的 parallel 语法差异) 不需要抽象,直接用 .gitlab-ci.yml
调试体验 Pipeline 失败后需要先定位是 DSL 翻译层的 bug 还是引擎本身的 bug 直接在 GitLab CI 页面看错误,无中间层
维护成本 两个引擎版本升级都可能破坏适配层(GitLab CI API breaking changes / Jenkins 插件兼容性) 只跟进 GitLab CI 一个
统一查看界面 需自建前端页面,聚合两引擎执行记录 零开发——GitLab CI Pipeline 页面已原生支持
实际使用场景 公司内部只用 GitLab CI,Jenkins 无实际使用团队 与实际场景完全匹配
未来扩展性 提前抽象,但抽象边界很难预判(第二个引擎是什么?API 什么样?) 需要时再抽象,此时已有 GitLab CI 的完整使用经验,抽象更准确

决策理由

方案 A 属于典型的为不存在的需求做过度设计。公司内部目前只使用 GitLab CI,没有任何团队在用 Jenkins 或其他引擎。在这种情况下,"双引擎"架构的实际效果是:花了 4-6 周开发一套 DSL 翻译层,其中 GitLab CI 那一半只相当于直接调用 API 的 1 周工作量,Jenkins 那一半在可预见的未来不会被使用。

更关键的是抽象时机问题。在只有一个引擎的使用经验时做抽象,几乎必然会在抽象边界上犯错——你不知道第二个引擎的 API 是什么样的,不知道它的 Pipeline 模型和 GitLab CI 有多大差异,不知道哪些概念可以统一、哪些必须分别处理。等到真正需要引入第二引擎时,大概率要重写抽象层。先做好一件事,等第二件来了再抽象,比提前猜错要省得多。


4. 基础设施类需求的归属

方案 A:WAF / 大内网 / Ansible 作为功能需求(原设计)

FR-6(Ansible 自动化部署)、FR-7(WAF 安全态势)、FR-8(大内网互联)与 Wayne、CloudDM、CacheCloud 并列,作为平台的 10 个功能需求之一,需排优先级、写验收标准、分配开发人员。

方案 B:归入基础设施依赖(最终选择)

将三者移至「基础设施依赖」章节,注明由基础设施团队负责,平台开发团队只需了解其约束(如"跨机房 Pod 通信延迟 < 2ms"),不负责实施。

对比

维度 方案 A(作为功能需求) 方案 B(作为基础设施依赖)
PRD 中的呈现 与 Wayne、CloudDM 同级,共 10 个 FR,每个有编号、需求表、验收标准 独立章节,只描述约束和 SLA,不排开发优先级
读者理解 非技术读者(产品、管理层)会认为这 10 个 FR 都是平台团队要交付的 明确标注"非平台开发范围",避免职责混淆
排期影响 10 个 FR 需要排入同一份开发计划,团队压力和管理层预期都会膨胀 6 个 FR 排入开发计划,范围真实可控
验收责任 平台团队需要对 WAF 防护效果、网络延迟 SLA 负责,即使这些不由自己实施 基础设施团队负责验收,平台团队只负责"在基础设施就绪的前提下,平台功能正常"
变更影响 网络架构调整时需要同时修改 PRD 的 FR-8 和基础设施团队自己的文档,容易不一致 基础设施团队独立维护自己的文档,PRD 只引用约束
实际执行者 FR-6 的 Ansible Playbook 由 SRE 编写;FR-7 的 WAF 规则由安全团队配置;FR-8 的网络链路由网络团队搭建——没有一条是平台开发团队执行的 与实际执行者一致

决策理由

把 WAF、大内网、Ansible 列为功能需求,最直接的后果是管理层和协作方对平台团队的交付预期被错误放大。当 PRD 中列了 10 个 FR,而团队实际只能负责其中 6 个的开发,剩下 3 个要等基础设施团队就绪后"验收",这会在排期评审中造成困惑:"FR-7 WAF 的开发进度怎么样了?"——答案是"它不是我们开发的",但 PRD 的结构没有体现这一点。

更深层的问题是验收责任错位。如果 FR-7 作为功能需求,平台团队需要对"WAF 覆盖所有对外暴露的 HTTP/HTTPS 入口"这条验收标准负责。但 WAF 的部署和规则配置是安全团队的工作,平台团队既没有权限也没有能力去验收它。把验收标准挂在错误的团队名下,最终只会导致要么验收流于形式,要么平台团队替基础设施团队背锅。

方案 B 将三者移至「基础设施依赖」章节,明确传递一个信息:这些是平台运行的前提条件,由对应的团队负责交付,平台团队只关心它们的约束和 SLA。这让每个团队只对自己实际执行的工作负责。


5. RKE2 集群需求的粒度

方案 A:详细配置参数(原设计)

FR-1 包含 6 条需求,详细描述 RKE2 的技术配置:

需求编号 内容 信息类型
FR-1.1 每个机房独立部署 RKE2,cluster-cidr(10.42.0.0/16),service-cidr(10.43.0.0/16) 网络规划参数
FR-1.2 Canal(Calico + Flannel)CNI,kube-proxy iptables 模式 组件选型参数
FR-1.3 支持 Air-gap 离线部署 能力约束
FR-1.4 节点注册支持静态 Token 和 Bootstrap 证书签名两种模式 部署方式参数
FR-1.5 containerd 运行时,不依赖 Docker 组件选型参数
FR-1.6 Systemd 服务管理方式部署 部署方式参数

方案 B:约束表格 + 引用 SOP(最终选择)

FR-1 精简为一张 4 行约束表格,只保留平台开发团队需要感知的信息:

约束项 说明 对平台团队的影响
CNI 插件 Canal(Calico + Flannel) Wayne 通过 Client-Go 操作 K8s 对象时需知 CNI 行为
容器运行时 containerd(不依赖 Docker) 镜像构建和调试工具选型不依赖 Docker CLI
离线部署 支持 Air-gap 环境 所有依赖包需预先下载,镜像推送到内网 Harbor
集群规模 七机房各一套独立集群 Wayne 需管理多集群连接

详细部署规范(网络 CIDR、节点注册模式、Systemd 配置等)引用运维 SOP 文档。

对比

维度 方案 A(详细参数) 方案 B(约束 + SOP)
信息定位 混合了"平台约束"和"运维配置"两类信息 只保留"平台约束",运维配置归 SOP
读者适配 开发人员读到大量网络 CIDR 和节点注册模式参数——这些不影响他们写代码 开发人员只看到影响自己工作的 4 条约束
运维人员适配 运维需要的部署步骤散落在 PRD 的需求表中,无法独立查阅 集中在 SOP 文档,按操作场景组织("初始化集群"、"扩容节点"、"升级版本")
变更影响面 service-cidr 规划调整 → 需要走 PRD 评审流程修改 FR-1.1 service-cidr 调整 → 只改 SOP 文档,PRD 无变动
信息权威性 PRD 不是运维操作手册,放网络配置参数会导致"以 PRD 为准还是以 SOP 为准"的歧义 SOP 是运维操作的单一权威来源,无歧义
文档体积 FR-1 占 PRD 约 15% 篇幅 FR-1 占 PRD 约 3% 篇幅

决策理由

PRD 和 SOP 服务于不同的读者和不同的生命周期:

  • PRD 的读者:产品、开发、管理层。他们需要知道"平台依赖什么基础设施",以便理解技术约束和排期风险。
  • SOP 的读者:SRE、运维。他们需要知道"具体怎么部署",包括每一步的命令、参数、验证方式。

方案 A 把这两类信息混在一起,导致两个问题:第一,开发人员在 PRD 中读到 cluster-cidr 和节点注册模式时,这些信息对他们写代码没有任何帮助,只是阅读噪音;第二,运维人员在 PRD 中找到的部署参数不完整(只有选型结论没有操作步骤),他们最终还是需要一份独立的 SOP 文档,等于维护了两个"半成品"。

方案 B 让 PRD 只承担它该承担的职责——告诉读者"RKE2 集群的这些约束会影响平台设计"——而把部署操作的完整细节交给 SOP。当运维团队调整网络规划或节点注册方式时,只需改 SOP,PRD 保持不变。职责清晰,维护成本最低。


6. 资源指标面板的覆盖范围

方案 A:多云 + 自建机房全自研(原设计)

FR-9 要求资源指标面板同时覆盖:

数据源 接入方式 复杂度
AWS Cost Explorer API + CUR(Cost and Usage Reports),按服务/账户/标签维度 高——API 有请求频率限制,账单数据 T+1 到 T+3 延迟,预留实例分摊需独立计算
阿里云 账单 API + BSS(Business Support System),按产品/实例/标签维度 高——账单格式与 AWS 完全不同,需独立适配
腾讯云 费用中心 API,按项目/资源/标签维度 高——第三套格式,又有自己的分账逻辑
自建机房 Prometheus 节点指标 + SNMP 网络指标 低——数据已在 Prometheus 中

还需实现统一数据模型(将三家云厂商的不同账单结构归一化)、汇率换算、按部门/项目/机房聚合、月度趋势对比、闲置资源告警。

方案 B:初期只做自建机房(最终选择)

只覆盖自建机房的资源开销,数据源为 Prometheus 节点指标 + SNMP 网络指标,按 Namespace → Project → Department 路径归属。多云账单接入推至后续迭代。

对比

维度 方案 A(多云 + 自建) 方案 B(只做自建)
数据源适配工作量 3 套云 API 适配(认证、分页、账单格式解析)+ 1 套 Prometheus 查询 1 套 Prometheus 查询
数据标准化 需建统一数据模型,将 AWS CUR / 阿里云 BSS / 腾讯云费用中心的账单字段归一化(服务名映射、计费单位统一、分账标签对齐) Prometheus 指标格式天然统一,只需按 K8s Namespace 做归属映射
数据延迟 AWS T+1~T+3,阿里云 T+1,腾讯云 T+1,需处理"部分云数据已到、部分未到"的中间态 Prometheus 实时采集,分钟级延迟
预留/节省计划 AWS Reserved Instance / Savings Plan、阿里云预留实例券等需按摊销方式计算实际成本,每家逻辑不同 无此问题——自建机房无预留实例概念
前端展示 需支持"云厂商"维度的筛选和对比 只有机房维度,前端逻辑简单
交付周期 多云适配 + 数据标准化 + 前端展示,预估 4-6 周 Prometheus 查询 + 归属映射 + 前端展示,预估 1-2 周
实际痛点 多云账单多数团队已有各自查看方式(AWS Console / 阿里云控制台),统一面板是锦上添花 自建机房开销目前是真正的管理盲区——没有统一归集,各团队不知道自己的物理机用了多少电、多少带宽

决策理由

方案 A 的核心风险不在于技术难度,而在于投入产出比极低。对接多云 Billing API 的工作量不亚于一个独立产品线:

仅 AWS 一家,Cost Explorer API 就有按 Service / Account / Tag / Region 等多种维度的查询方式,账单数据有 T+1 到 T+3 的延迟,Reserved Instance 和 Savings Plan 需要按摊销方式计算实际成本(不是简单的月度费用除以 30 天)。阿里云和腾讯云各有一套完全不同的账单结构和分账逻辑。三家云厂商的"服务名"都不统一(AWS 叫 EC2,阿里云叫 ECS,腾讯云叫 CVM),需要建一层服务名映射才能做跨云对比。这些都是脏活累活,但对用户的价值有限——每个云厂商自己的控制台已经提供了详细的账单查看能力。

自建机房的资源开销则完全不同:Prometheus 节点指标已经存在(CPU / 内存 / 磁盘 / 网络),数据格式统一,只需建一层 K8s Namespace → Project → Department 的归属映射就能产出价值。这是目前真正"没人管"的管理盲区——没有统一归集,各团队不清楚自己的物理机资源利用率和开销。

先解决最痛的盲区,多云账单留到二期。二期启动时,一期已经积累的"部门/项目归属映射"可以直接复用于多云场景,不需要推倒重来。


总结

以上 6 个优化点可以归纳为两类决策逻辑:

决策逻辑 涉及的优化点 核心原则
砍掉不必要的自研 #1 认证模型、#2 DashBoard、#3 CI/CD 引擎 开源工具已有的能力不重复造轮子
厘清职责边界 #4 基础设施归属、#5 RKE2 粒度、#6 资源面板范围 只写平台团队实际要交付的内容