介绍
Buildbot是一个基于Python的连续集成系统,用于自动化软件构建、测试和发布流程. 在之前的教程中,我们安装了Buildbot(https://andsky.com/tech/tutorials/how-to-install-buildbot-on-ubuntu-16-04),[created systemd Unit files](https://andsky.com/tech/tutorials/how-to-create-systemd-unit-files-for-buildbot)以允许服务器的 init系统管理流程,以及配置了Nginx作为反向代理程序以将SSL安全浏览器请求直接发送到Buildbot的Web界面。
在本指南中,我们将展示如何设置连续集成系统以自动测试存储库的新更改。我们将使用一个简单的Node.js应用程序来演示测试过程和必要的配置。为了将我们的测试环境与Buildbot主机隔离,我们将创建一个Docker图像以作为我们的Buildbot工作者运行。
前提条件
要遵循本教程,您将需要:
- ** 一个 Ubuntu 16.04 服务器,至少具有 1 GB 的 RAM**,通过遵循 Ubuntu 16.04 初始服务器设置指南
配置非 root
sudo
用户和防火墙
此外,您还需要在服务器上完成以下教程:
- 如何在Ubuntu 16.04上安装Buildbot
- 如何在Ubuntu 16.04上创建系统单元文件
- 如何在Ubuntu 16.04上安装 Nginx
- 如何在Ubuntu 16.04上使用加密来保护 Nginx
- 如何在Ubuntu 16.04上安装和使用Docker:(只有步骤1和2)
一旦你完成了这些要求,你已经准备好开始。
在 GitHub 中建立示例存储库
在我们开始配置Buildbot之前,我们将看看我们将用于本指南的示例存储库。
在您的 Web 浏览器中,请访问我们将使用的 hello hapi 应用程序在 GitHub 用于演示. 这个应用程序是一个简单的hello world
程序,有几个单元和集成测试,写在 hapi,一个 Node.js 网页框架。
由于此示例用于演示各种连续集成系统,您可能会注意到一些用于为其他系统定义管道的文件. 对于 Buildbot,我们将在服务器上定义构建步骤,而不是在库中。
後來,我們將在庫內設定 Buildbot 的 webhooks,以便變更會自動引發新的測試。
点击屏幕右上角的 Fork按钮:
如果您是 GitHub 组织的成员,您可能会被问到您想把库放在哪里:
选择一个帐户或组织后,将添加存储库的副本到您的帐户:
您将在 Buildbot 配置中使用您的叉子 URL. 现在我们有一个存储 URL,我们可以开始配置 Buildbot。
设置 Docker 用于 Buildbot
首先,我们需要在 Docker 和 Buildbot 之间配置访问,然后,我们需要为我们的容器创建一个 Docker 图像。
配置用于 Buildbot 的 Docker 访问
我们需要允许Buildbot和Docker在几个不同的层面进行通信。
首先,我们需要确保 Buildbot 进程可以访问 Docker 对象,我们可以通过将 buildbot 用户添加到 docker 组中来做到这一点:
1sudo usermod -aG docker buildbot
此新组将在下次重新启动 Buildbot 主机时为 Buildbot 提供,我们将在稍后进行。
由于 Buildbot 是用 Python 编写的,它利用了 docker-py
Python 包而不是直接发出 Docker 命令。
您可以通过键入docker-py
来安装:
1sudo -H pip install docker-py
最后,我们需要打开从容器到主机系统和外部世界的网络访问,我们可以通过在我们的防火墙中允许一个docker0
界面的例外来做到这一点。
允许从docker0
界面访问流量,键入:
1sudo ufw allow in on docker0
Buildbot 和 Docker 现在应该能够有效地彼此沟通。
创建 Docker 图像以作为 Buildbot 工人使用
接下来,我们将创建一个 Docker 容器以作为 Buildbot 工人运行我们的测试。Buildbot 可以动态启动 Docker 容器以作为工人使用,但容器首先需要与一些 Buildbot 工人组件一起构建。
幸运的是,Buildbot 项目提供了一个基本的 Buildbot 工人图像,它已经配置了所有 Buildbot 特定的要求。
在我们的情况下,我们将使用的示例应用程序(https://github.com/do-community/hello_hapi)是一个Node.js应用程序,所以我们需要确保Node.js在图像上可用。
要定义我们的图像,请在您的主目录中创建并打开名为Dockerfile
的文件:
1nano ~/Dockerfile
在此檔案中,我們使用「FROM buildbot/buildbot-worker:master」來基於 Buildbot 工作者圖像的圖像,然後,我們可以切換到「root」用戶來安裝 Node.js,然後再切換到「buildbot」用戶來執行實際命令:
1[label ~/Dockerfile]
2FROM buildbot/buildbot-worker:master
3
4USER root
5RUN curl -sL https://deb.nodesource.com/setup_8.x | bash -
6RUN apt-get install -y nodejs
7USER buildbot
保存并关闭文件,当你完成。
一旦我们有一个Dockerfile
,我们可以从中构建我们的图像,我们将将图像称为npm-worker
,以明确我们安装的额外依赖性:
1docker build -t npm-worker - < ~/Dockerfile
Docker将根据我们在Dockerfile
中描述的命令开始构建您的图像,它将拉下基础图像及其依赖层,安装Node.js,然后将所产生的环境保存到名为npm-worker
的图像中。
设置 Buildbot 主机
现在我们有一个Docker图像,我们可以配置Buildbot大师来使用它。
由于我们正在定义一个全新的构建过程,并且由于我们对主配置的定制到目前为止是最小的,我们将从零开始我们的配置,以避免丢失当前信息,我们将将原始文件移动到备份文件:
1sudo mv /home/buildbot/master/master.cfg /home/buildbot/master/master.cfg.bak
显示备份文件的配置,以便我们可以复制一些重要的值,以便在我们的新配置中使用:
1sudo cat /home/buildbot/master/master.cfg.bak
我们想要转移到新配置的重要部分是用户凭证和权限,请在输出中寻找从c['www']['authz']
和c['www']['auth']
开始的配置部分:
1[secondary_label Output]
2. . .
3c['www']['authz'] = util.Authz(
4 allowRules = [
5 util.AnyEndpointMatcher(role="admins")
6 ],
7 roleMatchers = [
8 util.RolesFromUsername(roles=['admins'], usernames=['Sammy'])
9 ]
10)
11c['www']['auth'] = util.UserPasswordAuth({'Sammy': 'Password'})
12. . .
将这些细节添加到我们的新的 Buildbot 主配置中,以保持我们的用户和身份验证设置。
现在,创建一个新的 master.cfg 文件,我们可以重新定义 Buildbot 实例的行为:
1sudo nano /home/buildbot/master/master.cfg
我们将在本文件中定义我们的新的Buildbot主配置。
创建基本项目配置
Buildbot 配置文件实际上是一个 Python 模块,它提供了很大的灵活性,而不是一些复杂性。
我们将从一些基本的配置开始. 将以下行粘贴到您的文件中:
1[label /home/buildbot/master/master.cfg]
2# -*- python -*-
3# ex: set filetype=python:
4from buildbot.plugins import *
5
6c = BuildmasterConfig = {}
7
8# Basic config
9c['buildbotNetUsageData'] = None
10c['title'] = "Hello Hapi"
11c['titleURL'] = "https://github.com/your_github_name/hello_hapi"
12c['buildbotURL'] = "https://buildmaster_domain_name/"
13c['protocols'] = {'pb': {'port': 9989}}
文件的顶部包含几个评论,许多文本编辑器能够解释以正确地应用语法突出化。之后,我们从buildbot.plugins
包中导入一切,以便我们拥有构建我们的配置的工具。
Buildbot 配置都是由一个名为BuildmasterConfig
的字典定义的,所以我们将这个变量设置为一个空字典开始。
在下列配置中要注意的一些事情:
*「buildbotNetUsageData」設定為「None」。 如果您想向開發人員報告使用數據,請將此變更為「基本」字串。 *「標題」和「titleURL」反映了項目名稱和 GitHub 儲存庫。 使用連結到您自己的子。 *「buildbotURL」設定為 Buildbot 主機的 SSL 安全域名。 請記住,要從「https://」開始,並以追溯 slash `/' 結束。
- 與我們最近的配置不同,「协议」定義並不關聯到本地主機。 我們需要允許從 Docker 容器的連接到 Docker 橋樑網絡「docker0」。
配置 Docker 工人
接下来,我们需要定义我们的 Docker 工人 Buildbot 将使用 Docker 以根据需要提供工人. 要做到这一点,它需要知道如何连接到 Docker 以及使用哪些图像。
在文件的底部插入以下内容:
1[label /home/buildbot/master/master.cfg]
2. . .
3
4# Workers
5c['workers'] = []
6c['workers'].append(worker.DockerLatentWorker("npm-docker-worker", None,
7 docker_host='unix://var/run/docker.sock',
8 image='npm-worker',
9 masterFQDN='buildmaster_domain_name'))
c['workers'] = []
行显示了我们将通过配置使用的一种基本惯例。我们将配置字典中的一个密钥设置为一个空的列表。
为了定义我们的工人,我们创建并将worker.DockerLatentWorker
实例附加到工人
列表中。我们将这个工人命名为npm-docker-worker
,以便在配置中稍后可以参考它。然后我们将docker_host
设置为Docker的接口位置,并提供我们创建的Docker图像的名称(在我们的情况下npm-worker
).我们将masterFQDN
设置为我们的Buildbot主机的域名,以确保容器可以访问主机,而不论服务器的内部主机名设置如何。
设置一个日程表
接下来,我们将定义一个日程安排器 Buildbot 使用日程安排器来决定何时以及如何根据它从更改源或更改链接中接收的更改来运行构建(我们将稍后配置更改链接)。
在文件的底部插入以下配置:
1[label /home/buildbot/master/master.cfg]
2. . .
3
4# Schedulers
5c['schedulers'] = []
6c['schedulers'].append(schedulers.SingleBranchScheduler(
7 name="hello_hapi",
8 change_filter=util.ChangeFilter(project='your_github_name/hello_hapi', branch='master'),
9 treeStableTimer=3,
10 builderNames=["npm"]))
在此情况下,我们附加一个schedulers.SingleBranchScheduler
实例,这使我们能够在存储库中查看单个分支,这简化了配置。
我们将编程器命名为hello_hapi
以正确识别它,然后我们定义一个更改过滤器,可以将来自不同来源的许多不同的更改集交给编程器,更改过滤器将定义一组标准,这将决定该更改是否应该由这个特定的编程器处理。
接下来,我们将treeStableTimer
设置为3秒,这有助于阻止Buildbot为密切相关的更改排队,最后,我们定义当更改符合我们的标准时应该使用的构建者的名称(我们会暂时定义这个构建者)。
配置 Node.js 项目的构建工厂
接下来,我们将配置一个构建工厂来处理 Node.js 项目. 构建工厂负责定义构建或在我们的案例中测试一个项目所需的步骤。
将下列内容放在您的文件底部:
1[label /home/buildbot/master/master.cfg]
2. . .
3
4# Build Factories
5npm_f = util.BuildFactory()
6npm_f.addStep(steps.GitHub(repourl='git://github.com/your_github_name/hello_hapi.git', mode='full', method='clobber'))
7npm_f.addStep(steps.ShellCommand(command=["npm", "install"]))
8npm_f.addStep(steps.ShellCommand(command=["npm", "test"]))
首先,我们定义了一个构建工厂,名为npm_f
。我们添加的第一个步骤是steps.GitHub
实例. 在这里,我们设置了应该被拖入构建器的存储库。
我们添加的第二步和第三步是steps.ShellCommand
对象,这些对象定义了壳命令在构建过程中在库内运行。在我们的情况下,我们需要运行npm install
来收集项目的依赖性。之后,我们需要运行npm test
来运行我们的测试套件。在大多数情况下,建议将命令定义为列表([
npm,
install`]),以防止壳在命令中的元素上应用不必要的扩展。
创建一个建设者
一旦我们有一个构建工厂,并添加了步骤,我们可以设置一个构建器,构建器将我们已经定义的许多元素联系在一起,以确定如何执行构建。
在文件的底部插入以下配置:
1[label /home/buildbot/master/master.cfg]
2. . .
3
4# Builders
5c['builders'] = []
6c['builders'].append(
7 util.BuilderConfig(name="npm",
8 workernames=["npm-docker-worker"],
9 factory=npm_f))
请记住,我们的构建工厂叫做npm_f
,我们的 Docker 工人叫做npm-docker-worker
,我们定义的日程表将任务传递给名为npm
的工人。
配置数据库和 Web 界面
最后,我们可以配置数据库和 Web 界面设置. 与前面许多项目不同,这两个设置被定义为字典而不是列表. db
字典只是指向已经在我们的/home/buildbot/master
目录中的state.sqlite
文件。 www
字典包含大量额外的配置。
将下列信息粘贴到文件底部. 将您从原始 Buildbot 主机配置中复制的身份验证信息替换为下面的身份验证块:
1[label /home/buildbot/master/master.cfg]
2. . .
3
4# Database
5c['db'] = { 'db_url': "sqlite:///state.sqlite",}
6
7# Web Interface
8c['www'] = dict(port=8010, plugins=dict(waterfall_view={}, console_view={}))
9
10# Auth info copied from the original configuration
11c['www']['authz'] = util.Authz(
12 allowRules = [
13 util.AnyEndpointMatcher(role="admins")
14 ],
15 roleMatchers = [
16 util.RolesFromUsername(roles=['admins'], usernames=['Sammy'])
17 ]
18)
19c['www']['auth'] = util.UserPasswordAuth({'Sammy': 'Password'})
20# End of auth info copied from the original configuration
21
22# GitHub webhook receiver
23c['www']['change_hook_dialects'] = {
24 'github': {
25 'secret': 'your_secret_value',
26 'strict': True,
27 }
28}
定义数据库设置后,我们创建一个www
字典,从定义要倾听的端口和某些视图来开始,然后我们添加我们从之前的Buildbot配置文件中提取的验证要求。
最后,我们在www
字典中定义了一个名为change_hook_dialects
的字典,我们使用这个词典来定义一个GitHub的更改链接,它会听取GitHub的webhook消息。
完成后,保存并关闭文件。
重新启动 Buildbot 主机以应用新配置
此时,我们已经完全重新配置了 Buildbot 主流程,我们需要重新启动 Buildbot 主流程来实现这些更改。
在我们这样做之前,重要的是要检查我们的文件是否有语法错误. 由于我们从头开始重新构建了配置,我们可能会引入一些错误。
检查文件的语法通过键入:
1sudo buildbot checkconfig /home/buildbot/master
该命令会报告发现的任何问题. 如果没有发现任何错误,您将收到一个看起来像这样的消息:
1[secondary_label Output]
2Config file is good!
如果报告了任何错误,请通过仔细阅读错误消息来更好地了解错误。
当不再出现任何错误时,请通过键入以下方式重新启动 Buildbot 主服务:
1sudo systemctl restart buildbot-master
检查操作是否成功通过键入:
1sudo systemctl status buildbot-master
1[secondary_label Output]
2● buildbot-master.service - BuildBot master service
3 Loaded: loaded (/etc/systemd/system/buildbot-master.service; enabled; vendor preset: enabled)
4 Active: active (running) since Tue 2017-06-27 19:24:07 UTC; 2s ago
5 Main PID: 8298 (buildbot)
6 Tasks: 2
7 Memory: 51.7M
8 CPU: 1.782s
9 CGroup: /system.slice/buildbot-master.service
10 └─8298 /usr/bin/python /usr/local/bin/buildbot start --nodaemon
11
12Jun 27 19:24:07 bb5 systemd[1]: Started BuildBot master service
如果该服务成功重新启动,它将被标记为活跃。
在示例存储库中创建一个 GitHub Webhook
现在Buildbot已配置到一个Web端点来接受GitHub webhook帖子,我们可以为我们的叉子配置一个webhook。
在您的 Web 浏览器中,导航到您的示例项目存储库:
1https://github.com/your_github_user/hello_hapi
在设置页面的左侧菜单中,点击 Webhooks(GitHub可能会要求您在此过程中重新输入密码以确认您的身份):
点击右侧的 ** 添加 webhook ** 按钮来添加新的 webhook。
下面的页面将包含一个表格来定义您的webhook。在 Payload URL字段中,添加您项目的GitHub更改链接终端的URL。这是通过指定https://
协议,然后是您的Buildbot大师的域名,然后是/change_hook/github
。
将内容类型设置为 application/x-www-form-urlencoded
. 在 Secret 字段中,输入您在 Buildbot 主配置文件中选择的秘密密密码句子。
完成后,点击添加webhook
按钮。
您将返回项目的 webhooks 索引,在那里将显示您的新 webhook. 如果您更新几次,应该在您的 webhook 旁边显示一个绿色标记图标,表示消息已成功传输:
如果您看到一个红色X,请再次点击网关,然后滚动到 最近的交付部分. 如果您点击失败的交付,可获取有关错误的更多信息。
测试Webhook
现在我们已经有了我们的 webhook,我们可以测试,以确保当我们对库进行更改时,Buildbot会被警告,在Docker中触发构建,并能够成功执行测试套件。
在你的GitHub叉子的主页上,点击绿色克隆或下载
按钮左侧的创建新文件
按钮:
在下面的屏幕上,创建一个dummy_file
并填写一些文本:
点击页面底部的 ** 承诺新文件 ** 按钮,当你完成。
接下来,访问您的 Buildbot Web 界面并登录,如果您尚未认证。
取决于您将dummy_file
向您的存储库承诺了多久,您可能会看到正在进行的构建,它看起来像这样:
如果构建已经完成,则将位于最近构建
部分:
我们定义的构建者的名称npm
用于标记构建,在示例中,我们还可以看到从以前的主配置中使用的样本构建器的较旧运行。
无论进展如何,请单击构建者名称和构建号码链接,访问构建详细信息页面. 此视图包含有关所进行的构建的信息。
如果您点击一个步骤,则将显示命令的输出,这可以帮助调试,如果有什么不对劲:
在上述输出中,我们可以验证Buildbot在我们的测试套件中成功地运行了三个测试。
如果构建未成功完成,您可能需要检查的其他部分包括构建详细信息页面上的其他卡以及 /home/buildbot/master/twistd.log
文件。
调整 Buildbot 服务
在我们完成之前,我们应该对我们的Buildbot服务做一些调整。
目前,我们为我们不再使用的员工定义了一个buildbot-worker
服务(当需要时,我们的Docker worker会自动启动)。
要停止运行服务并禁用它在启动时启动,键入:
1sudo systemctl stop buildbot-worker
2sudo systemctl disable buildbot-worker
1[secondary_label Output]
2Removed symlink /etc/systemd/system/buildbot-master.service.wants/buildbot-worker.service.
上面的输出表示,工人不会下次启动。 若要验证该服务不再运行,请键入:
1sudo systemctl status buildbot-worker
1[secondary_label Output]
2● buildbot-worker.service - BuildBot worker service
3 Loaded: loaded (/etc/systemd/system/buildbot-worker.service; disabled; vendor preset: enabled)
4 Active: inactive (dead)
5
6Jun 27 21:12:48 bb6 systemd[1]: Started BuildBot worker service.
7Jun 27 21:55:51 bb6 systemd[1]: Stopping BuildBot worker service...
8Jun 27 21:55:51 bb6 systemd[1]: Stopped BuildBot worker service.
我们应该做的最后一件事是建立我们的Buildbot主服务和Docker大卫之间的软依赖性,因为Buildbot主服务将无法在没有Docker的情况下提供新员工,我们应该定义这一要求。
在/etc/systemd/system
目录中打开buildbot-master.service
文件以调整服务文件:
1sudo nano /etc/systemd/system/buildbot-master.service
在[单位]
部分中,在network.target
项后,在After
指令中添加docker.service
。
1[label /etc/systemd/system/buildbot-master.service]
2[Unit]
3Description=BuildBot master service
4After=network.target docker.service
5Wants=docker.service
6
7[Service]
8User=buildbot
9Group=buildbot
10WorkingDirectory=/home/buildbot/master
11ExecStart=/usr/local/bin/buildbot start --nodaemon
12
13[Install]
14WantedBy=multi-user.target
保存并关闭文件,当你完成。
重新加载 systemd daemon 和服务,立即应用配置:
1sudo systemctl daemon-reload
2sudo systemctl restart buildbot-master
Buildbot 主流程应该在 Docker 可用后开始。
结论
在本教程中,我们配置 Buildbot 以使用 webhooks 听取 GitHub 存储库的更改。当收到更改时,Buildbot 会基于自定义 Docker 图像启动一个容器来测试新的 commit。Docker 图像包含一个 Buildbot 工人实例以及测试我们的项目代码所需的依赖性。