蓝盾流水线「容器构建机」全链路源码解析

蓝鲸持续集成平台(蓝盾)是一个免费并开源的 CI 服务。日常 CI 场景中,开发者点击 「执行流水线」 后,如果 Job 的构建类型选择 「容器构建机」,最终会在 Kubernetes 中启动一个 Deployment 作为构建 Pod。

1. 背景

本文以蓝盾社区版7.1为例,结合实际源码和配置,详细梳理从前端点击「执行」到最终在 Kubernetes 集群拉起 Deployment 的全链路调用过程,

2. 工程鸟瞰

bk-ci/src
├── gateway # OpenResty 网关(Lua)
├── frontend # Vue 前端,模块级微前端
├── backend # Kotlin + SpringCloud 微服务
│ ├── process # 流水线引擎
│ ├── dispatch # 构建调度(Docker & K8s)
│ └── ...
├── agent # Go 语言 Agent
└── pipeline-plugin # Java 插件 SDK

3. 源码拆解

3.1 前端触发

页面地址:https://devops.bk.tencent.com/console/pipeline/{projectId}/{pipelineId}/preview

事件/方法对应后端接口作用
requestStartupInfoGET /ms/process/api/user/builds/{p}/{pl}/manualStartupInfo获取流水线启动所需参数
executePipeline()POST /ms/process/api/user/builds/{p}/{pl}真正触发流水线执行

相关文件路径:

  • src/frontend/devops-pipeline/src/views/subpages/preview.vue: 获取流水线启动所需参数
  • src/frontend/devops-pipeline/src/components/PipelineHeader/PreviewHeader.vue: 点击执行按钮,触发流水线执行

通过全局事件总线 bus 通信,以及具名视图(named views)机制实现页面拆分和组合。在 preview.vue 页面监听 executePipeline 事件,然后在 PreviewHeader.vue 中通过事件总线触发执行。

3.2 网关转发

/ms/process/api/user/builds/… 统一转发到 process 微服务。

location /ms/process/ {
proxy_pass http://process/;
}

3.3 Process 服务:流水线启动主链路

主要方法调用链如下:

UserBuildResource.manualStartup()                // 接收启动流水线的请求

ServiceBuildResourceImpl.manualStartup() // 具体实现,做参数校验、权限校验等

PipelineBuildFacadeService.buildManualStartup() // 负责组装启动参数、调用核心服务

PipelineBuildService.startPipeline() // 启动流水线主流程,负责流水线状态流转、记录等

PipelineRuntimeService.startBuild() // 流水线引擎,解析模型,调度 Stage/Job/Container,准备构建任务

这一阶段主要负责接收前端的启动请求,经过参数校验、权限校验后,组装启动参数,最终进入流水线引擎。流水线引擎会解析流水线的模型(YAML/DSL),为后续的调度和任务准备做铺垫。

3.4. 生成并下发构建任务

主要方法调用链如下:

PipelineContainerService.prepareBuildContainerTasks()      // 遍历流水线模型,为每个 Job/Container 生成任务,判断分发类型

VmOperateTaskGenerator.makeStartVMContainerTask() // 针对容器构建机,生成 VM 启动任务(taskAtom = "dispatchVMStartupTaskAtom")

pipelineEventDispatcher.dispatch(PipelineBuildStartEvent()) // 下发流水线启动事件

此阶段会遍历流水线模型中的每个 Job/Container,根据其类型(如容器构建机)生成对应的任务。对于容器构建机,会生成 VM 启动任务,并通过事件分发器下发流水线启动事件,为后续的事件驱动调度做准备。

3.5. 事件驱动:Stage/Container/Task 调度

主要方法调用链如下:

PipelineBuildStartListener.run(event)                      // 消费 PipelineBuildStartEvent,驱动流水线调度

BuildStartControl.handle(event)

PipelineBuildStartEvent.execute(watcher)

buildModel()

pipelineEventDispatcher.dispatch(PipelineBuildStageEvent()) // 下发 Stage 事件

PipelineStageBuildListener.run(event) // 消费 PipelineBuildStageEvent

StageControl.handle(event)

PipelineBuildStageEvent.execute(watcher)

pipelineContainerService.listContainers(...) // 遍历当前 Stage 下所有 Container(Job)

pipelineEventDispatcher.dispatch(PipelineBuildContainerEvent()) // 为每个 Job 下发事件

PipelineContainerBuildListener.run(event) // 消费 PipelineBuildContainerEvent

ContainerControl.handle(event)

ContainerCmdChain.doCommand(context) // 命令链执行,关键命令 StartActionTaskContainerCmd

pipelineEventDispatcher.dispatch(PipelineBuildAtomTaskEvent()) // 下发插件任务事件

蓝盾采用事件驱动架构,每个阶段(Stage)、每个 Job(Container)、每个插件(Task)都通过事件进行调度。每个事件都有对应的 Listener 消费,逐步推进流水线的执行流程,保证了系统的高解耦和可扩展性。

3.6. 插件任务调度与 VM 启动

主要方法调用链如下:

PipelineAtomTaskBuildListener.run(event)                   // 消费 PipelineBuildAtomTaskEvent

TaskControl.handle(event)

taskAtomService.start(buildTask)

SpringContextUtil.getBean(IAtomTask::class.java, task.taskAtom).execute(task, runVariables)

DispatchVMStartupTaskAtom.execute() // 对于 VM 启动任务,加载并执行

dispatch()

getDispatchType() // 返回 DockerDispatchType(社区版容器构建机默认)

pipelineEventDispatcher.dispatch(PipelineAgentStartupEvent()) // 下发分发事件

每个插件任务(Atom)都会被动态加载并执行。对于 VM 启动任务,会加载 DispatchVMStartupTaskAtom 插件,判断分发类型(如 Docker),并下发 PipelineAgentStartupEvent,为后续的构建机分发做准备。

3.7. dispatch-docker 服务:分发到 k8s

主要方法调用链如下:

DockerVMListener.onStartup(dispatchMessage)                // 消费 PipelineAgentStartupEvent

getDockerRoutingType(projectId) // 判断路由类型(如 configmap 配置为 "KUBERNETES")

startKubernetesDocker(...) // 路由类型为 KUBERNETES 时,走 k8s 资源池

DispatchBuildService.startUp()

createAndStartNewBuilder()

containerServiceFactory.load(projectId).createAndStartBuilder()

KubernetesContainerService.createAndStartBuilder()

kubernetesBuilderClient.createBuilder() // HTTP POST /api/builders 调用 dispatch-k8s-manager

dispatch-docker 服务会根据项目的路由配置,决定是走本地 Docker 还是 k8s 资源池。若配置为 KUBERNETES,则会通过 HTTP 请求调用 dispatch-k8s-manager 服务,准备在 k8s 集群中拉起构建容器。

查看 bk-ci-bk-ci-dispatch-docker 这个 configmap,可以发现配置文件里的 defaultDockerRoutingType 是 “KUBERNETES”。

3.8. dispatch-k8s-manager 服务:拉起 k8s Deployment

主要方法调用链如下:

POST /api/builders                                         // 路由

createBuilder handler

service.CreateBuilder

task.DoCreateBuilder

kubeclient.CreateDeployment(dep) // 通过 k8s API 创建 Deployment,拉起实际的构建容器

dispatch-k8s-manager 服务负责与 Kubernetes API 交互,接收来自 dispatch-docker 的 HTTP 请求后,组装 Deployment 对象并调用 k8s API,最终在集群中拉起实际的构建容器,完成流水线的环境准备。

3.9 总结时序图

alt text

4. 总结

阶段关键技术点一句话描述
前端Vue + Event Bus点击按钮 → 事件总线 → 请求发出
网关OpenResty 前缀转发统一入口,/ms/process/** 直接透传至 process 服务。
流程引擎自研事件-命令链框架PipelineBuildStart → Stage → Container → Task → AgentStartup,层层事件推进,高内聚低耦合。
插件IAtomTask SPI 机制运行时动态加载 DispatchVMStartupTaskAtom,扩展即插即用。
调度dispatch-docker → dispatch-kubernetes根据 defaultDockerRoutingType=KUBERNETES 路由到对应资源池。
K8s 交付dispatch-k8s-manager 与 kube-apiserver 交互一条 HTTP 请求即可在集群内拉起 Deployment,数秒完成环境就绪。

5. 参考