Prow Flow 与 test-infra 深度剖析 —— eBay Tess 平台 CI/CD 核心揭秘

Prow 是 Kubernetes 社区开源的 CI/CD 框架,在 eBay Tess 平台中被深度定制化部署,成为整个平台 CI/CD 流水线的神经中枢。本文将从架构设计出发,逐一剖析 Prow 的核心组件、PR 完整生命周期流转,并结合 eBay test-infra 仓库的实际配置,说明各组件在 cluster 908(tessprow 集群)上的部署位置与职责边界。

整体架构:服务集群 vs 构建集群

Prow 采用双集群架构,职责严格分离:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
┌──────────────────────────────────────────────────────────────────┐
│ GitHub Enterprise │
│ (github.corp.ebay.com) │
│ │ │
│ Webhook Events / API calls │
└────────────────────────┬─────────────────────────────────────────┘


┌──────────────────────────────────────────────────────────────────┐
│ Service Cluster (cluster 908 / tessprow) │
│ │
│ Hook → Plank/ProwControllerManager → Tide → Crier │
│ Deck Horologium Sinker GHProxy Manual-Trigger │
│ GIS-Approve (eBay-specific) │
│ │
│ Creates ProwJob CRDs, schedules Pods │
└──────────────────────────────┬───────────────────────────────────┘

┌───────────────┼──────────────────┐
▼ ▼ ▼
Build Cluster 1 Build Cluster 2 ...more
(default/trusted) (rules-k8s) CI clusters
Runs Pods Runs Pods kind/e2e jobs

Service Cluster(服务集群):运行所有 Prow 控制面组件,处理 GitHub 事件,管理 ProwJob CRD,不直接运行测试 Pod。

Build Cluster(构建集群):只运行 Prow 调度过来的 Pod,实际执行 CI 测试、构建任务。

在 eBay 内部,service cluster 对应的是 cluster 908,其中 Prow 组件统一部署在 tessprow 命名空间(eBay 自定义)以及 default 命名空间(上游兼容)。


test-infra 仓库结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
test-infra/                          # eBay fork of kubernetes/test-infra
├── config/
│ ├── prow/
│ │ ├── config.yaml # Prow 主配置(plank/deck/sinker/tide)
│ │ ├── plugins.yaml # 插件配置(approve/trigger/owners等)
│ │ └── cluster/ # K8s 部署清单(56 个 manifest 文件)
│ │ ├── hook_deployment.yaml
│ │ ├── deck_deployment.yaml
│ │ ├── tide_deployment.yaml
│ │ ├── crier_deployment.yaml
│ │ ├── sinker_deployment.yaml
│ │ ├── horologium_deployment.yaml
│ │ ├── ghproxy.yaml
│ │ ├── gis-approve-deploy-svc.yaml # eBay 专属组件
│ │ └── monitoring/ # Prometheus + Grafana + AlertManager
│ └── clusters/
│ └── k8s-prow/
│ └── cluster-configuration.tf # GKE 集群 Terraform
├── prow/ # Prow 核心源码
│ ├── cmd/ # 各组件入口
│ │ ├── hook/
│ │ ├── plank/
│ │ ├── deck/
│ │ ├── tide/
│ │ ├── sinker/
│ │ ├── crier/
│ │ ├── horologium/
│ │ ├── pipeline/ # Tekton 集成(含 TrustFabric 改造)
│ │ └── manual-trigger/ # eBay 自定义:手动触发器
│ └── kube/
│ └── cluster.go # TrustFabric 认证集成
├── images/ # CI 工具镜像
│ ├── git/ # git + github known hosts
│ └── git-custom-k8s-auth/ # git + AWS IAM + GKE auth
└── docs/
└── playbooks/prow.md # 运维手册

核心组件深度剖析

1. Hook —— 事件入口网关

职责:接收 GitHub Webhook 事件,分发给各 Plugin 处理。

部署位置default 命名空间,4 副本(高可用)

1
2
3
4
5
6
7
8
# config/prow/cluster/hook_deployment.yaml
spec:
replicas: 4
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 1

处理流程

1
2
3
4
5
6
7
8
9
10
11
GitHub Webhook


Hook(:8888)
│ 验证 HMAC 签名(hmac-token Secret)
├─► trigger plugin → 创建 ProwJob(PR comment "/test all")
├─► approve plugin → 管理 /approve 标签
├─► lgtm plugin → 管理 /lgtm 标签
├─► label plugin → 管理 PR 标签
├─► size plugin → 计算 PR 大小
└─► ... 40+ plugins

eBay 定制:Hook 连接的是内部 GitHub Enterprise(github.corp.ebay.com/api/v3),并通过 --github-endpoint=http://ghproxy 将请求经由 GHProxy 缓存转发,减少直接 API 调用消耗。

Hook 通过 KUBECONFIG 环境变量同时挂载多个集群的 kubeconfig,实现跨集群 ProwJob 调度:

1
2
3
4
5
6
env:
- name: KUBECONFIG
value: "/etc/kubeconfig/config:\
/etc/kubeconfig-build-test-infra-trusted/kubeconfig:\
/etc/kubeconfig-build-k8s-prow-builds/kubeconfig:\
/etc/kubeconfig-build-rules-k8s/kubeconfig"

2. Plank / ProwControllerManager —— 任务调度器

职责:监听 ProwJob CRD,创建 Pod 到对应 build cluster 并同步状态。

部署位置default 命名空间

核心逻辑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
ProwJob CRD(created by Hook/Horologium/Tide)


ProwControllerManager
│ 读取 .spec.cluster 字段确定目标集群
│ 读取 decoration config 注入 utility containers
├─► 创建 Pod 到 build cluster
│ ├── initupload (init container: 上传起始元数据)
│ ├── clonerefs (init container: clone 代码)
│ ├── place-entrypoint (init container: 安装 entrypoint)
│ ├── test-container (用户定义的测试逻辑)
│ └── sidecar (sidecar: 收集日志 + 上传 GCS)

└─► 更新 ProwJob 状态(Pending → Running → Success/Failure)

Decoration Configconfig.yaml 中配置):

1
2
3
4
5
6
7
8
9
10
11
12
13
plank:
default_decoration_config_entries:
- config:
timeout: 2h
grace_period: 15m
utility_images:
clonerefs: "gcr.io/k8s-prow/clonerefs:v20230125-c8bfad3373"
initupload: "gcr.io/k8s-prow/initupload:v20230125-c8bfad3373"
entrypoint: "gcr.io/k8s-prow/entrypoint:v20230125-c8bfad3373"
sidecar: "gcr.io/k8s-prow/sidecar:v20230125-c8bfad3373"
gcs_configuration:
bucket: "kubernetes-jenkins"
path_strategy: "legacy"

3. Deck —— Web 仪表盘

职责:提供 Prow Web UI,展示 ProwJob 历史、Spyglass 日志查看、PR 状态。

部署位置default 命名空间,3 副本

1
2
3
4
5
6
args:
- --tide-url=http://tide/
- --hook-url=http://hook:8888/plugin-help
- --spyglass=true
- --rerun-creates-job
- --github-endpoint=http://ghproxy

Spyglass 功能:内嵌日志查看器,支持:

  • metadata lens:展示 started.json / finished.json
  • buildlog lens:高亮错误(支持 DATA RACE、Fatal Error 等正则)
  • coverage lens:代码覆盖率可视化

Deck 通过 --rerun-creates-job 允许用户在 UI 中一键重新触发失败的 ProwJob。


4. Tide —— 自动合并控制器

职责:监控所有 PR 的标签状态,自动 retest + merge 满足条件的 PR。

部署位置default 命名空间,单副本(必须,避免并发 merge 冲突)

1
2
3
4
spec:
replicas: 1 # Do not scale up.
strategy:
type: Recreate # 更新时先删后建,不允许并行运行

合并决策流程

1
2
3
4
5
6
7
8
9
10
11
12
13
每隔 ~1min 同步一次:

Tide 轮询所有配置的 GitHub 仓库


对每个 PR 检查:
✓ 是否满足 merge 条件(labels: lgtm + approved + !do-not-merge)
✓ 是否通过所有 required status checks
✓ base branch 是否是最新

├─► 条件满足 → 触发 merge(single or batch)
├─► 缺少测试结果 → 创建 batch ProwJob 重跑
└─► 落后 base → 需要 rebase

Tide 的状态持久化到 GCS:

1
2
--history-uri=gs://k8s-prow/tide-history.json
--status-path=gs://k8s-prow/tide-status-checkpoint.yaml

5. Horologium —— 定时任务调度器

职责:触发 periodic 类型的 ProwJob(cron 触发,不依赖 GitHub 事件)。

部署位置default 命名空间

periodic job 示例:

1
2
3
4
5
6
7
8
9
periodics:
- name: ci-kubernetes-e2e-gce
interval: 2h # 每 2 小时触发一次
spec:
containers:
- image: gcr.io/k8s-prow/kubekins-e2e:latest
args:
- --repo=k8s.io/kubernetes
- --test=true

Horologium 读取 config.yaml 中所有 periodics 配置,按 intervalcron 表达式精确触发。


6. Sinker —— 垃圾清理器

职责:定期清理过期的 ProwJob 和 Pod,防止集群资源堆积。

部署位置default 命名空间

1
2
3
4
5
6
# config.yaml
sinker:
resync_period: 1m # 每分钟扫描一次
max_prowjob_age: 48h # ProwJob 超过 48h 删除
max_pod_age: 48h # Pod 超过 48h 删除
terminated_pod_ttl: 30m # 已终止 Pod 保留 30min(用于日志查看)

7. Crier —— 状态报告器

职责:将 ProwJob 状态回报到 GitHub(commit status / check run)、Slack 等渠道。

部署位置default 命名空间

1
2
3
4
5
6
7
ProwJob 状态变更(Pending/Running/Success/Failure)


Crier
├─► GitHub Status API(绿色 ✓ / 红色 ✗)
├─► GitHub Check Runs(更详细的 UI)
└─► Slack 通知(失败时)

Crier 从 ProwJob 的 .spec.reporter_config 中读取各渠道配置,实现多目标状态同步。


8. GHProxy —— GitHub API 缓存代理

职责:缓存对 GitHub API 的请求,防止触发 Rate Limit,并提升响应速度。

部署位置default 命名空间(build cluster 也有部署)

1
2
3
4
Prow 组件 → http://ghproxy → github.corp.ebay.com/api/v3

HTTP Cache(304 Not Modified 优化)
Rate Limit 统计与监控

所有组件(Hook、Tide、Deck、Crier)都通过 --github-endpoint=http://ghproxy 路由 GitHub API 请求。


9. GIS-Approve —— eBay 专属审批组件

职责:eBay 内部自动化审批机器人,满足条件时自动 /approve PR。

部署位置tessprow 命名空间(eBay 专属)

1
2
3
4
5
6
7
8
9
10
# config/prow/cluster/gis-approve-deploy-svc.yaml
metadata:
namespace: tessprow # eBay 自定义命名空间
spec:
containers:
- args:
- --github-host=github.corp.ebay.com
- --login-user=tess-bot
- --login-mail=DL-eBay-tess-ci@ebay.com
image: hub.tess.io/k8s-prow/gis-approve:v2

Bot 账号:tess-bottess-ci@ebay.com


10. Manual-Trigger —— 手动触发器(eBay 扩展)

职责:提供 HTTP API 接口,允许用户绕过 GitHub 事件直接触发 ProwJob。

部署位置tessprow 命名空间,通过 http://manual-trigger.tessprow 访问

1
2
3
4
5
6
7
8
9
10
# 触发一个 postsubmit job
curl -X POST \
"http://manual-trigger.tessprow/manual-trigger?\
org=tess&repo=tessops&base_ref=master\
&prowtype=postsubmit&prowjob=sddz-e2e-k8s-1.32"

# 触发 presubmit,带 PR 号
curl -X POST "http://manual-trigger.tessprow/manual-trigger" \
-H "Content-Type: application/json" \
-d '{"org":"tess","repo":"tessops","pull_number":123,"prowjob":"unit-test"}'

典型使用场景

  • CI 集群故障后手动补跑失败 job
  • 针对特定 commit 触发部署验证
  • 运维操作中的一次性任务触发

PR 完整生命周期:从提交到合并

以一个典型的 presubmit 流程为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
┌─────────────────────────────────────────────────────────────────┐
│ 1. Developer opens PR on github.corp.ebay.com │
│ → GitHub sends webhook to Hook │
└────────────────────────┬────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────┐
│ 2. Hook receives pull_request event │
│ trigger plugin: checks plugins.yaml for presubmit jobs │
│ → creates ProwJob CRD (type: presubmit) │
└────────────────────────┬────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────┐
│ 3. ProwControllerManager picks up ProwJob │
│ → determines target build cluster (.spec.cluster) │
│ → injects utility init containers (clonerefs, initupload) │
│ → creates Pod in build cluster │
└────────────────────────┬────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────┐
│ 4. Pod runs in build cluster │
│ init: clonerefs → clone PR branch + base branch │
│ init: place-entrypoint → install wrapper │
│ main: test container runs actual CI script │
│ sidecar: stream logs to GCS, await exit │
└────────────────────────┬────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────┐
│ 5. Crier reports status back to GitHub │
│ → GitHub PR shows ✓ or ✗ on commit status │
│ → Deck Spyglass shows full log via GCS link │
└────────────────────────┬────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────┐
│ 6. Reviewer adds /lgtm and /approve │
│ Hook approve plugin: sets "approved" label │
└────────────────────────┬────────────────────────────────────────┘


┌─────────────────────────────────────────────────────────────────┐
│ 7. Tide detects PR: lgtm ✓ + approved ✓ + CI ✓ │
│ → optionally retests to ensure base is latest │
│ → merges PR (or batch-merges with other ready PRs) │
└─────────────────────────────────────────────────────────────────┘

eBay Tess 平台特殊定制

TrustFabric 认证

标准 Prow 使用 kubeconfig 文件进行 K8s API 认证。eBay 在 prow/kube/cluster.goprow/cmd/pipeline/main.go 中集成了 TrustFabric(eBay 内部 SPIFFE/mTLS 认证框架),允许在 Go >= 1.18 的运行时中通过工作负载身份(Workload Identity)直接认证 K8s API Server,无需静态 kubeconfig。

GitHub Enterprise 适配

所有组件连接的是内部 GitHub Enterprise,通过统一配置:

1
2
3
4
# 配置文件中
github_host: github.corp.ebay.com
github_endpoint: https://github.corp.ebay.com/api/v3
github_graphql_endpoint: https://github.corp.ebay.com/api/graphql

内部镜像 Registry

所有基础镜像从 hub.tess.io 拉取,避免依赖外部 registry:

1
2
3
4
# .ko.yaml
baseImageOverrides:
gcr.io/distroless/static: hub.tess.io/tess/alpine:hardened
# ...

组件在 Cluster 908 上的部署位置总览

Cluster 908 是 eBay Tess 平台的 Prow 服务集群,对应上文中的 service cluster。以下是各组件的完整部署位置:

组件 命名空间 Manifest 文件 副本数 说明
Hook default hook_deployment.yaml 4 高可用 Webhook 接收
Deck default deck_deployment.yaml 3 Web UI,滚动升级
Tide default tide_deployment.yaml 1 严禁扩容,Recreate 策略
Horologium default horologium_deployment.yaml 1 Periodic job 调度
Sinker default sinker_deployment.yaml 1 垃圾回收
Crier default crier_deployment.yaml 1 状态报告
ProwControllerManager default prow_controller_manager_deployment.yaml 1 取代 Plank 的新控制器
Pipeline default pipeline_deployment.yaml 1 Tekton 集成
CherryPicker default cherrypicker_deployment.yaml 1 自动 cherry-pick
GHProxy default ghproxy.yaml 1 GitHub API 缓存
Halogen default halogen.yaml 1 日志高亮服务
GIS-Approve tessprow gis-approve-deploy-svc.yaml 1 eBay 专属审批 Bot
Manual-Trigger tessprow (独立部署) 1 eBay 专属手动触发
Kube-State-Metrics default kube-state-metrics_deployment.yaml 1 监控指标采集
Prometheus monitoring monitoring/ 1 指标存储
Grafana monitoring monitoring/ 1 可视化仪表盘
AlertManager monitoring monitoring/ 1 告警路由

关键区别

  • 上游标准组件部署在 default 命名空间
  • eBay 自研/定制组件部署在 tessprow 命名空间(隔离管理,便于权限控制)

Monitoring 体系

config/prow/cluster/monitoring/ 目录下有 28 个监控配置文件,构建完整的可观测性栈:

1
2
3
4
5
6
7
monitoring/
├── prometheus_deployment.yaml # Prometheus 主实例
├── prometheus_rbac.yaml
├── grafana_deployment.yaml # Grafana 可视化
├── alertmanager_*.yaml # AlertManager 告警
├── *_servicemonitor.yaml # 各组件的 ServiceMonitor
└── *_podmonitor.yaml # Pod 级别监控

关键监控指标:

  • ProwJob CRD 数量(按 type/state/cluster 分组)→ 检测积压
  • GitHub API Rate Limit(via GHProxy)→ 防止限流
  • Tide merge 速率→ 检测 merge 停滞
  • Build cluster Pod 资源使用→ 容量规划

小结

Prow + test-infra 构成了 eBay Tess CI/CD 的完整基础设施:

  1. Hook 是事件驱动的入口,通过 Plugin 体系扩展性强
  2. ProwControllerManager 实现跨集群的 Pod 调度,是核心任务引擎
  3. Tide 的批量合并能力显著提升大规模代码库的吞吐
  4. eBay 定制层(TrustFabric + GIS-Approve + Manual-Trigger)在不破坏上游兼容性的前提下满足了企业级需求
  5. Cluster 908(tessprow)上,标准组件跑在 default 命名空间,eBay 扩展组件隔离在 tessprow 命名空间,职责清晰

理解这套架构后,无论是排查 CI 失败、新增 Plugin 功能、还是扩展构建集群,都能找到明确的代码路径和配置入口。