使用BuildBot的持续集成实施示例

BuildBot试用配置
(图片由ComputerizerPixabay上发布)

你好

我的名字叫Evgeny Cherkin ,我是Polymetal矿业公司开发团队的程序员。

在开始任何重大项目时,您都会开始思考:“哪种软件最好用于维护?”。 下一个版本发布之前的IT项目经历了一系列阶段。 这些步骤的链是自动化的,那很好。 发布IT项目新版本的自动化过程称为“ 持续集成”事实证明, BuildBot对我们来说是一个很好的帮助,它可以执行此过程。

在本文中,我决定概述BuildBot的功能。 这个软件能做什么? 如何接近他,如何与他建立正常的有效工作关系? 您还可以通过在计算机上创建一个用于组装和测试项目的工作服务,将我们的经验运用到自己身上。


1.为什么是BuildBot?



早在habr-e时,我遇到了有关使用BuildBot实现持续集成的 文章 。 例如,在我看来, 是最有用的。 还有另一个例子- 更简单 。 这些文章可以使用手册中的示例进行调味,之后是英语。 轿跑车是一个很好的起点。 阅读完这些文章之后,您可能会立即想要在BuildBot上做一些事情。

别说了 有人在项目中使用过吗? 事实是, 许多人已将其应用于任务中。 您可以在Google代码档案中找到使用BuildBot的 示例

那么人们使用buildbot的逻辑是什么? 毕竟,还有其他工具: CruiseControlJenkins 。 我会这样回答。 对于大多数任务, 詹金斯确实足够了。 BuildBot则更具适应性,并且在那里的任务像在Jenkins中一样容易解决。 选择你 但是,由于我们正在寻找用于开发目标项目的工具,所以为什么不选择一个允许我们从简单的步骤开始,获得具有交互性和独特界面的构建系统的工具。

对于那些目标项目是用python编写的人来说,就会出现一个问题:“为什么不选择一个在项目所用语言方面具有清晰界面的集成系统?”。 然后是时候介绍BuildBot的好处了。
因此,我们的“乐器四重奏”。 我自己定义了BuildBot的四个功能:
  1. 这是GPL下的开源框架
  2. 这是使用python作为配置工具并描述了所需的操作。
  3. 这是从发生装配的机器接收响应的机会。
  4. 这些最终是主机的最低要求。 部署需要使用python和twisted,并且不需要虚拟机或java机器。

2. BuildMaster领导的概念



BuildBot BuildMaster

任务分发架构的中心是BuildMaster 。 它是一项服务:

  • 跟踪项目源代码树中的更改
  • 发送工人服务应执行以构建项目并对其进行测试命令
  • 通知用户所采取措施的结果

BuildMaster是通过master.cfg文件配置的。 该文件位于BuildMaster的根目录。 稍后,我将说明如何创建此根。 master.cfg文件本身包含python,这是一个使用BuildBot调用的脚本。

下一个最重要的BuildBot称为Worker 。 此服务可以在具有不同操作系统的其他主机上运行,​​也可以在BuildMaster所在的位置上运行 。 它也可以存在于具有其自己的程序包和变量的经过特殊准备的虚拟环境中。 可以使用vertualenv,venv等python实用工具准备这些虚拟环境。

BuildMaster将命令广播给每个Worker ,然后由他们执行它们。 也就是说,事实证明,构建和测试项目的过程可以转到Windows下的Worker以及Linux下的另一个Worker。

项目源代码的检出发生在每个Worker上

3.安装



所以走吧 我将使用Ubuntu 18.04作为主机。 在其上,我将放置一个BuildMaster -a和一个Worker -a。 但首先您需要安装python3.7:

sudo apt-get update sudo apt-get install python3.7 

对于需要python3.7.2而不是3.7.1的用户,可以执行以下操作:

 sudo apt-get update sudo apt-get software-properties-common sudo add-apt-repository ppa:deadsnakes/ppa sudo apt-get install python3.7 sudo ln -fs /usr/bin/python3.7 /usr/bin/python3 pip3 install --upgrade pip 

下一步是安装TwitedBuildBot以及启用其他BuildBot -a功能的软件包。

 /*   sudo        /usr/local/lib/python3.7/dist-packages*/ #     Worker- sudo pip install twisted # twisted sudo pip install buildbot #BuildMaster #  pip install pysqlite3 #  sqllite    pip install jinja2 #framework  django,  web     pip install autobahn #Web c   BuildMaster->Worker pip install sqlalchemy sqlalchemy-migrate #     # Web  BuildBot-a pip install buildbot-www buildbot-grid-view buildbot-console-view buildbot-waterfall-view pip install python-dateutil #   web #         pip install buildbot-worker #Worker #  sudo pip install virtualenv #  

4.第一步



是时候创建一个BuildMaster了 。 我们将把它放在文件夹/ home / habr / master中
 mkdir master buildbot create-master master #     

下一步。 创建一个工人 。 我们将把它放在文件夹/ home / habr / worker中
 mkdir worker buildbot-worker create-worker --umask=0o22 --keepalive=60 worker localhost:4000 yourWorkerName password 

启动Worker时 ,默认情况下,它将在/ home / habr / worker中创建一个文件夹,其中包含项目的名称,该名称在master.cfg中指定。 然后在带有项目名称的文件夹中,他将创建构建目录,然后在其中进行签出Worker的工作目录将是目录/ home / habr / yourProject / build

金钥匙
现在,对于我之前写的内容:由于该脚本没有运行权限,因此将不会执行Master将要求Worker在该目录中远程执行的脚本。 要解决这种情况,您需要一把钥匙 --umask = 0o22 ,这禁止写入该目录,但保留启动权限。 我们只需要这个。

BuildMasterWorker之间建立了联系。 碰巧它中断了, Worker等待BuildMaster的响应一段时间。 如果未收到响应,则重新启动连接。 密钥--keepalive = 60正是您需要指示连接重新启动之后的时间。

5.配置。 逐步食谱



BuildMaster配置在机器端完成,我们在其中运行了create-master命令。 在我们的例子中,这是目录/ home / habr / mastermaster.cfg配置文件尚不存在,但是命令本身已经创建了master.cmg.sample文件。 您需要将其在master.cfg.sample中重命名为master.cfg
 mv master.cfg.sample master.cfg 

让我们打开这个master.cfg 。 我们将分析它的组成。 之后,我们将尝试制作自己的配置文件。

master.cfg
 c['change_source'] = [] c['change_source'].append(changes.GitPoller( 'git://github.com/buildbot/hello-world.git', workdir='gitpoller-workdir', branch='master', pollInterval=300)) c['schedulers'] = [] c['schedulers'].append(schedulers.SingleBranchScheduler( name="all", change_filter=util.ChangeFilter(branch='master'), treeStableTimer=None, builderNames=["runtests"])) c['schedulers'].append(schedulers.ForceScheduler( name="force", builderNames=["runtests"])) factory = util.BuildFactory() factory.addStep(steps.Git(repourl='git://github.com/buildbot/hello-world.git', mode='incremental')) factory.addStep(steps.ShellCommand(command=["trial", "hello"], env={"PYTHONPATH": "."})) c['builders'] = [] c['builders'].append( util.BuilderConfig(name="runtests", workernames=["example-worker"], factory=factory)) c['services'] = [] c['title'] = "Hello World CI" c['titleURL'] = "https://buildbot.imtqy.com/hello-world/" c['buildbotURL'] = "http://localhost:8010/" c['www'] = dict(port=8010, plugins=dict(waterfall_view={}, console_view={}, grid_view={})) c['db'] = { 'db_url' : "sqlite:///state.sqlite", } 


5.1 BuildmasterConfig


 c = BuildmasterConfig = {} 

BuildmasterConfig-配置文件的基本字典。 它必须包含在配置文件中。 为了易于使用,在配置代码中输入了别名“ c”c [“ keyFromDist”]中的名是用于与BuildMaster进行交互的固定元素。 在每个键下,将相应的对象替换为一个值。

5.2工人


 c['workers'] = [worker.Worker("example-worker", "pass")] 

这次我们指出了BuildMaster 工人列表。 我们通过指定you-worker-namepassword上面创建了Worker 。 现在必须指定它们,而不是example-workerpass

5.3 change_source


 c['change_source'] = [] c['change_source'].append(changes.GitPoller( 'git://github.com/buildbot/hello-world.git', workdir='gitpoller-workdir', branch='master', pollInterval=300)) 

使用c词典的change_source键,我们可以访问列表,该列表将您要放置的项目的源代码轮询存储库。 该示例使用Git存储库,该存储库会定期轮询。

第一个参数是存储库的路径。

workdir是指向Worker一侧的文件夹的路径,相对于/ home / habr / worker / yourProject / build路径 git将存储版本库的本地版本。

分支包含存储库中应监视的特定分支。

pollInterval包含BuildMaster将轮询存储库以查找更改的秒数。

有几种方法可以跟踪项目存储库中的更改。

最简单的方法是Polling ,这意味着BuildMaster会定期使用存储库轮询服务器。 如果提交反映了存储库中的更改,则BuildMaster稍有延迟地创建一个内部Change对象,并将其发送到Scheduler事件处理程序,该事件处理程序将开始在Worker上构建和测试项目的步骤。 在这些步骤中,将指示存储库的更新 。 在Worker上将创建存储库的本地副本。 该过程的详细信息将在下面的两个部分5.45.5 )中公开。

跟踪存储库中的更改的一种更为优雅的方法是直接从其所在的服务器向BuildMaster发送有关更改项目源代码的消息。 在这种情况下,一旦开发人员进行了提交 ,具有项目存储库的服务器就会向BuildMaster发送一条消息。 然后,将通过创建PBChangeSource对象来拦截该对象。 此外,该对象将被传送到Scheduler ,后者将激活项目的组装和测试步骤。 该方法的重要部分是使用存储库中的服务器挂钩脚本。 在负责处理提交期间操作的挂钩脚本中,您需要调用sendchange实用程序并指定BuildMaster的网络地址。 您必须指定将侦听PBChangeSource的网络端口。 顺便说一句PBChangeSource是BuildMaster的一部分。 此方法在项目存储库所在的服务器上需要admin -a特权。 首先,您需要制作一个备份存储库。

5.4排放者


 c['schedulers'] = [] c['schedulers'].append(schedulers.SingleBranchScheduler( name="all", change_filter=util.ChangeFilter(branch='master'), treeStableTimer=None, builderNames=["runtests"])) c['schedulers'].append(schedulers.ForceScheduler( name="force", builderNames=["runtests"])) 

调度程序是一个元素,可作为触发器运行项目的整个组装和测试链。
Buildbot卸载程序

change_source记录的那些更改在BuildBot -a操作期间被转换为Change对象,现在每个基于它们的Sheduler都生成了启动项目构建过程的请求。 但是,它也确定何时将这些请求发送到队列。 对象 Builder保留请求队列,并在单独的Worker -e上监视当前程序集的状态。 BuildMaster -e和Worker -e上存在Builder 。 他将特定的构建BuildMaster发送到Worker ,必须遵循一系列步骤。
我们看到,在此类调度程序的当前示例中,创建了2件。 而且,每个都有自己的类型。

SingleBranchScheduler是最受欢迎的日程表类之一。 它监视一个分支,并由其中的记录更改触发。 当他看到更改时,可以推迟发送构造请求(推迟特殊参数treeStableTimer中指定的时间段)。 该名称指定将在BuildBot -web界面中显示的调度的名称。 在ChangeFilter中设置一个过滤器后,分支中的更改将通过该过滤器提示时间表以发送构建请求。 在builderNames中指定了名称builder -a,稍后再指定。 在我们的案例中,该名称将与项目的名称相同: yourProject

ForceScheduler是一件非常简单的事情。 这种类型的计划是通过在BuildBot -web界面上单击鼠标触发的。 参数与SingleBranchScheduler具有相同的本质。

PS 3号 突然派上用场
定期是按特定的固定频率触发的计划。 看起来像这样的电话
 from buildbot.plugins import schedulers nightly = schedulers.Periodic(name="daily", builderNames=["full-solaris"], periodicBuildTimer=24*60*60) c['schedulers'] = [nightly] 

5.5建造工厂


 factory = util.BuildFactory() factory.addStep(steps.Git(repourl='git://github.com/buildbot/hello-world.git', mode='incremental')) factory.addStep(steps.ShellCommand(command=["trial", "hello"], env={"PYTHONPATH": "."})) 

PeriodicalBuildTimer以秒为单位设置此周期性的时间。

BuildFactory创建一个具体的构建 ,然后构建器将其发送给WorkerBuildFactory指示Worker应该遵循的步骤。 通过调用addStep方法添加步骤


在此示例中添加的第一步是git clean -d -f -f –x ,然后是git checkout 。 这些动作嵌入在方法参数中,该参数未明确指出,但暗含了默认值freshmode ='incremental'参数指示完成存储的目录中的文件(尽管从存储库中丢失)保持不变。

添加的第二个步骤是使用环境变量PATHONPATH = ...从目录/ home / habr / worker / yourProject / build中使用带有变量hello的hello参数调用试验脚本,因此您可以编写自己的脚本并在Worker端执行-a通过util.ShellCommand步骤。 这些脚本可以直接放入存储库中。 然后在开票期间,他们将转到/ home / habr / worker / yourProject / build 。 但是,这里有两个“ buts”:
  1. 必须使用--umask开关创建Worker ,以便在签出 -a之后不会阻止执行权限。
  2. git push这些脚本时,必须指定exacutable属性,以便使用chechout -e Git不会失去执行脚本的权利。


5.6建筑商


 c['builders'] = [] c['builders'].append(util.BuilderConfig(name="runtests", workernames=["example-worker"], factory=factory)) 

关于此处描述什么Builder 。 现在,我将详细讨论如何创建它。 BuilderConfig是构造函数builder 。 您可以在c ['builders']中指定多个此类构造函数,因为这是一系列构造类型对象的列表。 现在,我们将稍微重写BuildBot中的示例,使其更接近我们的任务。
 c['builders'] = [] c['builders'].append(util.BuilderConfig(name="yourProject", workernames=["yourWorkerName"], factory=factory)) 

现在,我将介绍参数BuilderConfig

name设置名称构建器 -a。 在这里,我们将其称为yourProject 。 这意味着将在Worker上创建此路径/ home / habr / worker / yourProject / buildSheduler用该名称搜索构建器

workernames包含一个Workers列表。 每个都必须添加到c ['workers']中

factory是与构建器关联的特定构建 。 它将向构建器发送一个构建对象,以完成构成该构建 -a的所有步骤。

6.自己的配置示例



这是我建议通过BuildBot实现的示例项目架构


我们将使用svn作为版本控制系统。 存储库本身将位于特定的云中。 这是此云的地址svn.host/svn/yourProject/trunk 。 在svn下的云中,有一个用户名: user ,passwd: 密码帐户。 作为build -a步骤的脚本也将位于svn分支的单独buildbot / worker_linux文件夹中 。 这些脚本位于存储库中,并保存了可执行文件属性。

BuildMasterWorker在同一project.host主机上工作。 BuildMaster将其文件存储在/ home / habr / master文件夹中。 Worker存储以下路径/ home / habr / workerBuildMasterWorker进程之间的通信是使用BuildBot -a协议(即“ pb”协议)通过4000端口进行的。

目标项目完全用python编写。 任务是跟踪其更改,创建可执行文件,生成文档,进行测试。 万一发生故障,所有开发人员都需要向邮件发送一条消息,说明操作失败。

我们将把BuildBot Web映射连接到project.host的端口80。 修补程序是可选的。 扭曲的库已经有一个Web服务器, BuildBot使用它。

我们将使用sqlite存储BuildBot的内部信息。

对于邮件发送,您需要主机smtp.your.domain-它允许从projectHost@your.domain发送电子邮件而无需身份验证。 同样在主机“ smtp ”上,该协议在1025年被侦听。

此过程涉及两个人: adminuser 。 admin管理BuildBot 。 用户是提交提交的

可执行文件是通过pyinstaller生成的。 文档是通过doxygen生成的。

对于这种架构,我编写了这个master.cfg

master.cfg
 import os, re from buildbot.plugins import steps, util, schedulers, worker, changes, reporters c= BuildmasterConfig ={} c['workers'] = [ worker.Worker('yourWorkerName', 'password') ] c['protocols'] = {'pb': {'port': 4000}} svn_poller = changes.SVNPoller(repourl="https://svn.host/svn/yourProject/trunk", svnuser="user", svnpasswd="password", pollinterval=60, split_file=util.svn.split_file_alwaystrunk ) c['change_source'] = svn_poller hourlyscheduler = schedulers.SingleBranchScheduler( name="your-project-schedulers", change_filter=util.ChangeFilter(branch=None), builderNames=["yourProject"], properties = {'owner': 'admin'} ) c['schedulers'] = [hourlyscheduler] checkout = steps.SVN(repourl='https://svn.host/svn/yourProject/trunk', mode='full', method='fresh', username="user", password="password", haltOnFailure=True) projectHost_build = util.BuildFactory() cleanProject = steps.ShellCommand(name="Clean", command=["buildbot/worker_linux/pyinstaller_project", "clean"] ) buildProject = steps.ShellCommand(name="Build", command=["buildbot/worker_linux/pyinstaller_project", "build"] ) doxyProject = steps.ShellCommand(name="Update Docs", command=["buildbot/worker_linux/gendoc", []] ) testProject = steps.ShellCommand(name="Tests", command=["python","tests/utest.py"], env={'PYTHONPATH': '.'} ) projectHost_build.addStep(checkout) projectHost_build.addStep(cleanProject) projectHost_build.addStep(buildProject) projectHost_build.addStep(doxyProject) projectHost_build.addStep(testProject) c['builders'] = [ util.BuilderConfig(name="yourProject", workername='yourWorkerName', factory=projectHost_build) ] template_html=u'''\ <h4>  : {{ summary }}</h4> <p>   : {{ workername }}</p> <p>: {{ projects }}</p> <p>         : {{ buildbot_url }}</p> <p>         : {{ build_url }}</p> <p> WinSCP     c ip:xxx.xx.xxx.xx.   habr/password,   executable    ~/worker/yourProject/build/dist.</p> <p><b>    Buildbot</b></p> ''' sendMessageToAll = reporters.MailNotifier(fromaddr="projectHost@your.domain", sendToInterestedUsers=True, lookup="your.domain", relayhost="smtp.your.domain", smtpPort=1025, mode="warnings", extraRecipients=['user@your.domain'], messageFormatter=reporters.MessageFormatter( template=template_html, template_type='html', wantProperties=True, wantSteps=True) ) c['services'] = [sendMessageToAll] c['title'] = "The process of bulding" c['titleURL'] = "http://project.host:80/" c['buildbotURL'] = "http://project.host" c['www'] = dict(port=80, plugins=dict(waterfall_view={}, console_view={}, grid_view={})) c['db'] = { 'db_url' : "sqlite:///state.sqlite" } 


首先,您需要创建 BuildMasterWorker -a。 然后将此master.cfg文件粘贴到/ home / habr / master中

下一步是启动BuildMaster服务
 sudo buildbot start /home/habr/master 

然后启动Worker -a服务
 buildbot-worker start /home/habr/worker 

做完了! 现在, Buildbot将跟踪更改并在svn中的 commit执行 ,遵循使用上述架构构建和测试项目的步骤。

下面,我将描述上述master.cfg的一些功能


6.1在通往master.cfg的途中


在编写master.cfg时,会发生很多错误,因此您需要读取日志文件。 它既存储在BuildMaster -ec绝对路径/home/habr/master/twistd.log上,又存储在Worker -a侧,绝对路径为/home/habr/worker/twistd.log 。 当您阅读并修复错误时,将需要重新启动BuildMaster -a服务。 方法如下:
 sudo buildbot stop /home/habr/master sudo buildbot upgrade-master /home/habr/master sudo buildbot start /home/habr/master 

6.2使用svn


 svn_poller = changes.SVNPoller(repourl="https://svn.host/svn/yourProject/trunk", svnuser="user", svnpasswd="password", pollinterval=60, split_file=util.svn.split_file_alwaystrunk ) c['change_source'] = svn_poller hourlyscheduler = schedulers.SingleBranchScheduler( name="your-project-schedulers", change_filter=util.ChangeFilter(branch=None), builderNames=["yourProject"], properties = {'owner': 'admin'} ) c['schedulers'] = [hourlyscheduler] checkout = steps.SVN(repourl='https://svn.host/svn/yourProject/trunk', mode='full', method='fresh', username="user", password="password", haltOnFailure=True) 

首先,看一下svn_poller 。 该接口与每分钟定期轮询存储库的接口相同。 在这种情况下, svn_poller仅访问中继分支。 神秘的参数split_file = util.svn.split_file_alwaystrunk设置规则:如何将svn文件夹的结构拆分为分支。 他为他们提供了相对的方式。 反过来, split_file_alwaystrunk通过说只有主干在存储库中来简化了过程。

Schedulers中指定ChangeFilter ,它看到None,并通过split_file_alwaystrunk根据指定的关联将主干分支与其关联。 响应主干中的更改,它将启动名为yourProject构建器

需要此处的属性 ,以便管理员作为过程的所有者从装配和测试结果中接收新闻通讯。

build -a checkout步骤能够完全删除Worker存储库的本地版本中的所有文件。 然后执行完整的svn更新 。 该模式通过参数mode = fullmethod = fresh进行配置haltOnTailure参数指示,如果svn更新 不成功 ,则应暂停整个组装和测试过程,因为进一步的步骤没有意义。

6.3给您的信:记者有权声明


记者是邮件通知服务。
 template_html=u'''\ <h4>  : {{ summary }}</h4> <p>   : {{ workername }}</p> <p>: {{ projects }}</p> <p>         : {{ buildbot_url }}</p> <p>         : {{ build_url }}</p> <p> WinSCP     c ip:xxx.xx.xxx.xx.   habr/password,   executable    ~/worker/yourProject/build/dist.</p> <p><b>    Buildbot</b></p> ''' sendMessageToAll = reporters.MailNotifier(fromaddr="projectHost@your.domain", sendToInterestedUsers=True, lookup="your.domain", relayhost="smtp.your.domain", smtpPort=1025, mode="warnings", extraRecipients=['user@your.domain'], messageFormatter=reporters.MessageFormatter( template=template_html, template_type='html', wantProperties=True, wantSteps=True) ) c['services'] = [sendMessageToAll] 

它可以通过多种方式发送消息。

MailNotifier使用邮件发送通知。

template_html设置新闻通讯的文本模板。 要创建标记,请使用html。 它是由jinja2引擎修改的(可以与django进行比较)。 BuildBot具有一组变量,在形成消息文本的过程中,将其值替换为模板。 这些变量刻在{{double花括号}}中。 因此,例如, 摘要显示已完成操作的状态,即成功或失败。 项目将输出yourProject 。 因此,使用jinja2BuildBot变量和python字符串格式化程序中的控制命令,您可以创建非常有用的消息。

MailNotifier包含以下参数。

fromaddr-每个人都将从其接收新闻通讯的地址。

sendToInterestedUsers = True向进行提交的所有者和用户发送一条消息。

查找 -后缀添加到接收时事通讯的用户名中。 因此,作为用户的管理员将通过admin@your.domain接收时事通讯。

relayhost指定打开smtp服务器的主机名, smptPort指定smtp服务器侦听的端口号。

mode =“ warning”表示只有在至少一个构建步骤以失败或警告状态结束的情况下,才应进行邮件发送。如果成功,则不需要邮寄。

extraRecipients包含一个列表,除了所有者和提交者之外,还应向他们发送邮件

messageFormatter是一个对象,用于定义消息的格式,其模板以及jinja2中可用的变量集。诸如wantProperties = TruewantSteps = True之类的参数指定了这组可用变量。

与['services'] = [sendMessageToAll]提供服务列表,其中包括我们的记者

我们做到了!恭喜你



我们创建了自己的配置,并看到了BuildBot具备的功能我认为,这足以了解是否需要使用此工具来创建您的项目。他对你感兴趣吗?它会为您派上用场吗?方便吗?然后,我有充分的理由写这篇文章。

还有一件事。我希望使用BuildBot的专业社区变得更广泛,翻译手册并提供更多示例。

谢谢大家的关注。祝你好运

Source: https://habr.com/ru/post/zh-CN460501/


All Articles