大型分布式团队的集中化持续交付

持续集成是一种软件开发实践,即团队开发成员集成他们的工作,通常每个成员每天至少集成一次,随着对自动化要求的不断提高,需要自动化构建来完成的应用也越来越多,此问题对于大型团队愈加严重,即:集成次数更多、权限管理更加复杂。以下主要分享大型团队持续集成服务器的集中化管理中所遇到的挑战和积累的经验。

如何集中化管理CI Agent?

团队结构

首先看一下现在的团队结构,可以看到绿色的部分表示现在我所在的团队—ASIA, ASIA团队属于XXX Group,XXX Group下有很多其它的团队。与此同时ASIA又分为很多其它的团队,以Regionalweb 团队为例,它是一个分布式的团队,主要分布在香港、新加坡和其它的区域。

可以看到团队结构是相对比较复杂的,而这样复杂的团队结构也导致AWS账户结构变得复杂。对于我来说,我现在所管理的AWS账户就有20多个。

我们知道CI/CD是持续交付中很重要的部分,而CI/CD又依赖于持续集成服务器(还有另外一个说法是自动化服务器)。

在我们能将应用自动化部署到各个团队的AWS环境之前,需要首先将CI Agent部署到各个团队的AWS环境中。CI Agent是持续集成服务器中核心的部分,我们的pipeline就是运行在CI Agent之中,它像是我们的助手,可以帮助我们自动化集成和部署应用。

现在的问题是:需要将CI Agent部署到这么多团队的AWS环境,如果靠手动操作,岂不是很崩溃?

如何集中化管理这么多团队AWS环境的CI Agent?我想到了一个办法 — 影分身之术。不知道大家之前有没有看过《火影忍者》,影分身之术是一种禁术,使用它可以生成无数的分身。如果我们获得影分身的技能,那么通过分身即使管理再多团队AWS环境的CI Agent也变得很Easy。

那么接下来所有的内容就是讲怎么去学习影分身之术。学习影分身之术第一是要学如何结印。

跟大家开一个玩笑,我们都是普通的人类,即便我们学会了结印也没有办法去进行影分身,看似这个方案失败了。

影分身之术虽然失败了,但不要气馁,我又想到了另一个方法 — 基础设施即代码

基础设施即代码

什么是基础设施即代码呢?简单的理解就是我们以代码的方式来生成基础设施,即将基础设施代码化。什么是基础设施?对应到云上服务,比如说AWS EC2、数据库服务、ELB等都是基础设施。

通过代码的方式来生成基础设施,这有什么好处?其中一个好处是通过强CI Agent基础设施代码化,再加上不同的配置参数,可以定制化、快速并且正确的在不同AWS环境中自动化创建CI Agent。

如何集中化创建CI Agent?

我们采用Ansible加CloudFormation来实现。其中Ansible作为参数模板来使用,而Cloudformation是AWS用来实现基础设施即代码的一种服务。

利用Ansible作为参数模板可以对不同的团队不同的环境配置不同的参数。将不同的参数运用到同样的CloudFormation模版,就可以生成特定环境的CI Agent集群。

当我们已经可以集中化创建CI Agent,通常情况下会出现另外一个需求,即需要更新CI Agent。但是我又不想一个一个去修改,那么如何集中化并且自动化升级CI Agent?

如何集中化并且自动化升级CI Agent?

通过对CI Agent的基础设施代码库添加CI/CD pipeline来实现CI Agent自动化更新。具体流程如下:

基础设施开发人员修改完CI Agent基础设施代码并Push代码到代码库后会自动触发CI/CD pipeline运行。pipeline主要分为个Step:

  • 使用新的基础设施代码创建新的CI Agent。这一个Step会在现存的CI Agent中运行,并创建新的CI Agent。
  • 新的CI Agent创建完成后,使用新的CI Agent调用脚本删除旧的CI Agent。

举个形象一点的例子,比如说有一个机器人,它可以通过模版生成收音机、单车等,但这个机器人在使用过了一段时间之后,它也需要自我升级,这时谁帮它做升级呢?不要低估了这个机器人,他不仅可以接受收音机和单车的模板,同是它也可以接受一个机器人的模板,机器人通过机器人模板创建出一个新的机器人,创建好的新机器人会把老的机器人干掉,这样就实现自我更新。

到现为止,已经可以集中化的创建Agent,并且能够自动化的更新Agent。基础设施程序员只需要修改基础设施代码然后提交代码就做够了。

两种CI Agent部署策略的比较

最初的CI Agent部署策略

现在看一下CI Agent的部署策略,这是最初CI Agent的部署策略。

到这里看似一切都很完美,但实际上是这样的吗?CI Agent的部署策略有什么问题,我们将所有的Agent都部署到DEV环境,由DEV环境的CI Agent具有权限跨AWS Account去操作Staging和Production环境,它有什么问题吗?

主要有三个问题:

  • Single Point Of Failure 单点故障,我们将所有CI Agent都部署到Dev环境中,如果Dev环境遇到什么故障,那么Staging和Production的部署都将会被阻塞。
  • Upgrade Affect All Pipeline 升级Dev环境的CI Agent时,如果升级出现问题导致服务中断也会导致Staging和Production的部署都将会被阻塞。
  • Assume Role Dev环境的CI Agent需要跨AWS Account去操作Staging和Production环境。在AWS中就是以Assume Role方式来实现的。 举个形象一点的例子,比如说ThoughtWorks公司的办公室需要装修,然后前台联系我们装修公司,我是装修队的工头,装修公司给了ThoughtWorks公司前台装修队编号,然后ThoughtWorks前台装修队编号记录下来作为验证。第二天,我带着小弟们来到ThoughtWorks前台,前台首先验证了我们的装修队编号,验证通过后,前台给了我们一个临时的门卡让我们可以自由进出,当我们装修完成后前台会收回临时门卡。

这个例子中,装修公司的装修队就像是Dev环境CI Agent,而ThoughtWorks办公室就像是其他的环境(Staging/Production)。我们可以想到,这里面存在一个安全风险,如果装修队里有人心术不正想做一些坏事的是很容易的。

那么,这些问题怎么解决呢?

现在的CI Agent部署策略

可以看到,在新的CI Agent部署策略中,每个环境都具有相应的CI Agent,这样带来的好处有哪些?首先,不会有单点故障问题,升级Dev环境的Agent引起的问题不会导致Staging和Production的部署被阻塞。其次是操作Dev的时候会使用Dev环境的Agent,操作Staging的时候会使用Staging环境的Agent,操作Production的时候会使用Production环境的Agent,不会有跨AWS Account的操作,当然也不再需要Assume Role。

那么新的CI Agent部署策略有什么问题吗?

是的,它是占用了更多的资源,意味着要花更多的钱,怎么去解决这样的问题?

如何优化CI Agent资源利用?

首先,我们能想到的是当CI Agent空闲的时候把它关掉,或者是只留下很少的CI Agent。那么什么时候CI Agent会空闲下来呢?你肯定已经想到了晚上和周末,但是这个也不一定,主要还是取决于团队的工作模式。如果团队允许成员晚上或者周末提交代码,那么还是要保留一些CI Agent。

其次,可以将pipeline step的任务进行分类,主要分为两类:与环境相关的任务与环境无关的任务

什么叫与环境相关的任务? 比如我要部署Dev环境,那么就需要具有Dev环境部署的权限,这类任务是与环境相关的任务。

什么叫与环境无关的任务?比如说Unit Test,它可以在任何一个环境中运行,因为它并不需要在特定环境中创建基础设施。

与环境相关的任务和与环境无关的任务分离有什么作用呢?分离后,可以将与环境无关的任务运行在Default agents cluster上面,这样就可以减少我们自己环境CI Agent的数量。但是你一定很疑惑Default agent cluster是什么吧?还记得一开始提到过,我们团队处在一个大的Group下面,其实在这个大的Group下面还有一个共享的CI Agent Cluster,其它所有的团队都可以使用,这样可以更大程度的使用资源,减少浪费。

如何平衡资源的花费和提供更好的服务?

资源的花费和更好的服务,看似鱼和熊掌不可兼得。提供更好的服务就需要更多的资源花费,而减少资源的花费又会导致服务质量的下降。

我们不想要的是:

  • 在CI Agent所要执行的任务不断增多的时候,我们需要无限等待,这显然很低效。
  • 在没有任务需要执行的时候,所有的CI Agent都在空闲这,而这显然会导致资源的浪费。

而我们想要的是:

  • 在CI Agent所要执行的任务不断增多的时候,CI Agent集群能够自动扩展CI Agent数量来响应执行任务。
  • 在没有任务需要执行的时候,CI Agent集群能够自动缩减CI Agent数量以避免资源的浪费。

我想到一个方法是提升它的可伸缩性,怎么提升它的可伸缩性?大家看一下这个图:

我们想实现可伸缩的CI Agent Cluster,那么首先需要弄清楚什么情况下需要增加CI Agent数量,什么情况下该把CI Agent数量减少。我们通过一个Metric Lambda从CI Agent API里去获取很多维度的信息。比如,现在正在运行的任务数和现在待运行的任务数。获取到各个维度的信息后将其存放在AWS CloudWatch之中。

Scaling Alarm会监控待运行的任务数,当待运行的任务数高于我们设定的一个阈值并持续了设定的一段时间之后,就会自动触发Scaling Up的任务,Scaling Up任务会增加新的CI Agent来响应执行任务。当待运行的任务数低于我们设定的一个阈值并持续了设定的一段时间之后,就会自动触发Scaling Down的任务,Scaling Down任务会减少CI Agent数量以避免资源的浪费。

如何知道CI Agent Cluster的运行状态?

我们有这么多的团队的CI Agent Cluster,我们怎么知道CI Agent Cluster运行的状况?你怎么去判断一个CI Agent Cluster运行状况是否良好,其实取决于自己所定义的逻辑。

如上图,Avaliablity Alarm会去监控可用CI Agent数量,当可用CI Agent数量低于我们设定的阈值并只需了设定的一段时间后,会自动触发PageDuty,PageDuty是一个第三方的Alerting服务。我们对所有的Alerting分为了两大类,高优先级Alerting低优先级Alerting。高优先级Alerting一般会通过电话进行Alerting,低优先级Alerting一般通过邮件或者短信进行Alerting。只要怎么定义那些Alerting属于高优先级那些属于低优先级,这取决于你自己。

使用BulidKite所带来的优势和劣势

上面讲的都是所有持续集成服务器所具有的通性问题,它属于架构性的问题。当然我们也有一些个性的问题。今年3月份,由于客户的需求,我们将Jenkins迁移到了BuildKite。 大家都知道Jenkins是持续提升服务器,Buildkite也一样。

什么是BuildKite?

我们知道Jenkins是Master Slave模式,其实BuildKite也是类似Master Slave模式,不同的是Jenkins的Master和Slave都由我们自己维护,而BuildKite的Master则是由BuildKite公司所维护,我们只会需要使用Agent Token将Slave注册到BuildKite Agent Server之中即可。

使用BuildKite的优点

  • BuildKite Agent只需要提供运行时环境
  • Buildkite 提供统一的用户管理日志管理Pipeline配置管理Build信息管理

使用BuildKite的所存在的问题

Security - 维护BuildKite Agent Token

BuildKite Agent是通过一个Agent Token注册到BuildKite Agent Server之中的,这个Agent Token是一个明文的字符串,如果这些Agent Token被泄露了,那么BuildKite上所有的信息都可以被别人获取。

那么该如何最大限度的保证Agent Token不被泄露呢?我们使用了如下两条策略:

  • 不同的环境使用不同的Agent Token。
  • 不同的环境使用不同的KMS来加密Agent Token。

现在我模拟一下各种情况,来检查现在的策略是否足够安全。假设,现在有人不小心将我们CI Agent Cluster基础设施代码泄露了,但是我们的Agent Token是经过KMS加密后的密文,所以即便别泄露出去也不会产生安全隐患。在上面的基础上再假设,现在我们Dev环境的一个用户的AWS账号也被泄露了,并且这个账号具备AWS KMS的权限,那么此时,我们Dev环境的Agent Token就会被解密出来。但是,Staging和Production依然不会受到影响。利用这些策略,我们可以最大限度的缩小受影响的范围。

Security - 同Group下的团队可以使用同Group下的所有CI Agent

同Group下的团队可以使用同Group下的所有CI Agent。在同一个Group里有很多的团队,每个团队都会建立自己的CI Agent Cluster,我们不希望自己的CI Agent被其他团队的人使用。然而,在默认情况下,另一个团队的小伙伴既是可以使用我们团队的CI Agent,这会带来一些安全隐患。如下图,A团队中的小女孩在默认情况下可以使用Regional-Web团队的CI Agent,如果她是一个心地善良的小女孩就没什么问题,但如果她心怀不轨就可以利用我们的CI Agent去执行一些危险的任务,通过Agent删除Agent所在count下的资源。

这个问题,我们通过给CI Agent加GIT Organisation白名单来限制其他团队使用我们的CI Agent。通过这种方式,即便另一个团队的人具有实用我们CI Agent的权限,但当CI Agent执行Pipeline时候,它发现所要运行代码库所在的GIT Organisation不在白名单里,它会抛出异常停止执行任务。这样就解决了这个安全隐患。

单点故障

BuildKite Agent的Master由BuildKite公司维护,如果Master出现为题,那么我们所有的Pipeline都将不能够使用。但是,话说回来,即便是我们自己维护Master,如果我们自己并没有很好的运维能力依然可能会出现问题,那么我宁愿相信BuildKite团队,毕竟他们是专业的团队在维护BuildKite Master。

本文根据 5月27日西安Jenkins 沙龙演讲整理而成。

原文发布于微信公众号 - DevOps时代(DevOpsTimes)

原文发表时间:2018-06-27

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏阳光之海

Chrome浏览器v70正式发布:同步方式改变 新增AV1解码器

谷歌今天发布了Chrome浏览器的最新版本 70.0.3538.67(正式版本)。今天发布的最令人期待的新功能是新的Chrome设置面板选项,允许用户控制浏览器...

28240
来自专栏SDNLAB

漫谈虚拟路由方案

前言——关于虚拟路由 SDN,抑或是OpenFlow,能否为路由市场开辟一个新的时代?以OpenvSwitch为代表的开源软件交换机,已经推动SDN界发展了一段...

52850
来自专栏魏艾斯博客www.vpsss.net

新网云虚拟主机绑定 CNAME 不当网站打不开的解决办法

36330
来自专栏CSDN技术头条

ButterCMS架构:完成数百万次调用的关键任务API

原文:ButterCMS Architecture: A Mission-Critical API Serving Millions Of Requests P...

25960
来自专栏逸鹏说道

数据库高可用实战案例

原文链接:http://www.cnblogs.com/double-K/p/5803956.html 说到高可用,看官们会想到很多方案,也许是自亲身经历过系统...

37670
来自专栏CSDN技术头条

分享11款主流的开源编程工具

导读:有了开源编程工具,在基于开源许可证的情况下您可以轻松学习、修改、提高代码的质量,本文收集了11款最主流的且有价值的开源编程工具。或许会给您带来一丝惊喜。一...

26070
来自专栏腾讯技术工程官方号的专栏

【TEGer 在全球架构师峰会】 : 腾讯企业级消息中间件 CMQ 技术解密

本文将对腾讯 TEG 基础架构部中间件团队研发的企业级消息中间件 CMQ 原理进行分享介绍。

40790
来自专栏腾讯NEXT学位

怎样让开源项目看起来“高大上”

39440
来自专栏Java编程技术

阿里之路(二)

从今年7月到现在转眼间转岗到淘宝部门已经有小半年了,最近刚刚经历人生中第一次双11实战,体验了一把系统经受高并发高流量的冲击的感觉,一个字爽,作为小白,在这小半...

11520
来自专栏java一日一条

微信、QQ这类IM App怎么做——谈谈Websocket

关于我和WebSocket的缘:我从大二在计算机网络课上听老师讲过之后,第一次使用就到了毕业之后的第一份工作。直到最近换了工作,到了一家是含有IM社交聊天功能的...

38820

扫码关注云+社区

领取腾讯云代金券