如何在Ubuntu16.04上设置与Buildbot的持续集成
介绍
Buildbot 是一个基于 Python 的持续集成系统,用于自动化软件构建、测试和发布过程。 在之前的教程中,我们安装了Buildbot,创建了systemd Unit files让服务器的init系统管理进程,配置了Nginx作为反向代理为了将受 SSL 保护的浏览器请求定向到 Buildbot 的 Web 界面。
在本指南中,我们将演示如何设置持续集成系统来自动测试对存储库的新更改。 我们将使用一个简单的 Node.js 应用程序来演示测试过程和必要的配置。 为了将我们的测试环境与 Buildbot 主机隔离,我们将创建一个 Docker 映像作为我们的 Buildbot 工作人员运行。 然后,我们将配置 Buildbot master 以监视 GitHub 存储库的更改,并在每次检测到新更改时自动进行测试。
先决条件
要遵循本教程,您将需要:
- 一台具有至少 1 GB RAM 的 Ubuntu 16.04 服务器,按照 Ubuntu 16.04 初始服务器设置指南 配置非 root
sudo
用户和防火墙
此外,您还需要在服务器上完成以下教程:
- 如何在 Ubuntu 16.04 上安装 Buildbot
- 如何为 Buildbot 创建 Systemd 单元文件
- 如何在 Ubuntu 16.04 上安装 Nginx
- 如何在 Ubuntu 16.04 上使用 Let's Encrypt 保护 Nginx。
- 如何使用 Nginx 反向代理配置带有 SSL 的 Buildbot
- 如何在 Ubuntu 16.04 上安装和使用 Docker:(仅步骤 1 和 2)
完成这些要求后,您就可以开始了。
Fork GitHub 中的示例存储库
在开始配置 Buildbot 之前,我们将查看将用于本指南的示例存储库。
在您的网络浏览器中,访问我们将用于演示的 GitHub 上的 hello hapi 应用程序。 这个应用程序是一个简单的“hello world”程序,带有一些单元和集成测试,用 hapi 编写,一个 Node.js Web 框架。
由于此示例用于演示各种持续集成系统,您可能会注意到一些用于为其他系统定义管道的文件。 对于 Buildbot,我们将在服务器上而不是在存储库中定义构建步骤。
稍后,我们将在我们的存储库中为 Buildbot 设置 webhook,以便更改将自动触发新的测试。 现在,我们需要创建自己的存储库分支。
点击屏幕右上角的Fork按钮:
如果您是 GitHub 组织的成员,系统可能会询问您要将存储库分叉到哪里:
选择帐户或组织后,存储库的副本将添加到您的帐户:
您将在 Buildbot 配置中使用您的 fork 的 URL。 现在我们有了一个存储库 URL,我们可以开始配置 Buildbot。
为 Buildbot 设置 Docker
我们将从设置 Docker 开始,以便 Buildbot 使用它来执行构建。 首先,我们需要配置 Docker 和 Buildbot 之间的访问。 之后,我们需要创建一个 Docker 镜像以用于我们的容器。
为 Buildbot 配置对 Docker 的访问
我们需要允许 Buildbot 和 Docker 在几个不同的层次上进行通信。
首先,我们需要确保 Buildbot 进程可以访问 Docker 守护进程。 我们可以通过将 buildbot 用户添加到 docker 组来做到这一点:
sudo usermod -aG docker buildbot
这个新组将在 Buildbot 下次重新启动时可供 Buildbot 使用,我们稍后会这样做。
我们还需要确保 Buildbot 知道如何与 Docker 通信。 由于 Buildbot 是用 Python 编写的,它利用 docker-py Python 包 而不是直接发出 Docker 命令。
您可以通过键入以下内容来安装 docker-py
:
sudo -H pip install docker-py
最后,我们需要开放从容器到宿主系统和外界的网络访问。 我们可以通过允许防火墙中的 docker0
接口例外来做到这一点。
通过键入以下内容允许访问来自 docker0
接口的流量:
sudo ufw allow in on docker0
Buildbot 和 Docker 现在应该能够有效地相互通信。
创建 Docker 映像以用作 Buildbot Worker
接下来,我们将创建一个 Docker 容器,用作 Buildbot worker 来运行我们的测试。 Buildbot 可以动态启动 Docker 容器以用作工作器,但首先需要在构建容器时包含一些 Buildbot 工作器组件。
幸运的是,Buildbot 项目提供了一个基本的 Buildbot 工作映像 ,它已经配置了所有 Buildbot 特定的要求。 我们只需要使用这个镜像作为基础并安装我们项目需要的附加依赖项。
在我们的例子中,我们将使用 的 示例应用程序是一个 Node.js 应用程序,因此我们需要确保 Node.js 在图像上可用。
要定义我们的图像,请在您的主目录中创建并打开一个名为 Dockerfile
的文件:
nano ~/Dockerfile
在这个文件中,我们使用 FROM buildbot/buildbot-worker:master
将我们的镜像基于 Buildbot worker 镜像。 之后,我们可以切换到root
用户安装Node.js,然后切换回buildbot
用户运行实际命令:
~/Dockerfile
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 master 来使用它。
因为我们正在定义一个全新的构建过程,并且因为到目前为止我们对主配置的自定义已经最小化,所以我们将从头开始我们的配置。 为避免丢失当前信息,我们将原始文件移动到备份文件中:
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 模块,它以一定的复杂性为代价提供了极大的灵活性。
我们将从一些基本配置开始。 将以下行粘贴到您的文件中:
/home/buildbot/master/master.cfg
# -*- 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 桥接网络docker0
进行连接。
配置 Docker 工作器
接下来,我们需要定义我们的 Docker 工作者。 Buildbot 将根据需要使用 Docker 来配置工作人员。 为此,它需要知道如何连接到 Docker 以及使用哪个镜像。
将以下内容粘贴到文件底部:
/home/buildbot/master/master.cfg
. . . # 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.DockerLatentWorker
实例并将其附加到 worker
列表中。 我们将这个 worker 命名为 npm-docker-worker
以便我们稍后可以在配置中引用它。 然后我们将 docker_host
设置为 Docker 的套接字位置,并提供我们创建的 Docker 映像的名称(在我们的例子中为 npm-worker
)。 我们将 masterFQDN
设置为 Buildbot 主服务器的域名,以确保无论服务器的内部主机名设置如何,容器都可以访问主服务器。
配置调度程序
接下来,我们将定义一个调度程序。 Buildbot 使用调度程序来决定何时以及如何根据从更改源或更改挂钩接收到的更改运行构建(我们稍后将配置更改挂钩)。
将以下配置粘贴到文件底部:
/home/buildbot/master/master.cfg
. . . # 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
实例然后添加应该执行的顺序步骤来做到这一点。
将以下内容粘贴到文件底部:
/home/buildbot/master/master.cfg
. . . # 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”,以便在每次拉取新代码时彻底清理我们的存储库。
我们添加的第二步和第三步是 steps.ShellCommand
对象,它们定义了在构建期间在存储库中运行的 shell 命令。 在我们的例子中,我们需要运行 npm install
来收集项目的依赖项。 之后,我们需要运行 npm test
来运行我们的测试套件。 在大多数情况下,建议将命令定义为列表 (["npm", "install"]
),以防止 shell 对命令中的元素应用不必要的扩展。
配置生成器
一旦我们有了一个添加了步骤的构建工厂,我们就可以设置一个构建器。 构建器将我们已经定义的许多元素联系在一起,以确定如何执行构建。
将以下配置粘贴到文件底部:
/home/buildbot/master/master.cfg
. . . # 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 主配置复制的身份验证信息替换为以下身份验证块:
/home/buildbot/master/master.cfg
. . . # 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 change hook,它将监听来自 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
如果服务能够成功重新启动,它将被标记为活动。
在示例存储库中创建 GitHub Webhook
现在 Buildbot 配置了一个 web 端点来接受 GitHub webhook 帖子,我们可以为我们的 fork 配置一个 webhook。
在您的 Web 浏览器中,导航到示例项目存储库的分支:
https://github.com/your_github_user/hello_hapi
单击设置选项卡查看项目设置。 在设置页面的左侧菜单中,点击Webhooks(在此过程中GitHub可能会提示您重新输入密码以确认您的身份):
单击右侧的 Add webhook 按钮以添加新的 webhook。
接下来的页面将包含一个用于定义 Webhook 的表单。 在 Payload URL 字段中,添加项目的 GitHub 更改挂钩端点的 URL。 这是通过指定 https://
协议,然后是您的 Buildbot 主机的域名,然后是 /change_hook/github
来构建的。
将内容类型设置为 application/x-www-form-urlencoded
。 在 Secret 字段中,输入您在 Buildbot 主配置文件中选择的密码短语。 您可以选择“Just the push event”触发器并勾选“Active”复选框:
完成后,单击 添加 webhook 按钮。
您将返回到项目的 webhook 索引,其中将显示您的新 webhook。 如果您刷新几次,您的 webhook 旁边应该会显示一个绿色复选标记图标,指示消息已成功传输:
如果您看到的是红色 X,请再次单击 webhook,然后向下滚动到 Recent Deliveries 部分。 如果您单击失败的交付,可以获得有关问题的更多信息。
测试 Webhook
现在我们的 webhook 已经到位,我们可以测试以确保当我们对存储库进行更改时,Buildbot 会收到警报,触发 Docker 中的构建,并且能够成功执行测试套件。
在您的 GitHub 分支的主页上,单击绿色“克隆或下载”按钮左侧的 创建新文件 按钮:
在接下来的屏幕上,创建一个 dummy_file
并填写一些文本:
完成后点击页面底部的提交新文件按钮。
接下来,如果您尚未通过身份验证,请访问您的 Buildbot Web 界面并登录。
根据您将 dummy_file
提交到存储库以来的时间长短,您可能会看到正在进行的构建,如下所示:
如果构建已经完成,它将位于“最近的构建”部分:
我们定义的构建器的名称“npm”用于标记构建。 在示例中,我们还可以从之前的 Master 配置中看到示例构建器的旧运行。
无论进度如何,单击构建器名称和构建号链接以访问构建详细信息页面。 此视图包含有关已执行构建的信息。 我们添加到构建工厂的每个步骤都将显示在其自己的部分中:
如果单击某个步骤,将显示命令的输出。 如果出现问题,这可以帮助调试:
在上面的输出中,我们可以验证 Buildbot 在我们的测试套件中成功运行了三个测试。
如果构建未成功完成,您可能希望检查的其他一些区域是构建详细信息页面上的其他选项卡以及 /home/buildbot/master/twistd.log
文件。
调整 Buildbot 服务
在我们完成之前,我们应该对我们的 Buildbot 服务进行一些调整。
目前,我们为不再使用的工作人员定义了 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.
上面的输出表明worker不会在下次启动时启动。 要验证该服务是否不再运行,请键入:
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 守护程序之间建立软依赖关系。 由于没有 Docker,Buildbot 主服务将无法提供新的工作人员,我们应该定义这个要求。
打开/etc/systemd/system
目录下的buildbot-master.service
文件,调整服务文件:
sudo nano /etc/systemd/system/buildbot-master.service
在 [Unit]
部分中,将 docker.service
添加到 After
指令的 network.target
项之后。 添加一个附加的 Wants
指令,该指令也命名为 docker.service
。 Wants
建立软依赖,而 After
指令建立启动顺序:
/etc/systemd/system/buildbot-master.service
[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 工作人员。