介绍
Buildbot是一个基于Python的持续集成系统,用于自动化软件构建,测试和发布流程。 在之前的教程中,我们安装了Buildbot , 创建了systemd Unit文件,以允许服务器的init系统管理进程,并将Nginx配置为反向代理 ,以便将SSL安全的浏览器请求引导到Buildbot的Web界面。
在本指南中,我们将演示如何设置一个连续的集成系统来自动测试对存储库的新更改。 我们将使用一个简单的Node.js应用程序来演示测试过程和必要的配置。 为了将我们的测试环境与Buildbot主机隔离开,我们将创建一个Docker映像作为我们的Buildbot工作人员运行。 然后,我们将配置Buildbot主机以观看GitHub存储库以进行更改,每次检测到新的更改时自动测试。
先决条件
要遵循本教程,您将需要:
- 一个具有至少1 GB RAM的Ubuntu 16.04服务器,通过遵循Ubuntu 16.04初始服务器设置指南配置非根
sudo
用户和防火墙
另外,您需要在服务器上完成以下教程:
- 如何在Ubuntu 16.04上安装Buildbot
- 如何为Buildbot创建Systemd单元文件
- 如何在Ubuntu 16.04上安装Nginx
- 如何使用Ubuntu 16.04加密Nginx 。
- 如何使用Nginx反向代理使用SSL配置Buildbot
- 如何在Ubuntu 16.04上安装和使用Docker :(仅第1步和2)
当您完成这些要求后,即可开始。
在GitHub中叉出示例存储库
在我们开始配置Buildbot之前,我们将看看我们将为本指南使用的示例存储库。
在您的Web浏览器中,访问我们将用于演示的GitHub上的hello hapi应用程序 。 这个应用程序是一个简单的“hello world”程序,其中包含一些单元和集成测试,用hapi编写,一个Node.js的Web框架。
由于此示例用于演示各种连续集成系统,因此您可能会注意到一些用于为其他系统定义管道的文件。 对于Buildbot,我们将在服务器上定义构建步骤,而不是在存储库中。
之后,我们将在我们的存储库中为Buildbot设置webhook,以便更改将自动触发新的测试。 现在,我们需要创建我们自己的存储库的fork。
点击屏幕右上角的Fork按钮:
如果您是GitHub组织的成员,可能会询问您要在哪里分叉存储库:
选择帐户或组织后,存储库的副本将添加到您的帐户中:
您将在Buildbot配置中使用您的分支的URL。 现在我们有一个存储库URL,我们可以开始配置Buildbot。
为Buildbot设置Docker
我们将首先设置Docker,以便Buildbot使用它来执行构建。 首先,我们需要配置Docker和Buildbot之间的访问。 之后,我们需要创建一个Docker镜像来使用我们的容器。
配置对Dock的Dockbot访问
我们需要允许Buildbot和Docker在几个不同的层面进行通信。
首先,我们需要确保Buildbot进程可以访问Docker守护程序。 我们可以通过将buildbot用户添加到docker组来实现:
sudo usermod -aG docker buildbot
下一次Buildbot主机重新启动时,这个新组将可用于Buildbot,稍后我们将做。
我们还需要确保Buildbot知道如何与Docker通信。 由于Buildbot是用Python编写的,它利用docker docker-py
Python包而不是直接发出Docker命令。
您可以通过键入以下方法安装docker-py
:
sudo -H pip install docker-py
最后,我们需要开放从容器到主机系统和外界的网络访问。 我们可以通过允许防火墙中docker0
接口的异常来实现。
通过键入以下docker0
可以访问docker0
界面的流量:
sudo ufw allow in on docker0
Buildbot和Docker应该能够有效地相互通信。
创建一个Docker图像以用作Buildbot Worker
接下来,我们将创建一个Docker容器,用作Buildbot工作人员来运行我们的测试。 Buildbot可以动态启动Docker容器以作为工作人员使用,但首先需要使用包含的一些Buildbot工作组件构建容器。
幸运的是,Buildbot项目提供了一个基本的Buildbot工作者映像 ,它已经具有配置了所有Buildbot特定的所有要求。 我们只需要使用此映像作为基础,并安装我们项目需要的附加依赖项。
在我们的例子中,我们将使用的示例应用程序是一个Node.js应用程序,所以我们需要确保Node.js在映像上可用。
要定义我们的图像, Dockerfile
在主目录中创建并打开名为Dockerfile
的文件:
nano ~/Dockerfile
在这个文件中,我们使用FROM buildbot/buildbot-worker:master
将我们的图像从Buildbot工作映像中FROM buildbot/buildbot-worker:master
。 之后,我们可以切换到root
用户安装Node.js,然后切换回buildbot
用户来运行实际的命令:
FROM buildbot/buildbot-worker:master
USER root
RUN curl -sL https://deb.nodesource.com/setup_8.x | bash -
RUN apt-get install -y nodejs
USER buildbot
完成后保存并关闭文件。
一旦我们有一个Dockerfile
,我们可以从它构建我们的图像。 我们将调用映像npm-worker
来明确我们安装的额外的依赖关系:
docker build -t npm-worker - < ~/Dockerfile
Docker将根据Dockerfile
概述的命令开始构建您的映像。 它将下拉基础映像及其依赖层,安装Node.js,然后将生成的环境保存到名为npm-worker
的映像。
配置Buildbot Master
现在我们有一个Docker的图像,我们可以配置Buildbot主机来使用它。
因为我们正在定义一个全新的构建过程,并且由于我们对主配置的定制已经很小,直到此为止,我们将从头开始配置。 为了避免丢失当前的信息,我们将原始文件移动到备份文件:
sudo mv /home/buildbot/master/master.cfg /home/buildbot/master/master.cfg.bak
显示备份文件的配置,以便我们可以复制几个重要的值以在我们的新配置中使用:
sudo cat /home/buildbot/master/master.cfg.bak
我们要转移到新配置的重要部分是用户凭据和权限。 在输出中查找c['www']['authz']
和c['www']['auth']
开头的配置部分:
Output. . .
c['www']['authz'] = util.Authz(
allowRules = [
util.AnyEndpointMatcher(role="admins")
],
roleMatchers = [
util.RolesFromUsername(roles=['admins'], usernames=['Sammy'])
]
)
c['www']['auth'] = util.UserPasswordAuth({'Sammy': 'Password'})
. . .
复制并将这些行保存在某个位置,以便稍后引用它们。 我们将把这些细节添加到我们新的Buildbot主配置中,以保留我们的用户和身份验证设置。
现在,创建一个新的master.cfg
文件,我们可以重新定义我们的Buildbot实例的行为:
sudo nano /home/buildbot/master/master.cfg
我们将在此文件中定义新的Buildbot主配置。
设置基本项目配置
Buildbot配置文件实际上是一个Python模块,它提供了很大的灵活性,牺牲了一些复杂性。
我们将从一些基本配置开始。 将以下行粘贴到文件中:
# -*- python -*-
# ex: set filetype=python:
from buildbot.plugins import *
c = BuildmasterConfig = {}
# Basic config
c['buildbotNetUsageData'] = None
c['title'] = "Hello Hapi"
c['titleURL'] = "https://github.com/your_github_name/hello_hapi"
c['buildbotURL'] = "https://buildmaster_domain_name/"
c['protocols'] = {'pb': {'port': 9989}}
该文件的顶部包含许多文本编辑器能够解释以正确应用语法高亮显示的几个注释。 之后,我们从buildbot.plugins
包中导入所有内容,以便我们有可用的工具来构建我们的配置。
Buildbot配置全部由一个名为BuildmasterConfig
的字典定义,因此我们将此变量设置为空字典以开始。 我们创建一个名为c
的速记变量,设置为同一个字典,以减少整个文件中所需的打字数量。
以下配置中需要注意的一些事项:
-
buildbotNetUsageData
设置为None
。 如果要向开发人员报告使用情况数据,请将其更改为字符串"basic"
。 -
title
和titleURL
反映项目名称和GitHub存储库。 使用您自己的叉子的链接。 -
buildbotURL
设置为Buildbot主机的SSL保护域名。 请记住以https://
开始,并以尾部斜杠/
结尾。 - 与上一次配置不同,
protocol
定义不绑定到localhost。 我们需要允许Docker容器通过Docker bridge networkdocker0
进行连接。
配置Docker Worker
接下来,我们需要定义我们的Docker worker。 Buildbot将使用Docker根据需要配置员工。 要做到这一点,它需要知道如何连接Docker和使用哪个图像。
在文件的底部粘贴以下内容:
. . .
# Workers
c['workers'] = []
c['workers'].append(worker.DockerLatentWorker("npm-docker-worker", None,
docker_host='unix://var/run/docker.sock',
image='npm-worker',
masterFQDN='buildmaster_domain_name'))
c['workers'] = []
行演示了我们将在配置中使用的基本约定。 我们将配置字典中的键设置为空列表。 然后我们将元素附加到列表中以实现实际配置。 这给我们以后添加额外元素的灵活性。
要定义我们的worker,我们创建一个worker.DockerLatentWorker
实例worker.DockerLatentWorker
加到worker
列表中。 我们将这个工作人员命名为npm-docker-worker
以便稍后在配置中引用它。 然后我们将docker_host
设置为Docker的套接字位置,并提供我们创建的Docker映像的名称(在本例中为npm-worker
)。 我们将masterFQDN
设置为我们的Buildbot主域名,以确保容器可以访问主服务器,而不管服务器的内部主机名设置如何。
配置计划程序
接下来,我们将定义一个调度程序。 Buildbot使用调度器来决定何时以及如何根据从更改源接收的更改或更改钩子来运行构建(稍后将配置更改挂钩)。
将以下配置粘贴到文件的底部:
. . .
# Schedulers
c['schedulers'] = []
c['schedulers'].append(schedulers.SingleBranchScheduler(
name="hello_hapi",
change_filter=util.ChangeFilter(project='your_github_name/hello_hapi', branch='master'),
treeStableTimer=3,
builderNames=["npm"]))
我们使用相同的方法将我们的配置附加到空列表中。 在这种情况下,我们附加一个schedulers.SingleBranchScheduler
实例。 这使我们能够在存储库上观察一个分支,从而简化了配置。
我们命名调度程序“hello_hapi”来正确识别它。 然后我们定义一个更改过滤器。 来自不同来源的许多不同的变化可以被递交给调度器。 更改过滤器定义一组标准,用于确定该特定调度程序是否处理该问题的更改。 在我们的例子中,我们根据项目的名称进行过滤,由GitHub webhook和我们希望观看的分支报告。
接下来,我们将treeStableTimer
设置为等待其他更改的时间等待3秒。 这有助于防止Buildbot对与密切相关的更改排队许多小型构建。 最后,我们定义当更改符合我们的条件时应该使用的构建器的名称(我们将定义该构建器)。
为Node.js项目配置构建工厂
接下来,我们将配置一个用于处理Node.js项目的构建工厂。 构建工厂负责定义应该在项目中构建或在我们的案例中测试的步骤。 它通过定义一个util.BuildFactory
实例,然后添加应执行的顺序步骤。
将以下内容粘贴到文件底部:
. . .
# Build Factories
npm_f = util.BuildFactory()
npm_f.addStep(steps.GitHub(repourl='git://github.com/your_github_name/hello_hapi.git', mode='full', method='clobber'))
npm_f.addStep(steps.ShellCommand(command=["npm", "install"]))
npm_f.addStep(steps.ShellCommand(command=["npm", "test"]))
首先,我们定义一个名为npm_f
的构建工厂。 我们添加的第一步是steps.GitHub
实例。 在这里,我们设置应该被下拉到构建器中的存储库。 我们将mode
设置为“full”,并将method
“clobber”完全清理我们的存储库,每次我们提取新的代码。
我们添加的第二和第三步骤是在构建期间定义shell命令在存储库内运行的步骤steps.ShellCommand
对象。 在我们的例子中,我们需要运行npm install
来收集项目的依赖。 之后,我们需要运行npm test
来运行我们的测试套件。 在大多数情况下,建议将命令定义为列表( ["npm", "install"]
),以防止shell对命令中的元素进行不必要的扩展。
配置生成器
一旦我们建立了一个添加步骤的工厂,我们就可以建立一个构建器。 构建器将许多已经定义的元素绑定在一起,以确定构建将如何执行。
将以下配置粘贴到文件的底部:
. . .
# Builders
c['builders'] = []
c['builders'].append(
util.BuilderConfig(name="npm",
workernames=["npm-docker-worker"],
factory=npm_f))
我们将一个util.BuilderConfig
对象附加到builders
列表中。 请记住,我们的构建工厂称为npm_f
,我们的Docker工作器称为npm-docker-worker
,并且我们定义的调度程序将任务传递给名为npm
。 我们的构建器定义这些元素之间的关系,以便我们的调度程序的更改将导致在Docker工作程序中执行构建工厂步骤。
配置数据库和Web界面
最后,我们可以配置数据库和Web界面设置。 与许多以前的项目不同,这两个设置被定义为词典而不是列表。 db
字典只指向我们的/home/buildbot/master
目录中的state.sqlite
文件。 www
字典包含大量附加配置。
将以下内容粘贴到文件底部。 将您从原始Buildbot主配置复制的认证信息替换为以下认证块:
. . .
# Database
c['db'] = { 'db_url': "sqlite:///state.sqlite",}
# Web Interface
c['www'] = dict(port=8010, plugins=dict(waterfall_view={}, console_view={}))
# Auth info copied from the original configuration
c['www']['authz'] = util.Authz(
allowRules = [
util.AnyEndpointMatcher(role="admins")
],
roleMatchers = [
util.RolesFromUsername(roles=['admins'], usernames=['Sammy'])
]
)
c['www']['auth'] = util.UserPasswordAuth({'Sammy': 'Password'})
# End of auth info copied from the original configuration
# GitHub webhook receiver
c['www']['change_hook_dialects'] = {
'github': {
'secret': 'your_secret_value',
'strict': True,
}
}
定义数据库设置后,我们创建一个www
字典,首先定义要监听的端口和一些要包含在Web UI中的视图。 接下来,我们添加从以前的Buildbot配置文件中提取的身份验证要求。
最后,我们在www
字典中定义一个名为change_hook_dialects
的字典。 我们使用它来定义一个GitHub更改钩子,它将从GitHub收听webhook消息。 为您的secret
选择一个安全的密码,这将被GitHub用来验证它将发送的消息。
完成后,保存并关闭文件。
重新启动Buildbot Master以应用新配置
在这一点上,我们完全重新配置了Buildbot主进程。 我们需要重新启动Buildbot主程序来实现更改。
在我们这样做之前,检查我们的文件的语法错误很重要。 由于我们从头开始重新构建了配置,所以很有可能我们引入了一些错误。
通过键入以下内容检查文件的语法:
sudo buildbot checkconfig /home/buildbot/master
该命令将报告它发现的任何问题。 如果没有发现错误,您将收到一条如下所示的消息:
OutputConfig file is good!
如果有任何错误报告,请仔细阅读错误信息,以便更好地了解错误。 再次打开配置文件以尝试解决任何问题。
当不再有任何错误时,键入以下内容重新启动Buildbot主服务:
sudo systemctl restart buildbot-master
输入以下内容,检查操作是否成功:
sudo systemctl status buildbot-master
Output● buildbot-master.service - BuildBot master service
Loaded: loaded (/etc/systemd/system/buildbot-master.service; enabled; vendor preset: enabled)
Active: active (running) since Tue 2017-06-27 19:24:07 UTC; 2s ago
Main PID: 8298 (buildbot)
Tasks: 2
Memory: 51.7M
CPU: 1.782s
CGroup: /system.slice/buildbot-master.service
└─8298 /usr/bin/python /usr/local/bin/buildbot start --nodaemon
Jun 27 19:24:07 bb5 systemd[1]: Started BuildBot master service
如果服务能够成功重新启动,它将被标记为活动。
在Example Repository中创建一个GitHub Webhook
现在,Buildbot配置了一个Web端点来接受GitHub的webhook帖子,我们可以为我们的fork配置一个webhook。
在您的Web浏览器中,导航到示例项目存储库的分支:
https://github.com/your_github_user/hello_hapi
单击设置选项卡查看项目设置。 在设置页面的左侧菜单中,单击Webhooks (GitHub可能会提示您在此过程中重新输入密码以确认您的身份):
单击右侧的添加webhook按钮添加一个新的webhook。
以下页面将包含一个表单来定义您的webhook。 在有效载荷URL字段中,添加项目的GitHub更改钩子端点的URL。 这是通过指定https://
协议,后跟您的Buildbot主机域名,后跟/change_hook/github
。
将内容类型设置为application/x-www-form-urlencoded
。 在秘密字段中,输入您在Buildbot主配置文件中选择的秘密密码。 您可以选择“Just the push event”触发器,并勾选“Active”复选框:
完成后,单击添加webhook按钮。
您将返回到项目的webhooks索引,您将在其中显示新的webhook。 如果刷新几次,则应在webhook旁边显示一个绿色的复选标记图标,表示已成功传输消息:
如果你看到一个红色的X,再次点击webhook,然后向下滚动到最近的交货部分。 如果您点击交货失败,可以使用更多关于出现问题的信息。
测试Webhook
现在我们有了我们的webhook,我们可以测试确保当我们更改我们的存储库时,Buildbot被提醒,在Docker中触发一个构建,并且能够成功地执行测试套件。
在您的GitHub分支的主页面中,点击绿色“克隆或下载”按钮左侧的创建新文件按钮:
在下面的屏幕上,创建一个dummy_file
并填写一些文本:
完成后,单击页面底部的提交新文件按钮。
接下来,访问您的Buildbot Web界面,如果您尚未进行身份验证,请登录。
根据您将dummy_file
到存储库的时间dummy_file
,您可能会看到正在进行中的构建,如下所示:
如果构建已经完成,那么它将在“最近的构建”部分中:
我们定义的构建器的名称“npm”用于标记构建。 在该示例中,我们还可以从先前的主配置中看到较早的样本构建器运行。
无论进度如何,单击构建器名称和构建号链接以访问构建详细信息页面。 此视图包含有关进行构建的信息。 我们添加到构建工厂的每一步将显示在其自己的部分中:
如果单击步骤,将显示命令的输出。 如果发生错误,这可以帮助调试
在上面的输出中,我们可以验证Buildbot在测试套件中成功运行了三个测试。
如果构建未成功完成,您可能希望检查的其他一些区域是构建详细信息页面上的其他选项卡以及/home/buildbot/master/twistd.log
文件。
调整Buildbot服务
在完成之前,我们应该对Buildbot服务进行一些调整。
目前,我们为一个我们不再使用buildbot-worker
人员定义了一个buildbot-worker
服务(我们的Docker工作在需要时自动启动)。 我们应该停止和禁止我们的老工人。
要停止正在运行的服务并禁用它从引导启动,请键入:
sudo systemctl stop buildbot-worker
sudo systemctl disable buildbot-worker
OutputRemoved symlink /etc/systemd/system/buildbot-master.service.wants/buildbot-worker.service.
上述输出表示工作人员将不会在下次启动时启动。 要验证该服务不再运行,请键入:
sudo systemctl status buildbot-worker
Output● buildbot-worker.service - BuildBot worker service
Loaded: loaded (/etc/systemd/system/buildbot-worker.service; disabled; vendor preset: enabled)
Active: inactive (dead)
Jun 27 21:12:48 bb6 systemd[1]: Started BuildBot worker service.
Jun 27 21:55:51 bb6 systemd[1]: Stopping BuildBot worker service...
Jun 27 21:55:51 bb6 systemd[1]: Stopped BuildBot worker service.
我们应该做的最后一件事是建立我们的Buildbot主服务和Docker守护程序之间的软依赖。 由于Buildbot主服务将无法在没有Docker的情况下配置新的员工,所以我们应该定义这个要求。
打开/etc/systemd/system
目录下的buildbot-master.service
文件来调整服务文件:
sudo nano /etc/systemd/system/buildbot-master.service
在[Unit]
部分,在network.target
项后添加docker.service
到After
指令。 添加一个另外的命令,也称为docker.service
。 该方法建立一个软依赖,而After
指令确定了起始顺序:
[Unit]
Description=BuildBot master service
After=network.target docker.service
Wants=docker.service
[Service]
User=buildbot
Group=buildbot
WorkingDirectory=/home/buildbot/master
ExecStart=/usr/local/bin/buildbot start --nodaemon
[Install]
WantedBy=multi-user.target
完成后保存并关闭文件。
重新加载systemd守护程序和服务以立即应用配置:
sudo systemctl daemon-reload
sudo systemctl restart buildbot-master
在Docker可用之后,现在应该启动Buildbot主程序。
结论
在本教程中,我们配置Buildbot以使用webhook监听对GitHub存储库的更改。 收到更改后,Buildbot将根据自定义Docker映像启动容器,以测试新的提交。 Docker映像包含一个Buildbot工作器实例以及测试我们的项目代码所需的依赖关系。 这样,当对库进行更改时,Buildbot就可以根据需要动态启动Buildbot工作。