基础架构的持续集成和应用部署


在DevOps的场景下如何对基础架构进行持续集成和分层的配置管理,基于统一配置管理工具的应用部署和传统部署有什么不同?
字数:8600 |大约阅读时间 18 分钟
标签: DevOps   CI   IaC  

持续集成通常是针对应用而言的,可是基础架构的持续集成应该怎么做?基础架构的持续集成应该属于持续交付/部署的基础。贯串本文的一个问题,或者在阅读本文时,您应该不断地问自己这个问题:我们的应用部署流程是怎样的?

在回答这个问题之前,我们先来回顾一下,目前几乎所有人正在使用的手工环境和资源交付流程。在源码被编译打包了以后,安装包文件被上传保存到了内部的某个文件服务器上。Ops团队的某个组/人被分配到工单,根据工单描述的需求,它在测试或者生产环境中开始工作:

  • 用图形界面进行虚拟机模板的手工克隆工作,或者由于没有相应的权限或者自助服务,不得不给虚拟化管理员发任务单,然后等待回复。
  • 获取用户名和密码手工登录服务器,有些企业还要是等待领导的审批,才能得到密码信封和所需要的访问密码。
  • 根据工单(变更单)里的描述和自己的经验对虚拟机的操作系统进行配置,在这个过程中,Ops往往可能还需和需求方进行不止一次的沟通,确认相关参数。
  • 手工的下载应用安装包,然后分别手工上传到目标的服务器,凭经验和工单信息部署应用,然后测试部署结果,可能是看下页面有没有正常显示,或者服务起没起.
  • 手工测试和确认这些虚拟机的服务和状态,凭经验觉得OK了以后,回复工单,关闭工单。

以上的工作场景,可能是Ops人员很常规的一天,或者是几天内的工作,当然在这个过程中,他们还需要参与一些救火行动;他们在这个过程中也可能会有疑问,也可能会对此工作结果不确定;但是,日常的工作经验告诉他,差不多了,关闭任务单要紧,还有好多项目催活呢!就这样,配置并不精确的虚拟机环境就交给了下游的需求方。

以上工作过程的问题如下:

  1. 工作周期长,速度慢。实际上工作周期拖延的越久,工作结果的质量就越差,而并不是我们想想中的慢工出细活。
  2. 所有步骤都是纯手工操作,不仅费事费力,而且出错几率高,也几乎不可能无痛的回退。可能有人会说了,我们不需要那么快,我们也不是互联网公司;可是从精益思想的角度看,以上这些工作都属于对业务价值的交付贡献为零的工作;你可能是由于公司给你发着工资,才错误的感觉到,这项工作活动应该有它的价值。
  3. 上游传递来的信息可能不全面,不准确,因此Ops很有可能造成错误配置,因此会返工。
  4. 传递给下游的虚拟机很可能会在后续的部署过程中,由于应用需求的变化,而需要下游的人员对其重新配置,产生重复的劳动。

手工部署的时间和代价 = 应的数量 X 应用版本数量 X 环境数量

对以上工作系统进行优化的原则:如果某一项活动的重复频率越高,那么对它进行优化,所产生的回报也会越明显;这里还要参考限制理论,优化的顺序要正确。

我们从这个角度出发,就可以来设定基础架构持续集成和应用部署流程的改进目标了:

  • 减少总体人工工作时间和代价
  • 提高交付的速度、可靠性和频率
  • 能进行应用部署,能进行数据库Schema的更新
  • 能够实现部署流程的自服务,让任何需要部署应用的人能一键式部署任何版本

到了这里我们就必须将上述手工劳动,变为自动化的过程。因此,基础架构即代码IaC (Infrastructure as code)和相关的配置管理工具就会用到。

delivery-Pipeline

上图是一个典型的持续交付流水线模型,在此我们对它的关注点如下:

  • 代码的变更被Jenkins自动化的构建(CI是基础),打包后的安装包被存储在Artifactory里,Artifactory里面还可以存储应用包的其它相关元数据,如测试结果,能否可以用于下一步部署的标签等等。
  • Jenkins自动化的搭建所需要的环境,调用虚拟化或者公有云资源池的API,制备虚拟机资源,然后调用Chef完成对虚拟机的配置,完成应用包部署所需要的所有层次的配置。
  • 环境配置完成后,应用正常运行了,在相关的测试工具对部署后的环境做验收测试,Chef具有支持测试驱动的相关工具。

基础架构的持续集成

为了实现完整的基础架构持续集成流程,以上持续交付流水线必须具备的能力和概念包括:分层的系统管理、基础架构即代码IaC、配置管理、Chef工具等。下面详细对它们进行描述。

分层的系统管理

系统管理的层次涉及到OS相关的三个层次。下面自下而上地简单描述一下。

  1. 制备管理:涉及到虚拟化层,这一层是资源表达层,目前所有主流的虚拟化都支持标准的Rest API,包括VMWare、EC2和Nuanix等。大多数主流配置管理工具都具备用于虚拟机生命周期管理(从生成、到开机、到删除等)的API功能,能按需的获得任何数量、规模、网络和操作系统类型的部署环境。
  2. 配置管理:在任何类型的操作系统里自动化的安装和配置软件包,将所有配置参数配置好以后,持续保持这些配置点的状态。对于简单应用,来说按配置参数启动服务即任务完成。
  3. 应用编排管理:对于复杂的分布式系统,由于各个自服务之间存在着依赖关系,所有自服务之前需要互通一些配置参数才能实现,应用程序整体的正常运行,配置应用服务器的odbc数据库连接,配置web前端的ldap认证服务器等等。目前微服务所涉及的服务发现和路由,是应用编排必备的配套设施。

不同的DevOps配置管理工具也都力求能覆盖以上三个层次,但是他们所追求的方向,或者想解决的主要问题并不相同。因此各个工具之间功能上有重叠。

因此在运用这些工具的时候,不仅要追求其卓越的功能,还要能意识到,并有意的在不同层面上做取舍。

基础架构即代码

IaC这个概念最早是被Chef这类工具提出并实现,它的基本想法就是让Ops人员象开发人员一样的,工作在基础架构的代码上,而不是面对着数十个图形和文字终端界面。使用类似于开发应用程序的方式,开发和管理基础架构环境,因此基础架构能通过API访问和操控是基础,目前所有主流的虚拟化/云计算平台都具备很好的API接口;可惜的是在传统的企业环境中,这些资源池的API功能几乎没有被用到。

像开发应用代码一样的管理IT基础架构,基础架构的开发和管理也需要遵循与应用开发类似的原则,这些原则包括:

  • 一切从源代码开始:并对其进行严格的版本管理,要对基础架构变更,就需要对相应的代码进行变更和测试,最后发布这些代码。从而力求做到服务器的无人登录运维。
  • 模块化设计:不同应用底层所使用的基础架构有着大量的相似之处,模块化的设计不仅意味着标准化,也意味着更少的重复代码。我所用过的Terraform、Chef和Puppet这三种工具,都具有高度的模块化特性。
  • 抽象能力:能够使用不同的模块和参数对任何特征的应用进行建模,用IaC代码进行表达,基础架构的代码开发也就是借助这种抽象能力,将所有管理对象(配置管理项)具体化地描述为应用服务模型。编写出来的基础架构代码,不仅包含了所有对应用配置描述性的语义,而且还是能够被执行的代码,在IaC代码执行之后,你就得到了所期望的虚拟机、应用配置和应用服务。
  • 可测试性:这是一个经常忽略的能力,而在了解之后,你会发现IaC也是编程语言,就是对基础架构进行高级的编程,而且IaC代码本身和它的运行结果都是可以测试的。在执行前对其语义语法测试,在运行以后对其运行结果测试。Chef在这方面表现的尤为突出。

Chef

Terraform

配置管理

我可能是最早的一批进行ITIL配置管理实践,CMDB建设的这批人;我以前和甲方客户有着大量的关于配置管理和CMDB的对话,所经历过的项目也非常煎熬。而在DevOps场景下,感觉以前的经历也是很有意思的,只是我现在说到的CI,在没有特指的情况下,是持续集成的概念,还不是配置项了。

Process for establishing and maintaining consistency of a product’s performance, functional and physical attributes with its requirements, design and operational information throughout its life。

以上是配置管理在维基百科里的定义,它所表达的含义还是值得借鉴的;而如今很多人对DevOps的认识,还有人是建立在DevOps配置管理相关的工具上的。为了纠正这个错误观点,我们经常说:“天文学并不只是关于望远镜的。”

配置管理工具中有很多是基于主机(OS)的管理工具,包括:CFEngine、Puppet、Chef、Salt和Ansible等。它们都具有基础架构即代码的相关原则和特征。都能实现:定义服务器的目标期望状态的能力,在每一次执行周期里,它们都进行状态检查,汇报当前状态和目标状态的偏差,在必要的时候也可以自动的执行必要的状态修复操作。

Chef这种配置管理工具,使用了Ruby风格的DSL语言,使用者只需要用Chef代码表达”What“即可,而不需要明白”How“;”What“既是对目标配置状态的描述,使用者只需要将需求转换为Chef代码,然后用Chef客户端工具运行它即可。Chef的代码清晰,描述能力强大。在编码的时候遵循DSL规则,如果有必要的话也可以调用Ruby。

Chef是客户端服务器的架构,安装了Chef-client程序的节点可以注册到一个Chef管理服务器里。

Chef的开发人员(IaC编码者),在安装了用于和Chef服务器交互的名为knife的工具,称之为工作站的系统上开发基础架构代码。Chef使用大量内置的DSL资源(例如:package,service,file,directory等操作系统资源分类)对目标节点的配置进行建模,代码可以映射到内部的用来执行这些代码的各种提供者上。

所能实现的示例代码如下所示,下面是配置Linux操作系统中的Apache服务器。


package 'httpd' do
  action :install
end

service 'httpd' do
  action [ :enable, :start ]
end

下面是在Linux操作系统里配置 /a/b/c 目录


directory '/a/b/c' do
  owner 'admin'
  group 'admin'
  mode '0755'
  action :create
  recursive true
end

以下面就是 /a/b/c 目录的配置结果状态:

$ls ‐ld   /a/b/c
drwxr-­‐xr-­‐x. 5   admin   admin   4096    Feb 14 11:22    /a/b/c

Chef其它的重要术语:

  • recipe :包含了一个或者对个资源描述定义(Chef预定义了文件、用户、软件包、服务等等资源,可以扩展开发自定义资源类)
  • cookbook :包含了一个或多个recipe配方
  • data bag :包含了一个或多个配置数据点(data bag item),是JSON格式,一个cookbook食谱可以包含一个或者多个数据袋
  • run list :包含了一个或者多个cookbook食谱,可将其部署在被管理的node节点上
  • role :一组特定内容的run list运行清单构成了一个角色
  • environments:同我们现在对环境的定义,并可以一一对应起来

Chef是偏主机配置管理的非常的Iac语言,它具有很丰富的扩展能力和生态系统。它有很好的扩展能力,很强的逻辑性,能够进行深度的表达和锻造。它和Terraform和Ansible都有较大差异。

部署流程设计

将以上手工处理过程转换为自动化执行的、一键式触发或者自动触发的流程需要关注很多个要点。

使用Chef部署自开发的应用程序,包括配置所依赖的操作系统配置和软件,以及自身所需的应用配置。可以使用Liquibase进行数据库的schema部署和更新。可以用Jenkins协调和组织所有工序的执行。使用Jenkins管理部署流程的感觉和用它执行CI是类似。

从简单开始,尽量将一组彼此相关的、版本化的可部署物组织在一起发布,例如在一个发布集合中可以包含:UI、REST服务器、消息服务和数据库。尽量使用一条命令构建,使用一条命令部署。

Cookbook设计类型

Library Cookbook 库食谱 :这种类型的食谱涵盖了通用的、可重用的逻辑。例如所有配置基线,也可以是安全基线。例如:dns、ntp、主机登录提示、用户和组、禁用服务清单等等。开发扩展的自定义chef资源,用来部署自开发应用。

Application Cookbook 应用系统食谱 :在以上库食谱的基础上,为一个套应用系统开发一个Cookbook食谱,每个应用可是一个recipe配方,recipe配方使用自定义开发的Chef资源。这样就形成了非常轻量的代码库。

Data Bag 数据袋:包含了各种应用配置,例如:服务端口、JAVA_OPTS等等。一个应用系统Cookbook食谱对应一个数据袋,袋子里面包含了该应用在每一套环境里的相关所有配置点。

上线一个应用的新版本意味着新版本IaC代码的更新和部署,大致的流程是:编辑Chef代码、推送到Chef管理服务器、在节点上运行Chef客户端程序执行部署动作。Chef服务器的版本始终和版本控制库里的Master主干保持一致,这同样意味着环境配置和Master主干代码保持一致。

用Chef开发自定义应用资源的实例代码如下,这段代码表示了一个Java应用war包的部署。

carbon

基于类似于以上的自定义资源类型,在必要的情况下,还可以对其开发Action(chef资源的操作),可能的操作定义有:

  • 从Artifactory服务器下载Java、Tomcat和WAR包。
  • 在标准的路径安装Java和Tomcat。
  • 创建和配置Tomcat容器
  • 在特定的容器里安装WAR包
  • 在主机上开防火墙端口
  • 生成应用属性文件
  • 启动Tomcat容器

Data Bag数据袋的实例代码结构如下:

"version":
"1.4.9",
"runtime":{
    "my-­app-­ui":{
        "java_opts": "-­‐Xmx2G -­‐XX:MaxPermSize=1024m"
    }
},
"app_config":{
    "db.url":   "jdbc:postgresql://devdb:5432/myapp",
    "svc.foo.url":  "http://devsvc:9000/foo"
}

以上是data_bags/my_app/DEV.json的定义,还可以有其它环境的定义data_bags/my_app/TEST.json和data_bags/my_app/PROD.json等。

人员角色

基础架构的持续集成需要Dev和Ops的相互协作,才能做通,才能全面覆盖应用所需要的技术栈。

部署人员 更data_bag新数据袋和环境定义文件,触发生产环境部署的动作,调度chef-client客户端的运行,或者推送新版本的Chef代码更新。

技术负责人 维护应用系统Cookbook食谱。

框架开发人员 维护库Cookbook食谱,维护框架,持续改进流程。

以上这三种角色,从上到下是从Ops到Dev的过渡。对于传统IT组织的架构,部署人员是Ops团队的,框架开发人员是Dev团队的。

这三种角色都凑齐了,才能起到全套应用系统的整体建模和编码,而且每个角色都有负责的部分。技术负责人可能是来自Ops和Dev团队的技术大拿。他们对整体的正确性和完整性负责。

目前也有Dev团队在其内部招聘运维研发的角色。这三种角色是基础架构即代码的层次结构和人员团队架构的对应,在实际工作中可以灵活应用;一方面覆盖所有技术层次,另外一方面引入所有必要的人员,是团队形成合力。

如果不是本着将全套应用系统做全量的部署,以上任何角色做自己职责范围内的IaC自动化实践,其实效果是事倍功半的,或者机会只有技术学习和探索的价值。

构建Cookbook

在开发了各种Cookbook之后,我们就需要对其进行持续测试,因此就需要使用Cookbook的持续构建流程。这个步骤就如同我们对应用程序的代码做CI一样。

cookbook-build

开发人员(程序员不是业务应用开发者的专业名词,这里指IaC开发者,可能来自任何团队)在Workstations工作站上开发Chef的Cookbook代码,将代码提交到GitHub上的Chef代码仓库。

Jenkins的Master服务器会触发CI Job,调用Ruby Slave对Cookbook代码进行集成和测试,然后触发EC2临时实例的创建,将Cookbook在EC2实例中进行测试,使用Artifactory中存储的应用软件包部署应用。如果测试都通过了,就触发Release Job,它将Cookbook代码上传到Chef服务器,供所各种环境中的被管理节点使用。

上图用EC2作为Chef代码的CI环境,可以替代的方案有Vagrant+虚拟化(Virtual Box或者kvm),或者使用其它虚拟机资源池,如Ovirt KVM、Xen、Nutanix。我实际测试过使用Terraform对接Nutanix资源池,虚拟机创建超级快,几乎是秒得的速度。现有的虚拟化资源池就是最方便的对接对象,需要了解一下API和对接工具即可。

对于Jenkins构建服务器而言,每一套应用系统对应的Cookbook组/集合的测试和发布都会在同一个构建服务器上发生,一般情况下这个服务器也是这些应用的CI服务器;这个Jenkins服务器也是相关Cookbook的CI作业和发布作业的运行地点。这个服务器上会安装所需要的Ruby gem包,应该能访问到与Chef服务器链接所需要的秘钥;应该可以使用到创建EC2测试节点虚拟机的秘钥,或者说访问其他类型虚拟机资源池的用户名和密码。

Cookbook CI Job

Cookbook CI作业的触发条件是:当有新Chef代码被合并的时候。它会进静态代码扫码和测试工作,包括如下内容:

  • 使用json和gem的相关工具分析JSON的句法
  • 使用Tailor做Ruby的句法和风格扫描
  • 使用Knife做Chef代码的句法分析
  • 使用Foodctritic做Che代码的句法分析和正确性分析

Chef代码在测试虚拟机里的集成测试是本文的重点,集成测试工具使用Test Kitchen,这个工具有一系列和虚拟化/云环境对接的插件,如 kitchen-ec2插件等。能按需临时的创建用于集成测试的虚拟机,在测试完毕,得出了测试结果之后,就删除本作业所创建的临时虚拟机。

在集成测试的生命周期过程中也可能创建多个测试虚拟机/EC2实例,这个过程使对应用系统里的所有组件进行仿真的、实际的安装包和服务部署,进行单节点或者多节点的全量应用系统部署。在每个节点上都执行Chef代码,在Chef对应用系统的配置和部署完成之后,在对运行中的应用进行验证测试,测试包括测试相关的服务端口是否能访问,返回结果是否正常等等,Chef是可以进行测试驱动开发的,因此可以写出较细致的测试代码,从而分析本Cookbook集成测试通过与否。在测试结束了以后(最好是10分钟左右或更短),删除所有测试的虚拟机资源。

Nutanix

应该尽可能的优化这个集成测试,尽量缩短它的执行时间。可以创建专用的EC2-AMI/Nutanix/Kvm/VMWare操作系统镜像,预装所需要的Ruby环境和Chef工具。

使用Chef Solo(不依赖chef服务器)执行Chef代码的测试,以免将临时节点也添加到了Chef服务器,同时也消除了Chef的客户端和服务器架构之间相互通讯的消耗,这个场景里其实没有使用Chef服务器的必要。使用一个名为CHEFDEV的伪环境来测试代码,而JSON文件里定义的真实环境则被保留用于正式生产环境。在创建EC2虚拟机的时候,给它们打上特定的标签,从而保持一定的可追踪性和环境的可维护性。

Cookbook Release Job

这个作业的运行内容和CI Job基本一致的,而它是靠人为手工触发的,从Chef角度看,可以说:本文上述的所有描述,属于Chef风格的基础架构即代码程序的持续交付。本作业将测试成功的代码在GitHub/GitLab里打上标签,并且上传Cookbook的新版本到正式的Chef生产服务器上。

可以想象经过多个Cookbook的build job之后,Cookbook的某个版本被发布到了生产环境中,用于环境的配置和应用的部署。本Job作业交付了IaC的开发结果到生产环境中的Chef管理服务器。

其它IaC的基础架构持续及集成与本文描述的也应该是类似流程。

应用部署流程

Cookbook的开发和集成完毕了以后,它的结果产物是一些列新版本的Cookbook代码,它们最终上传到Chef服务器。支持生产环境应用部署的Chef服务器与各种环境保持连接,包括测试、预发布和生产等等。

app-deploy-process

在发布过程中所使用到的制品是从Artifactory中拉取的。下面简单说下这个架构中的关键点。

Jenkins部署服务器 这是专门用于各种部署工作的Jenkins Master服务器,它和上一个步骤里Cookbook的Build服务器是不同的。它的Slave应该满足这些需求:安装了所需要Ruby环境和gem包。安装了Chef工具,并且具有能更新Chef服务器的秘钥。具有能访问各种虚拟化环境中节点的SSH秘钥。

部署作业的类型 可以对于每一个应用组(一套应用系统)设置两个部署作业:在开发环境中的,用于开发人员使用的DEV部署作业;另外是运维人员所使用的Non-Dev部署作业。在实践过程中也能发展出其他类型。

部署人员的工作流程:

  1. 变更Chef相关代码和配置,包括:编辑应用的data bag配置数据点,有必要的话编辑环境文件,合并代码。
  2. 然后在Jenkins部署服务器上执行作业。

通过以上的流程和工具,开发、测试和运维的相关人员,如果需要部署应用了,就可以用一键式的、自助式的部署模式,将任何应用应用系统通过一键式的方式自动化的部署到各种应用环境中。

push-button-deploy

这样我们将大量各种角色人员都从事的、没有附加值的应用部署工作,彻底的消灭掉了,节省的时间可以用来做更多有意义的工作,Dev人员有更多时间编码、测试人员不需要等待时间、运维人员也降低的工作压力。

总结

关于基础架构的持续集成,还有下列值得参考的原则:

  • 尽可能的标准化:包括技术、设计和流程等方面;要能够支持环境的规模化扩展,例外是可以的,但是要尽量避免。
  • 所有工具最好有API:避免在某个工具,在工具链上的任何环节的脱节。
  • 使用多种形式的沟通路径:这个实践需要用各种方式进行宣传和推广,包括:全员大会的主题分享、每个团队的启动会议、与开发人员的随时沟通,使用文档进行知识传播和沟通。
  • 保持乐观,尽量发现和找到那些志同道合的早期响应者,让他们和你站在一条战线上。

引用

参考视频

https://www.youtube.com/watch?v=PQ6KTRgAeMU

参考书籍


DevOps教练在知乎

See Also