4. 部署目标选型
Review Apps 的核心诉求是“每个合并请求都能拿到一个可访问、可销毁、可复现的独立环境”。但“独立环境”这四个字在不同运行时上的实现代价差异极大。选型不是挑最先进的技术,而是把 MR 的并发量、环境存活时长、启动延迟容忍度、隔离强度、成本模型和团队运维能力放在一起做匹配。本节把 Kubernetes、Docker(单机容器)、虚拟机和 Serverless 四种运行时放在同一组工程约束下对比,并给出可落地的判断依据。
先明确四个选型维度
在比较之前,需要先定义评价坐标,否则很容易陷入“K8s 更强大”这类无信息量的结论。对 Review Apps 而言,真正起决定作用的是以下四项:
- 启动延迟:从 CI 触发到环境可访问的时间。MR 场景下开发者等待超过 2~3 分钟就会频繁切换上下文,因此这是体验的第一道门槛。
- 隔离强度:环境之间是否共享内核、网络、存储。共享越多,越容易出现端口冲突、依赖污染和“别人的 MR 把我的环境搞挂了”。
- 生命周期管理成本:创建、更新、销毁是否可自动化,残留资源是否可回收。Review Apps 是高频短生命周期对象,管理成本往往比运行成本更高。
- 单位成本与闲置成本:环境在无人访问时是否仍在计费。MR 环境大量时间处于闲置状态,闲置成本模型直接决定方案能否长期运行。
下面这张图把四种运行时放在同一坐标系中,按“启动速度—隔离强度”和“运维复杂度—闲置成本”两组关系呈现它们的典型位置,便于快速定位。
Kubernetes:适合规模化、多服务、需要网络编排的场景
Kubernetes 作为 Review Apps 运行时的最大优势是“声明式生命周期 + 命名空间级隔离”。每个 MR 对应一个 Namespace,配合 Helm 或 Kustomize 渲染出整套 Deployment、Service、Ingress,销毁时直接删除 Namespace 即可回收全部资源。这种模型对微服务架构尤其友好:一个 MR 可能同时改动前端、订单服务和数据库迁移,K8s 能把它们作为一个整体环境交付。
典型实现方式是在 CI 中执行类似命令:
# 为 MR 创建独立命名空间并部署
kubectl create namespace review-mr-${CI_MERGE_REQUEST_IID}
helm upgrade --install app-${CI_MERGE_REQUEST_IID} ./chart \
--namespace review-mr-${CI_MERGE_REQUEST_IID} \
--set image.tag=${CI_COMMIT_SHA} \
--set ingress.host=mr-${CI_MERGE_REQUEST_IID}.review.example.com
# MR 关闭时清理
kubectl delete namespace review-mr-${CI_MERGE_REQUEST_IID}
工程约束也很明确:需要一套可用的集群、Ingress Controller、证书管理和镜像拉取凭证;冷启动时间通常在 20~60 秒(取决于镜像大小和调度速度);如果集群资源不足,Pod 会 Pending,环境就绪时间不可控。因此 K8s 更适合已有集群、MR 并发在数十级别、且团队具备基本 K8s 运维能力的场景。若只是为了跑一个单体应用,引入 K8s 的复杂度往往得不偿失。
Docker 单机:轻量、快速,但隔离与编排能力有限
Docker 单机方案通常是在一台常驻的构建/运行节点上,用 docker run 或 docker compose 为每个 MR 启动一组容器,通过端口映射或反向代理暴露访问。它的启动速度明显优于 K8s,因为省去了调度、拉取镜像到多节点、Service 发现等环节,通常 5~20 秒即可就绪。
一个可用的端口分配策略是按 MR IID 计算偏移量,例如基础端口 30000,则 MR 123 使用 30123:
docker run -d --name review-mr-123 \
-p 30123:8080 \
-e DATABASE_URL=postgres://review-mr-123-db:5432/app \
--network review-mr-123-net \
registry.example.com/app:${CI_COMMIT_SHA}
它的主要约束在于隔离强度:所有容器共享宿主机内核和 Docker daemon,一旦某个 MR 的容器占满 CPU 或磁盘,其他环境会受影响;端口数量有限,高并发时容易冲突;没有原生的多节点调度,单机故障会导致所有 Review Apps 同时不可用。因此 Docker 单机适合 MR 并发在 10~20 个以内、应用为单体或少量服务、团队希望快速落地且不想维护集群的场景。
虚拟机:隔离最强,但启动与成本代价最高
虚拟机方案为每个 MR 创建一台独立 VM(或从模板克隆),在 VM 内运行应用。它的隔离强度是四种方案中最高的:独立内核、独立网络栈、独立磁盘,一个环境的问题几乎不会外溢。对于需要验证内核参数、系统依赖、安全策略或数据库主从拓扑的 Review Apps,虚拟机是唯一能真实还原生产形态的选择。
但代价同样突出:从模板克隆并启动一台 VM 通常需要 1~5 分钟,若还要安装依赖、初始化数据库,环境就绪时间可能超过 10 分钟;每台 VM 即使闲置也占用内存和 CPU 配额,成本随 MR 数量线性增长;销毁不及时会留下大量僵尸 VM。因此虚拟机更适合低频、重量级、对隔离和系统级真实性要求极高的 Review Apps,例如基础设施变更、操作系统升级验证,而不适合作为高频 MR 的默认运行时。
Serverless:启动极快、闲置零成本,但形态受限
Serverless(以函数计算、容器化 Serverless 为代表)在 Review Apps 中的价值主要体现在两点:一是按请求计费,环境无人访问时不产生计算费用;二是冷启动通常在百毫秒到数秒级,配合预置并发可以做到近乎即时可用。对于前端静态站点、API 网关 + 函数、轻量后端服务,Serverless 能以极低成本支撑大量并行的 MR 环境。
它的限制在于运行时形态:函数通常有执行时长上限、临时存储限制、不支持常驻进程和自定义内核模块;数据库连接需要借助连接池或外部代理;WebSocket、长连接、后台任务等场景支持有限。因此 Serverless 适合“无状态、请求驱动、短时执行”的 Review Apps,例如前端预览、REST API 验证、文档站点;不适合需要常驻服务、复杂中间件或系统级调试的场景。
选型决策表
把上述分析收敛为一张可直接对照的决策表,按团队与项目特征给出推荐运行时。表中“推荐”指优先考虑,“可用”指在特定条件下可行,“不推荐”指代价明显高于收益。
| 场景特征 | Kubernetes | Docker 单机 | 虚拟机 | Serverless |
|---|---|---|---|---|
| MR 并发 > 50,微服务多组件 | 推荐 | 不推荐 | 不推荐 | 可用(无状态时) |
| MR 并发 < 20,单体应用 | 可用(已有集群) | 推荐 | 可用 | 可用 |
| 需要验证内核/系统级依赖 | 不推荐 | 不推荐 | 推荐 | 不推荐 |
| 前端静态预览、API 验证 | 可用 | 可用 | 不推荐 | 推荐 |
| 团队无集群运维能力 | 不推荐 | 推荐 | 可用 | 推荐 |
| 要求闲置零成本 | 不推荐 | 不推荐 | 不推荐 | 推荐 |
最后需要强调,运行时选型不是一次性决策。随着 MR 并发增长、应用架构演进和团队能力变化,原先合适的方案可能变成瓶颈。建议在 CI 中记录每个 Review App 的“创建到就绪耗时”和“销毁残留率”两个指标,当就绪耗时中位数持续超过团队容忍阈值,或残留环境数量持续上升时,就是重新评估运行时的信号。