目录

  1. 持续集成

  2. 简介

  3. 架构

    1. 系统架构
    2. Worker 连接
    3. BuildMaster 架构
  4. 第一次运行

    1. 创建 Master
    2. 创建 Worker
    3. 总结
  5. 源标识

  6. Change source

    1. 存储库和项目
  7. Change

  8. Scheduler

    1. ForceScheduler 调度器
  9. BuildRequest

  10. Builder 和 Build Factory

  11. Build

  12. BuildSet

  13. Worker

  14. 用户

  15. Build Properties

    1. 在步骤中使用属性

      1. Property
      2. Interpolate
  16. Reporter

  17. Web Server 的认证和授权

    1. 限制读访问
    2. 端点匹配器
    3. 角色匹配器
    4. 示例配置
  18. master.cfg 中的其它配置项


1. 持续集成

Martin Fowler 对持续集成(Continuous Integration)的定义是:持续集成是一种软件开发实践,团队开发成员经常集成他们的工作,通常每个成员每天至少集成一次,这意味着每天可能发生多次集成。每次集成都通过自动化构建(包括编译、发布、自动化测试)进行验证,从而尽快发现集成错误。许多团队发现该过程可以减少集成问题,让团队能够更快地开发内聚的软件。

ci.jpg

图片来源:百度百科


2. 简介

在 BuildBot 中,可以通过三种方式触发构建:

BuildBot 有如下优点:


3. 架构

3.1. 系统架构

buildbot.png

BuildBot 由单个 BuildMaster 和一或多个连接到 Master 的 Worker 组成。BuildMaster 决定构建什么,何时构建,以及如何构建。Worker 连接到 Master,执行交给它们执行的命令。

流程如下所示:

3.2. Worker 连接

buildbot-workers.svg

Worker 通常运行在各种不同的平台上,它们通过 TCP 协议连接到 BuildMaster,TCP 连接由 Worker 发起。BuildMaster 下发的 Command 以及 Worker 返回的 Result 通过 TCP 连接进行传输。BuildMaster 是集群的管理者,所有 Command 都由 BuildMaster 下发给 Worker。

BuildMaster 只提供用于执行构建的指令,不提供源代码,因此 Worker 需要去代码仓库获取源代码。

3.3. BuildMaster 架构

buildmaster.svg

Buildbot Master 的核心组件如下:


4. 第一次运行

4.1. 创建 Master

第一步是为 Master 创建 virtualenv。我们创建单独的目录,展示 Master 和 Worker 之间的区别:

然后创建虚拟环境。在 Python 3 中:

接下来,需要安装若干构建依赖,以确保可以安装 Buildbot 及其支持包。这些构建依赖项是:

安装这些构建依赖:

或者参考你的发行版的关于如何安装这些包的文档。

现在已经准备就绪,开始安装 Buildbot:

现在已成功安装 Buildbot,开始创建 Master。my_master 代表指向目录的路径,我们将在该目录中创建 Master:

Buildbot 的活动由配置文件控制。Buildbot 默认使用 master.cfg 文件中的配置,但其安装附带一个名为 master.cfg.sample 的示例配置文件。我们将使用示例配置文件,但必须将其重命名为 master.cfg

最后,启动 Master:

现在在该终端中将看到来自 Master 的日志信息。它应该以类似下面的行结束:

从现在开始,可以访问运行在端口 8010 上的 Web 状态页面:http://localhost:8010/

Master 至少需要一个 Worker,执行它的命令。为此,请转到下一节。

4.2. 创建 Worker

Worker 将执行 Master 发送的命令。在本教程中,我们使用 buildbot/hello-world 项目作为示例。因此,Worker 需要访问 git 命令,以便检出代码。确保已安装 git,否则构建将失败。

就像我们为 Master 所做的一样,我们将在 Master 的 virtualenv 旁边为 Worker 创建 virtualenv。当然,完全可以在另一台计算机上做这件事 — 只要 Worker 计算机能够连接到 Master 计算机。我们首先为 Worker 创建新目录:

再次创建虚拟环境。在 Python 3 中:

安装 buildbot-worker 命令:

现在,创建 Worker:

注意

如果你决定在另一台计算机上创建 Worker,那么应该将 localhost 替换为运行 Master 的计算机的名称。

用户名(example-worker)和密码(pass)应该与 my_master/master.cfg 中的相同;通过查看 c['workers'] 部分,验证情况是否如此:

最后,启动 Worker:

检查 Worker 的输出。它应该以类似下面的行结束:

同时,在另一个终端,在 Master 的日志(在 Master 目录中的 twisted.log)中,应该看到类似这样的行:

4.3. 总结

目录树现在应该类似这样:

现在,应该可以访问 http://localhost:8010,在这里将看到类似下面的 Web 页面:

buildbot-index.png

点击左侧的“Builds”,打开子菜单,然后点击 Builders,可以看到刚刚启动的 Worker(由绿色气泡标识)已经连接到 Master:

buildbot-builders.png

现在,Master 正在安静地等待新提交。

接下来讲述 Buildbot 中的基础概念。


5. 源标识

在 Buildbot 中,以下概念用于描述正在构建的源代码:

为构建项目,Buildbot 只需知道与该项目对应的 Source stamp set。Source stamp set 对组成项目的每个代码库都有一个 Source stamp。反过来,每个 Source stamp 都有足够的信息,标识代码库中代码的特定版本。

changes.svg


6. Change source

ChangeSource 是用户可配置的组件,它与外部的版本控制系统进行交互,检索新代码。在内部,新代码表示为变更(Change),大致相当于单个提交或变更集。变更被发送到调度器,然后调度器决定是否为这些新代码变更创建新构建。

Buildbot 的设计要求 Worker 拥有自己的源代码副本。因此,如果没有基于新代码提交事件创建新构建的调度器,那么 ChangeSource 是可选组件。

ChangeSource 可以通过多种方式,监视存储库的变更。比如周期性地轮询存储库;或者通过配置(比如通过被 commit 触发的钩子脚本),使存储库将变更推送给 ChangeSource。

下面是一个示例:

该示例创建一个 GitPoller 类型的 ChangeSource,它每隔 120 秒轮询一次存储库,获取 master 分支上的变化。

ChangeSource 必须被添加到 c["change_source"] 列表中。

6.1. 存储库和项目

通常情况下,ChangeSource 自动为其产生的任何 Change 提供正确的 repository 属性。对于操作类 URL 说明符的系统,是存储库 URL。其它 ChangeSource 按需适配该概念。

许多 ChangeSource 也允许指定项目。该属性在在同一个 BuildMaster 上从多个不同的代码库构建时非常有用:项目字符串可以用于区分不同的代码库。调度器可以依据项目进行过滤,因此可以为每个项目配置不同的构建器。


7. Change

Change 是一种抽象方式,Buildbot 用其表示开发人员对源文件执行的单个变更。在支持原子检入概念的版本控制系统中,Change 表示变更集或提交。

Change 用于 ChangeSource 与 Scheduler 进行通信。

Change 包含如下信息:


8. Scheduler

调度器是决定何时开始构建的组件。该决定可以基于时间、正在被提交的新代码,或类似的事件。

调度器负责创建构建请求(Build Request),它标识在源代码的特定版本上启动构建的请求。

每个 BuildMaster 拥有一组调度器对象,每个调度器对象获得每个传入的 Change 的副本。调度器负责决定何时运行构建。一些 Buildbot 安装可能只有一个调度器,而另一些可能有多个,每个调度器用于不同的目的。

调度器主要包含两部分信息:

下面是一个示例:

在上面的例子中,第一个调度器接收所有存储库上的变更,但只关注 master 分支上的变更。换言之,它只对它感兴趣的变更作出反应。当检测到这样的变更时,它运行 runteststreeStableTimer 用于避免在频繁提交时,产生大量的构建请求。

使用第二个调度器时,可以通过 Web UI 强制构建 runtests

Scheduler 必须被添加到 c["schedulers"] 列表中。

8.1. ForceScheduler 调度器

ForceScheduler 调度器是可以在 Web UI 中配置强制构建表单的方式。

/#/builders/:builderid Web 页面,可以在页面的右上角看到,为该 Builder 配置的每个 ForceScheduler 调度器都有一个按钮。如果点击该按钮,那么将弹出一个对话框,让你选择请求新构建的各种参数。

Buildbot 框架允许精确地定制构建表单的外观,哪些构建器有强制构建表单(并非所有构建器都需要强制构建),以及允许哪些用户在哪些构建器上进行强制构建。

可以通过配置 ForceScheduler,并且将其添加到 schedulers 列表的方式,实现这一点。

该调度器接受如下参数:

name

每个调度器必须有唯一的名称。该名称被用在状态展示中,在构建属性 scheduler 中也是可用的。在 UI 中,按照该属性排序强制构建按钮(因此可以通过添加前缀 01、02 等,精确控制其顺序)。

builderNames

强制构建按钮应出现的构建器列表。

reason

该参数允许用户指定构建原因。默认值是 “force build”。

reasonString

用于创建强制构建构建原因的字符串。该字符串可以包含 %(owner)s%(reason)s(代表输入到 reason 字段的值)。

username

该参数用于指定与构建关联的用户名(即 owner)。默认值是 username 参数。

codebases

用于指定应该呈现的代码库的字符串或 CodebaseParameter 列表。默认是没有名称的单个代码库(即 codebases=[‘’])。

properties

参数列表,每个属性一个参数。这些可以是任意参数,其中参数名称被视为属性名,或 AnyPropertyParameter,它允许 Web 用户指定属性名。默认值是空列表。

buttonName

在生成的强制构建表单中,“提交”按钮的名称。默认为调度器名称。

例子可能比长篇解释更好。

这将生成如下 UI:

forcedialog1.png


9. BuildRequest

BuildRequest 是用于开始特定构建的请求。BuildRequest 包含如下信息:

表示相同版本源代码和相同构建器的两个构建请求可能被合并。用户可以配置额外的限制,以决定构建请求的可合并性。


10. Builder 和 Build Factory

Builder 负责从 BuildRequest 创建新构建。创建新构建实质上是确定后续构建的以下属性:

将要运行的步骤序列由用户可配置的 BuildFactory 定义,BuildFactory 由用户附加到每个 Builder

只要有可能,即只要相关联的 Worker 空闲,Builder 将尝试从构建请求创建构建。当 Worker 空闲时,BuildMaster 将选择可以在该 Worker 上运行的最老的 BuildRequest,并且通知相应的 Builder 可能开始构建它。

默认情况下,每个 Builder 完全独立地运行。这意味着,如果 N 个 Builder 附加到一个 Worker,那么 Worker 可能尝试并发地运行这 N 个构建。该级别的并发可被各种各样的联锁(Interlocks)控制。

在底层,每个 BuilderBuildMaster 上有自己的独占目录,并且在它附加到的每个 Worker 上拥有一个独占目录。Master 上的目录用于保存状态信息。Worker 上的目录是实际的检出、编译和测试步骤所发生的位置。

下面是一个示例:

该示例创建一个名为 runtestsBuilder,它能够在 example-worker 上运行。

Builder 的名称不能相同,并且必须被添加到 c['builders'](其值是 BuilderConfig 对象的列表)。


11. Build

Build 表示源代码特定版本的单次编译或测试运行。构建由一系列步骤组成。这些步骤可能是任意的。比如,对于编译型软件,构建通常由 checkout、configure、make 和 make check 序列组成。对于像 Python 模块之类的解释型项目,构建通常是 checkout,随后调用捆绑的测试套件。

构建是由 Builder 实例创建。


12. BuildSet

BuildSet 代表一组 Build,它们编译和/或测试源树的相同版本。通常,这些构建由多个 Builder 创建,并且将执行不同的步骤。

BuildSet 被作为单个单元追踪,如果任意 Build 组件失败,那么 BuildSet 失败,因此仅当所有 Build 组件都成功时,BuildSet 才成功。BuildSet 可以发出两种状态通知消息:firstFailure 类型和 Finished 类型。

创建 BuildSet 时,需要提供包含一或多个 Source stamp 元组((branch, revision, changes, patch))的集合,其中某些元组可能为 None,以及将被运行的 Builder 列表。然后将它们交给 BuildMasterBuildMaster 负责为每个 Builder 创建单独的 BuildRequest


13. Worker

Worker 对应于执行构建的环境。单台物理机必须至少运行一个 Worker,以便 Buildbot 能够利用它运行构建。多个 Worker 可以在一台机器上运行,以提供不同的环境,这些环境可以通过容器或虚拟机的方式,重用相同的硬件。

每个 Builder 与一或多个 Worker 相关联。比如,用于执行 macOS 构建的 Builder 天然与 Mac Worker 相关联。

对于任意给定的 Builder,如果有多个 Worker 可用,那么将拥有一定程度的冗余:如果一个 Worker 脱机,那么其它 Worker 仍然可以保持 Builder 工作。此外,对于同一个 Builder,多个 Worker 将允许同时执行多个构建,如果发生很多强制构建或 try 构建,这可能很有用。

在理想情况下,为一个 Builder 配置的每个 Worker 应该相同。否则,构建或测试失败将依赖构建被运行在哪个 Worker 上,这将使失败的调查复杂化。

下面是一个示例:


14. 用户

Buildbot 对用户的认知有限。它假设世界由一组开发人员组成,可以用若干个简单属性描述开发人员。这些开发人员对源代码进行更改,引发可能成功或失败的构建。

在发出 Buildbot 命令时,用户也可能具有不同级别的授权,例如从 Web 接口或 IRC 通道强制执行构建。

主要通过源码控制系统了解开发者。到达的每个 Change 对象都带有 who 字段,该字段通常给出负责该变更的用户的帐户名称(在存储库机器上)。该字符串被展示在 HTML 状态页面和每个 Build 的责任列表中。

如果想使用 User 做更多事情,而不仅仅只引用它们,那么用户名需要映射到某种类型的地址。该映射的职责被留给需要地址的状态模块。将来,管理用户的职责将转移到用户对象。

git 变更中的 who 字段用于创建用户对象,它在 Buildbot 管理用户的方式上,允许更多的控制和灵活性。


15. Build Properties

每个构建拥有一组构建属性(Build Properties),其构建步骤可以使用这些构建属性,修改它们的行为。

属性被表示为一组键-值对。实际上,每个属性是一个变量,一旦设置,构建中的后续步骤可以使用该属性,修改它们的行为。属性值可以是数字、字符串、列表或字典。列表和字典可以包含其它列表或字典。因此,属性值可以是任意复杂的结构。

属性的工作原理与变量非常相似,因此它们可被用于实现各种功能。

下面是一些示例:

构建属性是为构建步骤提供配置信息的常用方法。

一些构建属性来自于外部源,在构建开始前被设置;其它的则在构建过程中被设置,可用于后面的步骤。属性的来源如下:

在内部,以 JSON 格式存储属性,因此属性被限制为基本类型的数据:数值、字符串、列表和字典。

15.1. 在步骤中使用属性

在大多数情况下,属性用于在构建期间更改构建步骤的行为。这可以通过使用可渲染对象(实现 IRenderable 接口的对象)作为步骤参数的方式实现。当启动步骤时,将使用构建属性的当前值渲染这些对象,并且使用渲染结果替换步骤参数的实际值。

Buildbot 提供覆盖常见情况的可渲染对象类型。也可以创建自定义的可渲染对象。

15.1.1. Property

最简单的可渲染对象是 Property,它渲染被其参数命名的属性的值:

可以通过传递 default 关键字参数的方式,指定默认值:

当属性不存在或其值被 Python 视为 False 时,将使用默认值。defaultWhenFalse 参数可以设置为 False,以便强制 Buildbot 仅在未设置参数时才使用默认参数:

默认值自身也可以是可渲染对象,比如:

15.1.2. Interpolate

Property 仅用于替换整个参数:在上面的例子中,它替换 echo 的参数。通常,需要将属性插入到字符串中。完成该任务的工具是 Interpolate

更常见的模式是通过使用 %(prop:<propname>)s 语法的方式,使用 Python 字典风格的字符串插值。在这种形式中,属性名出现在括号中。一个常见的错误是忽略尾部的 “s”,导致 Python 出现相当晦涩的错误(“ValueError: unsupported format character”)。

该示例将生成带有类似于 REVISION=12098 的参数的 make 命令。

字典风格插值的语法是 “选择器:选择器特定的键[:表示如何解释键生成的值的字符串]”。

支持的选择器如下。

prop

键是属性的名称。

src

键是代码库和 Source stamp 属性,用冒号分隔。注意,语法是 %(src:<codebase>:<ssattr>)s,与其它选择器不同。

kw

键引用传递给 Interpolate 的关键字参数。这些关键字参数可能是普通的值或可渲染对象。

secret

键引用 secretsProviders 中声明的提供者提供的密钥。

workers

键引用 workers 提供的信息条目。

解释值的方式如下。

-replacement

如果键存在,那么替换其值;否则,替换 replacementreplacement 可能为空(默认),%(prop:propname:-)s

~replacement

-replacement 类似,但仅当键的值被 Python 视为 True 时,才替换。Python 认为 None、0、空列表和空字符串是 False,因此这些值被 replacement 替换。

+replacement

如果键存在,那么替换 replacement;否则,替换空字符串。

?|sub_if_exists|sub_if_missing

?#|sub_if_true|sub_if_false

三元替换,取决于键出现(带 ?,与 + 类似)或为 True(带 ?#,与 ~ 类似)。其中管道符号(|)可以是除 ( 外的任意字符。

示例:

示例:

另外,Interpolate 支持使用位置字符串插值。这里,%s 是占位符,后续参数是替换(可以是可渲染对象):


16. Reporter

BuildMaster 可以通过多种方式,将构建状态呈现给不同用户。每种传递方式是配置文件的 services 列表中的一个 Reporter Target 对象。只需向该列表追加更多对象,即可添加 Reporter Target:


17. Web Server 的认证和授权

默认情况下,用户无需认证,即可在 Web UI 中访问控制功能。为安全起见,应该配置认证插件。

认证插件被实现为类,并且作为 auth 参数传递给 www

比如:

下面讲述授权框架

在 Buildbot 中,授权框架非常通用和灵活。缺点是对新手而言,不易理解。该“简单”示例通过实现管理员拥有全部权限的方式,使你可以轻松开始。

请认真阅读以下文档,以理解如何在 Buildbot 中设置授权。

授权框架与 REST API 紧密耦合。授权框架仅适用于 HTTP,不适用于其它交互方式,如 IRC 或 try 调度器。它根据规则允许或拒绝对 REST API 的访问。

authorization-rules.png

17.1. 限制读访问

请注意,可以使用该框架拒绝对 REST API 的读访问,但在 Websocket 或 SSE API 中,没有访问控制。这意味着用户仍然可以在 UI 中,看到来自正在运行的构建的实时更新,因为它们来自于 Websocket。

只能在 REST API 中读取的唯一资源是日志数据(也称为日志块)。

从严格的安全角度来看,实际上不能使用 Buildbot 的授权框架安全地拒绝对 Bot 的读取访问。访问控制被设计为限制只能通过 REST API 访问的控制 API。为减少攻击面,建议将 Buildbot 放在受访问控制的代理之后,如 OAuth2Proxy。

17.2. 端点匹配器

端点匹配器负责创建规则,以匹配 REST 端点,并且要求相应的角色。Buildbot 按照配置端点匹配器的顺序,处理它们。匹配端点的第一个规则将阻止进一步检查其它规则。当结果为拒绝时,如果想继续检查其它规则,那么设置 defaultDeny=False。如果没有端点匹配器匹配,那么授予访问权限。

可以通过在列表的末尾放置带有不存在角色的 AnyEndpointMatcher 的方式,实现默认拒绝策略。请注意,这将拒绝所有 REST API,并且大多数 UI 在遇到此类错误的情况下,未实现正确的访问拒绝消息。

每个 EndpointMatcher 类实现如下序列:

当前已实现多个端点匹配器。如果需要复杂的机制,那么需要实现自己的端点匹配器。在这种情况下,可以查看源代码,获取关于如何编写端点匹配器的详细示例。

17.3. 角色匹配器

角色匹配器负责创建规则,以匹配人员,授予他们角色。可以从认证插件提供的组信息授予角色,或者直接授予给人员的 Email。

17.4. 示例配置

下面的例子允许管理员控制所有事情,但允许匿名用户查看构建结果:

更复杂的配置,按分支分开:

使用 Github 认证,允许 “Buildbot” 组织内的用户对控制端点进行访问:


18. master.cfg 中的其它配置项

protocols 中包含与 Master 和 Worker 之间通信所使用的协议有关的信息。

title 字符串将出现在 HOME 页面的顶部(链接到 titleURL)。

buildbotURL 用于指出 Web 服务的地址,应该使用 www 中设置的端口。

激活 Web UI 的最小化配置,其中:

db_url 指定用于保存 Buildbot 状态的数据库。