大师Martin Fowler对持续集成(Continuous Integration)是这样定义的:持续集成是一种软件开发实践,即团队开发成员经常集成他们的工作,通常每个成员每天至少集成一次,也就意味着每天可能会发生多次集成。每次集成都通过自动化的构建(包括编译,发布,自动化测试)来验证,从而尽快地发现集成错误。许多团队发现这个过程可以大大减少集成的问题,让团队能够更快的开发内聚的软件。 图片来源于:百度百科
在BuildBot中,可以通过三种方式触发构建:
BuildBot有如下优点:
使用BuildBot的公司:
BuildBot整体架构: BuildBot是由一个 buildmaster 和一个或者多个 worker ,通过星型拓扑结构连接而成的。
Worker通常运行在各种不同的平台上。它们通过TCP协议连接到BuildMaster上。这些TCP连接都是由Worker发起的。BuildMaster下发的Command以及Worker返回的Result都通过这些TCP连接进行传输。BuildMaster是集群的管理者,因此所有Command都是由BuildMaster下发给Worker的。
BuildMaster只提供用于执行构建的指令,并不会提供源代码。因此,Worker需要去代码仓库获取源代码。
BuildMaster架构:
(BuildMaster的各个组件均在master.cfg中配置)
ChangeSource:
当监测到VCS上面发生变化时,ChangeSource会创建一个Change对象,提交给各个Scheduler。
ChangeSource既可以通过监听钩子脚本产生的消息,也可以通过定期轮询的方式,监测VCS上的变化
Scheduler:
决定何时执行构建。Scheduler会收集Change,构造BuildRequest,当Worker可用时,将BuildRequest交付给Builder
Builder:
用于精确地控制如何执行构建(其中包含一系列的BuildStep)
Status plugin:
通过HTTP、mail、IRC之类的协议,交付与构建结果有关的信息
(本文假定读者已经安装了Python3,本文在Python 3.7.2下测试通过)
pip install 'buildbot[bundle]'
pip install buildbot-worker
x
mkdir -p ~/tmp
buildbot create-master ~/tmp/master
mv ~/tmp/master/master.cfg.sample ~/tmp/master/master.cfg
buildbot upgrade-master ~/tmp/master
buildbot start ~/tmp/master
# 日志在:~/tmp/master/twistd.log
xxxxxxxxxx
mkdir -p ~/tmp
buildbot-worker create-worker ~/tmp/worker localhost example-worker pass
buildbot-worker start ~/tmp/worker
# 日志在:~/tmp/worker/twistd.log
其中:
localhost:运行buildmaster的服务器的地址。本例中是localhost
example-worker/pass:在buildmaster的配置文件中,有一个 workers列表 ,它是buildmaster所能识别的worker的集合。该列表中的元素是Worker对象,每个Worker对象都有唯一的名字 和 密码。在worker上配置的worker名称和密码,必须与buildmaster中配置的相同。比如:
xxxxxxxxxx
# master.cfg
####### WORKERS
# The 'workers' list defines the set of recognized workers. Each element is
# a Worker object, specifying a unique worker name and password. The same
# worker name and password must be configured on the worker.
c['workers'] = [worker.Worker("example-worker", "pass")]
接下来,访问: http://localhost:8010 ,可以看到类似下面的web页面: 点击左侧的“Builds”,打开子菜单。然后通过“Builders”,可以看到刚刚启动的worker,已经连接到master上了。
下面,开始详细地介绍buildmaster的各个组件。
Builder负责执行某个或一系列行为,这些行为可以是与构建软件相关的,也可以是任意命令。
需要使用一个worker列表来配置Builder,这些worker用于执行任务。Builder需要的基础信息还包括:它要做的事情的列表(这些事情会在被选择的worker上执行)。在Buildbot中,这个事情列表被表示为一个BuildFactory对象,它本质上是一个步骤序列,每个步骤定义了一个特定的操作或命令。
下面看一个例子:
xxxxxxxxxx
factory = util.BuildFactory()
# 检出源代码
factory.addStep(steps.Git(repourl='https://github.com/tim-chow/buildbot-test.git', mode='incremental'))
# 执行测试
factory.addStep(steps.ShellCommand(command=["python", "setup.py", "test"]))
c['builders'] = []
c['builders'].append(
util.BuilderConfig(name="runtests",
workernames=["worker-1"],
factory=factory))
在这个例子中,创建了一个叫 runtests
的Builder,它能够在 worker-1
上运行。
c['builders']
(该值是一个BuilderConfig对象的列表)。 Scheduler在它等待的事件发生时,基于事件信息,决定是否以及何时执行构建。可以有多个Scheduler。
Scheduler主要包含两部分信息:
下面看一个例子:
xxxxxxxxxx
# Configure the Schedulers, which decide how to react to incoming changes. In this
# case, just kick off a 'runtests' build
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"]))
第一个Scheduler会接收代码仓库上的变化,但是 只关注 master分支上的变化。换句话说,它只对它感兴趣的变化作出反应。当检测到这样的变化时,它会运行 runtests
。 treeStableTimer
是为了避免:在频繁提交时,产生大量的构建请求。
使用第二个Scheduler的时候,可以通过Web UI强制构建 runtests
。
与Builder类似,所有Scheduler都必须被添加到 c["schedulers"]
列表中。
ChangeSource的任务是监测代码库上的变化,并把它们提交给Scheduler。
ChangeSource可以通过多种方式,监测代码库上的变化。比如,周期性地轮询代码库;或者通过配置(比如,通过被commit触发的钩子脚本),使VCS把变更推送给ChangeSource。
下面看一个例子:
xxxxxxxxxx
c['change_source'] = []
c['change_source'].append(changes.GitPoller(
'https://github.com/tim-chow/buildbot-test.git',
workdir='integrate_buildbot_test', branch='master',
pollInterval=120))
在这个例子中,创建了一个GitPoller类型的ChangeSource,它每隔120秒轮询一次代码库,获取master分支上的变化。
与Builder类似,所有ChangeSource都必须被添加到 c["change_source"]
列表中。
BuildMaster可以通过多种方式,将构建状态发送给用户。每个交付方法是一个Reporter Target对象。
下面看一个例子:
xxxxxxxxxx
c['services'] = []
m = reporters.MailNotifier(fromaddr="buildbot@localhost",
extraRecipients=["744475502@qq.com"],
sendToInterestedUsers=False,
builders=["runtests"])
c['services'].append(m)
在这个例子中,BuildMaster会将 runtests
的构建状态,以邮件的形式发送给 744475502@qq.com
。
如果想要获取,更多关于Reporter的细节,请查看: http://docs.buildbot.net/current/manual/configuration/reporters.html 。
与Builder类似,所有Reporter Target都必须被添加到 c["services"]
列表中。
xxxxxxxxxx
c['protocols'] = {'pb': {'port': 9989}}
protocols
中包含 与 master和worker之间通信所使用的协议 有关的信息。
xxxxxxxxxx
c['title'] = "My Test BuildBot"
c['titleURL'] = "http://timd.cn/python-buildbot/"
title
字符串会出现在home页面的顶部(链接到 titleURL
)。
xxxxxxxxxx
c['buildbotURL'] = "http://192.168.10.102:8013/"
buildbotURL
用于指出web服务的地址。它应该使用 www
中设置的端口。
xxxxxxxxxx
c['www'] = dict(port=8010,
plugins=dict(waterfall_view={}, console_view={}, grid_view={}))
激活web UI的最小化配置。其中:
port
:用于指定接收请求的TCP端口 plugins
:用于指定要加载的UI插件以及它们的配置。这些插件是独立安装的 xxxxxxxxxx
c['db'] = {
# This specifies what database buildbot uses to store its state. You can leave
# this at its default for all but the largest installations.
'db_url' : "sqlite:///state.sqlite",
}
db_url
用于指定 用来保存BuildBot的状态的数据库 的url。
默认情况下,访问Web UI是无需认证的。为了安全起见,应该配置认证插件。每个认证插件是一个类。
比如:
xxxxxxxxxx
c['www'] = {
# ...
'auth': util.UserPasswordAuth({"timchow": "password"}),
}
如果想要了解,BuildBot支持哪些认证插件,以及如何自定义认证插件,请移步: http://docs.buildbot.net/current/manual/configuration/www.html#authentication-plugins 。
接下来学习 授权框架 。
授权框架只对HTTP生效。它根据规则,控制用户对Rest API的访问。
授权框架的执行流程如下图所示:
授权框架是基于角色的:
角色:授予用户的一个标签
Endpoint Matchers:负责创建用于匹配Rest端点的规则。对于某个Rest端点而言,如果某条规则匹配它,那么在用户拥有规则中指定的角色的情况下,才能访问它
在下面的例子中,对于强制构建这个Rest端点而言,第一条规则匹配它,因此,在用户拥有admins角色的情况下,才能访问它
xxxxxxxxxx
allowRules=[
util.AnyControlEndpointMatcher(role="admins"),
]
默认情况下,第一个匹配Rest端点的规则,会阻止接下来的匹配。如果想要继续检查其它规则,那么需要将Matcher的 defaultDeny 参数设置为False
Role Matchers:负责创建用于匹配授权用户的规则。对于某个用户而言,如果某条规则匹配它,那么它将被授予规则中指定的角色
在下面的例子中,对于用户root而言,第一条规则匹配它,因此,它被授予admins角色;对于用户Charlie而言,没有任何规则匹配它,因此,它不会被授予任何角色
xxxxxxxxxx
roleMatchers=[
RolesFromUsername(roles=["admins"], usernames=["root"]),
RolesFromUsername(roles=["developers", "integrators"], usernames=["Alice", "Bob"])
]
综上,如果用户A想要访问端点B,那么首先用Role Matchers中的规则匹配用户A,最终得到用户A拥有的所有角色;然后用Endpoint Matchers中的规则匹配端点B,如果某条规则匹配端点B,并且用户A拥有该规则所指定的角色,则用户A可以访问端点B…
配置授权规则的方式,如下所示:
xxxxxxxxxx
authz = util.Authz(
allowRules=[
util.AnyControlEndpointMatcher(role="admins"),
],
roleMatchers=[
util.RolesFromEmails(admins=["my@email.com"])
]
c['www']['authz'] = authz