Files
Qiniu/xinfra/decision-record.md
T

267 lines
22 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
tags: [xinfra, requirements, decision]
create time: 2026-07-10 10:30
---
# XINFRA PRD 优化决策记录
## 概述
本文档记录 [[requirements|XINFRA 平台需求文档]] 各优化点的方案对比与决策依据,供团队同步使用。每个修改点均以 **方案 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 资源面板范围 | 只写平台团队实际要交付的内容 |