# PJ2026-01060107 NC01 CI资源治理

## 修改历史

| 版本 | 对应 commit id | 更新日期 | 变更说明 |
| --- | --- | --- | --- |

当前正文仍在规格治理草稿中；未定稿前不新增版本号，不为单次编辑追加 `待提交` 版本。

## 正文

## PJ2026-01060107 NC01 CI资源治理需求规格

## 1. 文档控制

| 字段 | 内容 |
| --- | --- |
| 编号 | PJ2026-01060107 |
| 短名 | NC01 CI资源治理 |
| 层级 | L3 子课题 |
| 状态 | 草稿 |
| 实现引用版本 | draft-2026-07-21-p0-nc01-ci-resource-governance |
| 需求规格模板 | [ISO/IEC/IEEE 29148 需求规格模板](../../templates/iso-iec-ieee-29148-requirements-spec-template.md) |
| 上级规格 | [PJ2026-010601 发布流水](PJ2026-010601-controlled-release.md) |
| 关联规格 | [PJ2026-010603 YAML运维](PJ2026-010603-yaml-first-ops.md)、[PJ2026-01060308 CI/CD YAML-first目标治理](PJ2026-01060308-cicd-yaml-first-target-governance.md) |
| 跟踪任务 | pikasTech/unidesk#2721、TaskTree `tg_743359ba-58aa-426b-a6f0-4df84fb1d795` |

本文定义 NC01 上 CI 构建并发、排队、资源合同、稳定去重和核心运行面保护。所有可调事实由 owning YAML 控制，代码只校验、渲染和观察；禁止手改 Pipeline、PipelineRun、TaskRun、Pod 或运行中 workload 来补齐合同。

## 2. 事故与目标

单个 HWLAB PipelineRun 曾同时展开 14 个 `build-services` TaskRun，使 8 核 NC01 的 load、Swap 和 k3s Kine/SQLite 延迟同时上升，并造成核心 API 约四分钟 502。PaC Repository 的 `concurrencyLimit` 只限制 Repository 触发层的并发 PipelineRun，不能限制一个 PipelineRun 内 matrix 展开的 TaskRun 数量。

治理目标是让 PR merge 和 branch update 不再产生发布运行；每个经人工审阅的发布意图只产生一个受控运行，并让单次运行中的构建工作按 YAML 预算进入有界 worker 队列。CI 压力下，k3s 控制面、CoreDNS、Sub2API 和 Public Edge 的可用性优先于构建吞吐。

## 3. 架构与数据流

```mermaid
flowchart LR
  Y[owning YAML] --> V[配置校验]
  V --> R[HWLAB Pipeline renderer]
  R --> D[单 TaskRun 有界 worker 队列]
  D --> T[Tekton TaskRun/Pod]
  T --> K[Kubernetes 调度]
  T --> O[plan/status/diagnosis]
  K --> C[核心运行面保护]
```

受控手动发布触发和 Pipeline 内构建排队是两个独立层次。前者由发布 `plan`、人工审阅和带计划指纹的 `trigger` 管理，PaC Repository `concurrencyLimit` 只作为运行并发上限；后者由 renderer 将目标 matrix 收敛为一个受资源约束的 TaskRun，并在 Pod 内按 `maxParallelServices` 执行有界 worker 队列。不得复制大型内联 `taskSpec`，不得把二者合并为同名字段，也不得创建数据库、ConfigMap 或常驻控制器作为第二队列 authority。

## 4. 配置合同

HWLAB node/lane target 的 `ciResourceGovernance` 至少声明：

- `build.maxParallelServices`：单个构建 TaskRun 内最多并行服务数。
- `build.taskTimeout`：单个构建 TaskRun 的 Tekton timeout。
- `build.stepResources`：构建工具 step 的 CPU/内存 requests 与 limits。
- `build.sidecarResources`：BuildKit sidecar 的 CPU/内存 requests 与 limits。
- `build.priorityClassName`：构建 Pod 的可抢占优先级；必须低于核心运行面。
- `deliveryTrigger.mode`：L2/L3 固定为 `manual-plan-confirm`，禁止配置自动 push、merge 或 branch follower 触发。
- `deliveryTrigger.mechanism`：固定为 `pac-webhook`；受控 CLI 发送 webhook，禁止直建 PipelineRun。
- `dedupe.identityFields`：稳定意图键字段，固定为 repository、lane、source commit 和 pipeline intent。
- `coreProtection`：核心服务优先级和最小资源预算的配置引用；各服务事实仍归自身 owning YAML，不复制进 HWLAB lane YAML。

资源字段使用 Kubernetes quantity 字符串。requests 不得超过 limits，CPU 和内存必须同时声明。NC01 默认同时运行的构建服务数不得由代码、环境变量或运行面探测回填。

## 5. 原子需求

### 5.1 CI-RESOURCE-REQ-001 分层并发与排队

Renderer 必须只对 YAML 选中的构建 matrix 建立有界 worker 队列。单个构建 TaskRun 同时工作的服务数不得超过 `maxParallelServices`，每个服务使用独立工作目录和结果目录，任一批失败后不得继续下一批。原先依赖完整 matrix 的后续任务仍依赖该构建任务，非构建任务及其既有依赖语义保持不变。

### 5.2 CI-RESOURCE-REQ-002 资源合同

构建 TaskRun 的工具 step 和 BuildKit sidecar 必须带 YAML 声明的 `computeResources`。构建 Pod 使用低于核心服务的 PriorityClass；资源不足时允许构建排队或被抢占，不允许核心 API、DNS 或控制面为构建让路。

### 5.3 CI-RESOURCE-REQ-003 稳定去重

手动触发的发布意图键固定为 `repository + lane + source commit + pipeline intent`。相同键已处于 queued、running 或 succeeded 时，受控入口不得再创建第二次构建。人工重跑必须使用显式受控入口，携带独立、可审计的 rerun intent，并先重新 plan；不得通过删除旧 PipelineRun、修改 label 或人工创建 PipelineRun 绕过去重。

### 5.4 CI-RESOURCE-REQ-004 核心运行面保护

CoreDNS 和 k3s 控制面使用 Kubernetes 系统关键优先级。Sub2API 和集群内 Public Edge 使用平台核心优先级与自身 owning YAML 的 requests/limits。Host 运行的 Public Edge 使用其 owning YAML 声明的 CPU/内存保留或限制。任何保护变更必须通过相应受控 CLI 渲染和部署，禁止从 live workload 反解后强写。

### 5.5 CI-RESOURCE-REQ-005 状态与诊断

`plan` 必须针对精确 source commit 在一次只读输出中披露：YAML configRef、env identity、env reuse/build 判定、待构建镜像数量与服务列表、待 rollout 服务、全部受影响服务、范围基线、新增范围、触发层 concurrency、构建层 max parallel、批次数、step/sidecar 资源合同、priority class 和核心保护摘要。`status` 和 `diagnosis` 必须披露运行/排队 TaskRun 数与稳定意图键状态。共享读取超时只能投影为 warning 或 unavailable，不得把全部 consumer 伪装为真实失败。

- Renderer 范围判定必须符合以下规则：
  - renderer 输入或配置变化会重建完整 runtime tree，并改写未选 workload 的 source identity 时，`plan` 必须显示全部实际 rollout 服务；
  - 镜像构建范围继续由 env、组件输入和 artifact provenance 独立决定；
  - 禁止用局部源码影响范围替代实际 GitOps Pod template diff；
  - 只有未选 workload 的 manifest 和 source identity 均保持不变时，才允许报告局部 rollout。

### 5.6 CI-RESOURCE-REQ-006 中心自动触发禁用

中心 PaC owning YAML 必须声明 `deliveryTrigger.mode=manual-plan-confirm` 和 `deliveryTrigger.mechanism=pac-webhook`。PaC webhook 接收端继续作为唯一 PipelineRun 创建入口，但 Gitea push hook、PR merge callback、branch follower、poller 和其他自动发送方不得调用它；PR merge 后只允许 source mirror 或 authority 更新。受控 apply 必须删除或禁用 Gitea 自动 hook，并在 status 中把自动发送方启用视为配置漂移。

### 5.7 CI-RESOURCE-REQ-007 子进程回收

长期运行且会启动 Git、SSH、编译器或其他子进程的容器必须具有明确的子进程回收者。业务进程不得在没有 init/reaper 的情况下直接作为容器 PID 1；已经正确等待直接子进程的实现仍须处理其退出后被 PID 1 接管的孤儿后代。init/reaper 必须只负责信号转发和子进程回收，不得成为第二业务生命周期 authority。

运行面诊断必须按父 PID 聚合 zombie，并映射到 namespace、Pod、稳定 workload owner 和修复类别。修复通过正常自动交付滚动 owner；直接终止 zombie、批量杀进程或只重启 Pod 不能作为根因修复。

## 6. 验收

- L0：配置解析、资源 quantity、requests/limits 关系、matrix 收敛、有界 worker 队列和依赖保持通过轻量验证。
- L1：受控 `plan/status/diagnosis` 显示 YAML 来源、并发预算、批次、资源和保护状态，不依赖裸 Kubernetes 命令。
- L1：所有会启动子进程的常驻 Pod 均显示 init/reaper 为 PID 1；在真实 Git/SSH 操作前后，owner 的 zombie 数不增长。
- L2：正常 HWLAB source PR merge 后不产生 PipelineRun。受控 `plan` 显示 env reuse、镜像构建数量、精确构建与 rollout 范围且没有非预期扩大；随后使用手动 `trigger` 向 PaC 发送一次 webhook，并由 PaC 创建一次 PipelineRun。观察到同一时刻构建 TaskRun 不超过 YAML 预算，相同发布意图不产生第二次构建，所有构建 Pod 带资源合同，并且 NC01 Node、CoreDNS、Sub2API、Public Edge 和公开 API 在构建期间保持健康。

验收失败时保留计划、手动触发链和运行证据，并修复 owning YAML、planner 或 renderer。重启 k3s、删除 Pod/PipelineRun、手工 Argo sync、运行时 patch、mirror flush 或裸补跑均不能作为最终通过证据。
