导读:
环境的自动化创建是DevOps道路上的第一只拦路虎(参考自DevOps Handbook),Ops应该通过IaC和自动化配置管理工具,通过代码的方式来完地表达基础架构以及应用系统。从而使任何人可以按需搭建环境,这样才能增加流动性,使单件流成为可能。Terraform是基础架构的编程语言,而Chef、Ansible和Puppet则是应用部署的编程语言,将二者无缝的联系起来;在流水线里按需调度,这样就可以使:持续集成、持续部署、持续测试等活动,行云流水般的流动起来。
另外,IaC加配置管理工具的组合是操作在垂直维度,而不是水平方向;该组合向下用Terraform等工具和开发、测试和生产等环境的资源池API直接对接(如AWS的API),并完成资源的调度和制备,动态的组合出各种应用系统所需要的计算、存储和网络资源;在此基础上,在向上的方向上,将用配置管理工具如Chef刻画的应用系统动态的生长出来。
为什么是Terraform加配置管理工具的组合:首先Chef、Ansible和Puppet都是Host Based配置管理工具;那么Host是从而来的?通常是手工克隆的;因此需要一种能对接各种异构资源池的、能够管理到资源池内各种资源类型的API的抽象层,这样就到了Terraform工具这个层次了。因此单纯的使用Terraform或者Chef类工具都是不完整的。本文的案例中并没有使用Terraform创建虚拟机,而是直接用的Jenkins的插件,而这并不影响以上的论点。
正文
持续集成通常是针对应用而言的,可是基础架构的持续集成应该怎么做?基础架构的持续集成应该属于持续交付和持续部署的基础。贯穿本文的一个问题,或者在阅读本文时,您应该不断地问自己这个问题:我们的应用部署流程是怎样的?
在回答这个问题之前,我们先来回顾一下,目前几乎所有人都正在使用的手工环境和资源交付流程(这就是我所谓的工单定义的数据中心)。在源码被编译打包了以后,安装包文件被上传保存到了内部的某个文件服务器上或者Artifactory。Ops团队的某个组/人被分配到工单,根据工单描述的需求,它在测试或者生产环境中开始工作:
用图形界面进行虚拟机模板的手工克隆工作,或者可能由于没有相应的权限或者自助服务,不得不给虚拟化管理员发任务单,然后等待回复。
获取用户名和密码,手工ssh登录服务器,有些企业还要等待领导的审批,才能得到密码信封和所需要的访问密码。
根据工单(变更单)里的描述和自己的经验对虚拟机的操作系统进行配置,在这个过程中,Ops往往可能还需和需求方进行不止一次的沟通,确认相关参数。
手工的下载各个应用安装包,然后分别手工上传到目标服务器,凭经验和工单信息部署应用,然后测试部署结果,可能是看下页面有没有正常显示,或者服务起没起.
手工测试和确认这些虚拟机的服务和状态,凭经验觉得OK了以后,回复工单,关闭工单。
以上的工作场景,可能是Ops人员很常规的一天,或者是几天内的工作,当然在这个过程中,他们还需要参与一些救火行动;他们在这个过程中也可能会有疑问,也可能会对此工作结果不确定;但是,日常的工作经验告诉他,差不多了,关闭任务单要紧,还有好多项目催活呢!就这样,配置并不精确的虚拟机环境就交给了下游的需求方。
以上工作过程的问题如下:
工作周期长,速度慢。实际上工作周期拖延的越久,工作结果的质量就越差,而并不是我们想想中的慢工出细活。
所有步骤都是纯手工操作,不仅费事费力,而且出错几率高,也几乎不可能无痛的回退。可能有人会说了,我们不需要那么快,我们也不是互联网公司;可是从精益思想的角度看,以上这些工作都属于对业务价值的交付贡献为零的工作;你可能是由于公司给你发着工资,才错误的感觉到,这项工作活动应该有它的价值。
上游传递来的信息可能不全面,不准确,因此Ops很有可能造成错误配置,因此会返工。
传递给下游的虚拟机很可能会在后续的部署过程中,由于应用需求的变化,而需要下游的人员对其重新配置,产生重复的劳动。
手工部署的时间和代价 = 应的数量 X 应用版本数量 X 环境数量
对以上工作系统进行优化的原则:如果某一项活动的重复频率越高,那么对它进行优化,所产生的回报也会越明显;这里还要参考限制理论,优化的顺序要正确。
这里还有一个误区:自动化运维不仅仅是用Python或者Ansible编写一些可执行的自动化脚本,这些工作产物如果不能进入版本控制系统、不能够和应用部署流程直接对应和关联起来、如果只是Ops自己摆弄的玩具;这种自动化运维其实并没有价值。
我们从这个角度出发,就可以来设定基础架构持续集成和应用部署流程的改进目标了:
减少总体人工工作时间和代价
提高交付的速度、可靠性和频率
能进行应用部署,能进行数据库Schema的更新
能够实现部署流程的自服务,让任何需要部署应用的人能一键式部署任何版本
到了这里我们就必须将上述的手工劳动,都变为自动化的过程。因此,基础架构即代码IaC (Infrastructure as code)和相关的配置管理工具就会用到。
上图是一个典型的持续交付流水线模型,对它的关注点如下:
代码变更会触发Jenkins自动化的构建(CI是基础),打包后的安装包被存储在Artifactory里,Artifactory里面还可以存储应用包的其它相关元数据,如测试结果,能否可以用于下一步部署的标签等等。
Jenkins自动化的搭建所需要的环境,调用虚拟化或者公有云资源池的API,制备虚拟机资源,这个动作可以使用Jenkins对AWS EC2或者VMWare的插件,也能用Terraform这里IaC工具实现;然后再调用Chef类的配置管理工具完成对虚拟机内的配置,完成应用系统部署所需要的所有层次的配置。
环境配置完成后,应用正常运行起来了,在用相关的测试工具对部署后的环境做验收测试,Chef具备支持测试驱动开发的相关工具。
基础架构的持续集成
为了实现完整的基础架构持续集成流程,以上持续交付流水线必须匹配的能力和概念包括:分层的系统管理、基础架构即代码IaC、配置管理、Chef工具等。下面详细对它们进行描述。
分层的系统管理
系统管理的层次涉及到OS相关的三个层次。下面自下而上地简单描述一下。
Provisioning - 制备管理:涉及到虚拟化层,这一层是资源表达层,目前所有主流的虚拟化都支持标准的Rest API,包括VMWare、EC2和Nuanix等。大多数主流配置管理工具都具备用于虚拟机生命周期管理(从生成、到开机、到删除等)的API功能,能按需的获得任何数量、规模、网络和操作系统类型的部署环境。
Configuration - 配置管理:在任何类型的操作系统里自动化的安装和配置软件包,将所有配置参数配置好以后,持续保持这些配置点的状态。对于简单应用,来说按配置参数启动服务即任务完成。
Orchestration - 应用编排管理:对于复杂的分布式系统,由于各个自服务之间存在着依赖关系,所有子服务之前需要互通一些配置参数,才能实现应用程序整体的正常运行;例如需要配置应用服务器的odbc数据库连接,配置web前端的ldap认证服务器、不同服务模块之间的API调用地址等等。
不同的DevOps配置管理工具也都力求能覆盖以上三个层次,但是都不能全面覆盖,主要是它们所追求的方向,或者主要想解决的问题并不相同。因此各种工具之间功能上会有重叠,同时保持者各自的风格。
因此在运用这些工具的时候,不仅要追求其卓越的功能,还要能意识到,并有意的在不同层面上做取舍,并组合其它工具进行完善和增强。
基础架构即代码
IaC这个概念最早是被Chef这类工具提出并实现,它的基本想法就是让Ops人员象开发人员一样的,工作在代码层面,而不是面对着数十个图形和文字终端界面。使用类似于开发应用程序的方式,通代码来开发和管理基础架构环境,因此基础架构能通过API访问和操控是基础,目前所有主流的虚拟化/云计算平台都具备很好的API接口;可惜的是在传统企业中,这些资源池的API功能几乎没有被用到。
像开发应用代码一样的管理IT基础架构,基础架构的开发和管理也需要遵循与应用开发类似的原则,这些原则包括:
一切从源代码开始: 并对其进行严格的版本管理,想要对基础架构变更,就需要对相应的代码进行变更和测试,然后发布这些代码。从而力求做到服务器的免登录运维。
模块化设计:不同的应用系统底层所依赖的基础架构有着大量的相似之处,模块化设计不仅意味着标准化,还意味着更少的重复代码。我所用过的Terraform、Chef和Puppet这三种工具,都具有高度的模块化特性。
抽象能力:能够使用不同的模块和参数对任何特征的应用进行建模,用IaC代码进行表达,基础架构的代码开发也就是借助这种抽象能力,将所有管理对象(此处引起了我深深的回忆,也就是CMDB里定义的配置管理项CI)具体化地描述为应用服务模型。编写出来的基础架构代码,不仅包含了所有对应用配置描述性的语义,而且还是能够被执行的代码,在IaC代码(组合)执行之后,你就得到了所期望的虚拟机、应用配置和应用服务。
可测试性:这是一个往往被忽略的能力,可是它至关重要,你会发现IaC也是编程语言,就是对基础架构进行高级的编程,而且IaC代码本身和它的运行结果都应该是可以测试的。在执行前对其语义语法测试,在运行以后对其运行结果测试。Chef在这方面表现的尤为突出。
配置管理
我可能是最早的一批从事ITIL配置管理实践和CMDB建设的这批人;我以前和甲方客户有着大量的关于配置管理/CMDB的对话,所经历过的项目也非常煎熬。而在DevOps场景下,感觉以前的经历也是很有意思的,只是我现在说到的CI,在没有特指的情况下,是持续集成的概念,而不是CMDB里的配置项了。
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相关的配置管理工具上的。为了纠正这个错误观点,我们经常说:“天文学并不只是关于望远镜的。”
配置管理工具中有很多是基于主机的管理工具(Host based),包括:CFEngine、Puppet、Chef、Salt和Ansible等。它们都或多或少地具备基础架构即代码的原则和特征。都能实现:定义服务器的目标期望状态的能力,在每一次执行周期里,都进行状态检查,汇报当前状态和配置基线的漂移,在必要的时候也可以自动的执行必要的状态修复操作。
Chef这种配置管理工具,使用了Ruby风格的DSL语言,使用者只需要用Chef代码表达“What”即可,而不需要明白“How”;“What”就是对目标配置状态的描述,使用者只需要将需求翻译为Chef代码,然后用Chef客户端工具在OS中运行它即可。Chef代码语法清晰,描述能力强大。在编码的时候遵循DSL规则,如果有必要的话也可以调用Ruby。
Chef是客户端服务器的架构(Client-Server),安装了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 库食谱 :这种类型的食谱涵盖了通用的、可重用的逻辑。例如所有配置基线,也可以是安全基线(此处已经呼应了重要的信息安全话题,即DevSecOps,以后开专题分享)。例如:dns、ntp、主机登录提示、用户和组、禁用服务清单等等。扩展开发的Chef自定义资源,用来部署自开发应用。
Application Cookbook 应用系统食谱 :在以上库食谱的基础上,为一个套应用系统开发一个Cookbook食谱,每个应用可是一个recipe配方,recipe配方使用自定义开发的Chef资源。这样就形成了非常轻量的代码库。
Data Bag 数据袋:包含了各种应用配置(这里就是我们经常讨论到的应用配置管理,配置参数的分层管理等),例如:服务端口、JAVA_OPTS等等。一个应用系统Cookbook食谱对应一个数据袋,袋子里面包含了该应用在每一套环境里的几乎所有相关参数配置点。
上线一个应用的新版本,就意味着IaC代码的更新和部署,大致的流程是:编辑Chef代码、推送到Chef管理服务器、在节点上运行Chef客户端程序触发部署动作。Chef服务器的版本始终和版本控制库里的Master主干保持一致,这同样意味着环境配置和Master主干代码保持一致。
用Chef开发自定义应用资源类型的样例代码如下,这段代码表示了一个Java应用,部署工件为war包。
基于类似于以上的自定义资源类型,在必要的情况下,还可以对其开发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的相互协作,才能做通,才能全面覆盖应用系统所需要的技术栈。否则Ops人员单练术语无无效的局部改进。
部署人员 :更新Data_bag数据袋和环境定义文件,触发生产环境部署的动作,调度Chef-client客户端的运行,或者推送新版本的Chef代码更新。
技术负责人 :维护应用系统的Cookbook食谱。
框架开发人员 维护库Cookbook食谱,维护应用框架,从持续改进应用系统的交付流程。
以上这三种角色,从上到下是从Ops团队到Dev团队的过渡。对于传统IT组织的架构,部署人员是Ops团队或者是专职的,框架开发人员是Dev团队的。
这三种角色都凑齐了,才能做到全套应用系统的整体建模和编码,而且每个角色都有所负责的部分。技术负责人可能是来自Ops和Dev团队的技术大拿。他们对整体的正确性和完整性负责。
在这里Dev和Ops人员终于使用统一中语言来聊应用部署这个故事了,IaC也好比鹊桥一般。 -- 刘征 ;-)
这三种角色是基础架构即代码的层次结构和人员团队架构的对应,在实际工作中可以灵活应用;一方面覆盖所有技术层次,另外一方面引入所有必要的人员,是团队形成合力。目前也有Dev团队在其内部招聘运维研发的角色,但是并没有能和Dev团队紧密的协作起来。
如果不是本着将全套应用系统做全量的部署,以上任何角色只做自己职责范围内的IaC自动化运维实践,其结果会是事倍功半的,或者几乎只具有技术学习和探索的价值,而不具有形成集体生产力的可能性。
构建Cookbook
在开发了各种Cookbook之后,我们就需要对其进行持续测试,因此就需要使用Cookbook的持续构建流程。
这个步骤就如同我们对应用程序的代码做CI一样。也就是说更新的代码在上传到Chef服务器以前,必须和运行环境(EC2虚拟机)做持续集成和测试,验证其配置结果和预期的一致。讲到这里才点题,感谢读者的耐心。
开发人员(程序猿并不只是业务应用开发者的专用名词,这里指IaC开发者,来自任何团队)在Workstations工作站上开发Chef的Cookbook代码,将代码提交到GitHub里的Chef代码仓库或者企业私有代码库。
Jenkins的Master服务器会触发CI Job,调用Ruby Slave对Cookbook代码进行持续集成和测试,然后触发EC2临时实例的创建,将Cookbook在EC2实例中进行测试,使用Artifactory中存储的应用软件包部署应用。如果测试都通过了,就触发Release Job,该作业将Cookbook代码上传到Chef服务器,并通过Chef服务器提供给各种环境中的被管理节点。
上图使用AWS EC2作为Chef代码的CI环境,可以替代的方案有Vagrant+虚拟化(Virtual Box或者kvm),或者使用其它虚拟机资源池,如Ovirt KVM、Xen、Nutanix。
我实际测试过测试过Terraform对接Nutanix资源池,虚拟机创建速度超级快,几乎是秒得的速度。可能你企业环境里现有的虚拟化资源池就是最方便的对接资源池,只需要你能了解其API以及相关的对接工具即可。
在分析一下Jenkins构建服务器的需求,每一套应用系统对应的Cookbook集合的测试和发布都会在同一个构建服务器上发生,一般情况下这个服务器也可以是这些应用代码的CI服务器;这个Jenkins服务器也是相关Cookbook的CI作业和发布作业的运行地点。这个服务器上会安装所需要的Ruby gem包,它应该能访问到与Chef服务器链接所需要的秘钥;应该可以使用到创建EC2测试节点虚拟机的秘钥,或者说能访问到调用其他类型虚拟机资源池API的用户名和密码。
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分钟左右或更短),删除所有测试的虚拟机资源。
应该尽可能的优化这个集成和测试流程,尽量缩短它的执行时间。可以创建专用的EC2-AMI/Nutanix/Kvm/VMWare操作系统镜像,预装所需要的Ruby环境和Chef工具。
可以使用Chef Solo(不依赖chef服务器)执行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管理服务器。
其它工具和的基础架构持续及集成与本文所描述的也应该是类似流程。
应用部署流程
Cookbook的开发和集成完毕了以后,它的结果产物是一些列新版本的Cookbook代码,它们最终上传到Chef服务器。
管理者生产环境应用部署的Chef服务器与各种环境保持连接,包括测试、预发布和生产等等。
在发布过程中所使用到的所有制品都是从Artifactory中拉取的。下面简单说下这个架构中的关键点。
Jenkins部署服务器 这是专门用于各种部署工作的Jenkins Master服务器,它和上一个步骤里Cookbook的Build服务器是不同的服务器。它的Slave应该满足这些需求:安装了所需要Ruby环境和gem包。安装了Chef工具,并且具有能更新Chef服务器的秘钥。具有能访问各种虚拟化环境中节点的SSH秘钥。
部署作业的类型 可以对于每一个应用组(一套应用系统)设置两个部署作业:在开发环境中的,开发人员所使用的DEV部署作业,满足开发和测试的部署需求,从此测试环境在也不手工搭建和等等;另外是运维人员所使用的Non-Dev部署作业,也就是生产环境的部署作业。在实践过程中也能发展出其他类型。
部署人员的工作流程:
变更Chef相关代码和配置,包括:编辑应用的data bag配置数据点,有必要的话编辑环境文件,合并代码。此合并也会触发CI,CI必定也必须是通过的。
然后在Jenkins部署服务器上执行目标的部署作业。
通过以上的流程和工具,开发、测试和运维的相关人员,如果需要部署应用了,就可以用一键式的、自助式的部署模式,将任何应用应用系统通过一键式的方式自动化的部署到各种应用环境中。下图为安装了参数化插件的Jenkins界面,这是最建议的展现方式。这个方式的好处是,直接满足需求,将有限的精力都放在以上持续交付流水线的开发和优化上,而非一些面子工程上。
这样我们将各种角色人员都大量从事的、没有附加值的应用部署工作,就彻底的消灭掉了,节省的时间可以用来做更多有意义的工作,Dev人员有更多时间编码、测试人员可以做更多测试、运维人员也降低了工作压力。
总结
关于基础架构的持续集成,其它值得参考的原则如下:
尽可能的标准化:包括技术、设计和流程等方面;要能够支持环境的规模化扩展,例外是可以的,但是要尽量避免。
所有工具最好有API:避免由于某个工具,使工具链上的任何环节的断裂。
使用多种形式的沟通路径:应用这个实践还需要通过各种方式进行宣传和推广,包括:全员大会的主题分享、每个团队的启动会议、与开发人员的随时沟通,文档形式的知识传播和沟通等。
保持乐观,尽量发现和找到那些志同道合的早期响应者,让他们和你站在一条战线上。java架构相关的资源分享群 里面有分布式 微服务 性能优化 多线程并发等最新学习资源,免费获取,帮助提升核心竞争力,突破技术瓶颈,群号:617434785