[注:原文发布于2011年12月23日]
背景
现如今,单元测试、自动化验收测试、持续集成等技术手段已被很多项目团队所采用,它们可以在软件开发活动中很大程度的保证开发软件的正确性,即是否满足了新的需求并且没有破坏已有的需求。但是如果软件无法顺利的部署到生产环境上,就不能带来任何商业价值。
作为软件开发人员,为了验证软件是否能够部署成功,不应该只有当软件设计、开发、测试等阶段结束后才向生产环境或准生产环境部署,而应该把部署作为整个软件开发活动的一部分,从项目之初,在项目整个持续过程中,实现自动化的构建、部署、测试,即“部署流水线”。
挑战
有了“部署流水线”之后,当我们在每次代码提交时,都有可能向测试环境、准生产环境等不同环境部署软件并测试,会有如下情况涉及到自动化部署:
这就要求我们拥有自动化部署的能力,它有如下若干特点:
这样当我们将软件最终向生产环境部署时,同样的部署代码已经在类似的环境中使用并测试过,对于这次发布我们就有足够的信心能够成功。
自动化部署
由上可以看出,自动化部署最主要的问题在于如何创建基础设施,以及如何安装和配置软件产品。
在这里我们先说说如何自动化的安装和配置软件产品。只要是手工过程可以完成的安装和配置工作,理论上我们都可以将其代码脚本化。开发人员或者系统管理员完全可以通过 bash 或者 PowerShell 来完成这些工作。这要求我们将部署代码以及更为重要的环境配置文件都当作产品的一部分,放入版本控制库中。
现如今已经有了很多自动化准备技术可以帮助开发人员实现部署脚本,比如比较流行的 Puppet 和 Chef。以 [Chef](http://wiki.opscode.com/display/chef/Home) 举例,Chef 是一个开源的系统配置和集成框架,它通过自定义的 DSL 领域语言来实现基础设施和软件环境的搭建,并支持物理机器、虚拟机、云节点(理论上开放了 ssh 端口都可以)。由于 Chef 的部署代码是 ruby 语言,所以我们也可以很方便的对其扩展,实现任何自定义的功能。
有了自动化的部署脚本,既可以可控的可重复的执行部署过程。这里需要指出的是,当开发或测试人员在某个环境上发现产品配置问题时(比如发现性能测试环境上应用服务器调用某一个 API 服务的地址有误),应该极力避免开发人员直接远程连接到该机器上手工修改。一旦在版本控制中有了自动化部署脚本和配置管理,就应该从源代码级别来修改,通过重新触发一次新的“部署流水线”来修正问题并验证修改是否正确。
云和虚拟化技术
对于自动化的基础设施管理,近来年已经逐渐成熟的云和虚拟化技术可以给我们带来很大的好处。云和虚拟化其实并不是一个新的概念,只是最近几年在技术圈子里很火热。抛去云和虚拟化华丽的外表,其实这里只需要它们提供给开发人员基础设施管理的能力,使用云和虚拟化的好处有:
当然云和虚拟化的合理使用也会带来成本上的好处,但更为重要的是给我们开发团队的自动化部署、持续交付带来了可能。可以想象,通过云和虚拟化技术,开发人员可以在实现一个需求后,通过一条命令可以几分钟内将其自动化部署到一个新创建的环境中,在向业务人员做完演示后,又能通过一条命令将该环境清除。
下面举两个目前较为流行的例子,给出大家云和虚拟化技术对自动化部署时基础设施管理提供的便利。
Amazon平台
以 Amazon 的 EC2 服务为例,目前已经有了很多对其提供支持的工具,比如 [Amazon EC2 API Tools]( http://aws.amazon.com/developertools/351)。配置好 Amazon 的 key 后,我们可以很方便的创建一个 EC2 Node:
ec2-run-instances ami-a54d67d1 --instance-type t1.micro --region us-west-1 --key MY_AMZ_KEY
# =>服务器在美国西海岸,大小为 micro,系统镜像为 ami-a54d67d1
但这样仅仅是创建出一台最基本的基础设施,对于开发人员来讲,仍需要通过脚本代码来对其进行自动化配置。不过,目前已经有许多工具已经集成了对 EC2 的自动化部署。比如,Chef 通过一个插件 [knife-ec2]( https://github.com/opscode/knife-ec2)来直接对 Amazon EC2 进行支持。当我们用 Chef 写出对自己产品的部署配置代码后,可以通过一条命令自动化地创建出一个具有某种角色的服务器,安装并配置项目的产品及其依赖,示例脚本如下:
knife ec2 server create "role[rails_server]" --image ami-31814f58 --flavor t1.micro --availability-zone us-east-1a--ssh-key MY_AMZ_KEY
# =>服务器在美国东海岸,大小为micro,系统镜像为 ami-a54d67d1,Chef 按照部署代码将其配置为rails_server的角色
vagrant
对于虚拟化来说,现在也有许多技术可以给开发提供自动化基础设施管理。这里举 vagrant 为例。[vagrant](http://vagrantup.com/)是一个基于 VirtualBox 创建和发布虚拟化环境的工具,它可以自动化创建虚拟机,并对其进行基础设施配置。vagrant 的使用非常方便,并且 vagrant 也直接支持 Chef 和 Puppet。我们创建一个 VagrantFile 表示一台虚拟机,简单脚本内容如下:
Vagrant::Config.run do |config|
config.vm.box = "centos6"
config.vm.provision :chef_solo do |chef|
chef.cookbooks_path = "/PATH/TO/chef-repo/cookbooks"
chef.roles_path = "/PATH/TO/chef-repo/roles"
chef.add_role "db_master_server"
end
end
VagrantFile也是由 ruby 写成,这份VagrantFile表示是一台基于 centos6 box (vagrant 初始化一个虚拟机的镜像)的虚拟机,在创建时会通过 Chef 向其部署 `db_master_server` 的角色。有了VagrantFile我们就只需要通过简单的命令就可以控制虚拟机了。
vagrant up # =>创建、启动虚拟机
vagrant suspend # =>挂起虚拟机,保存状态
vagrant halt # =>停止虚拟机
vagrant destroy # =>销毁虚拟机
镜像及部署脚本管理
大部分的云和虚拟化技术都支持从一个镜像(image 或 box)开始创建节点,然后再结合我们的部署脚本对其进行安装和配置。对于有些已经比较固定的基础设置,或者一些配置耗时很长的过程,我们完全可以直接将其做入镜像中。而对于属于我们产品的部分,以及一些很容易变化的依赖,应该通过部署脚本来管理。
对于我们自定义创建出来的镜像,很难放置到团队的版本控制中去(一般这些镜像文件都会很大),但是,我们可以把创建镜像的过程自动化,并将其添加到版本控制中(这就如同我们的版本控制中只有源代码而不要保存二进制构建结果一样)。比如 vagrant 就可以通过简单的命令将当前虚拟机做成镜像(box)。我们可以通过最基础的 box 创建虚拟机,通过 Chef 安装配置机器,然后再通过 vagrant 将其做成镜像,这个过程完全可以代码化。
环境管理
通过云和虚拟化技术,再结合自动化部署脚本,提供给了开发人员一种通过受版本控制的脚本代码来自动创建部署机器的能力。但以上过程还是仅仅在针对一个节点的管理。基于这种能力之上,我们可以实现完整的对环境的自动化管理。
通过对项目系统拓扑结构的配置文件化,我们只需再添加少许工作,完全可以通过一个拓扑结构的配置文件(比如一个 xml 或者 yaml)结合持续集成构造出来的产品库(二进制构建结果),实现对整个环境的控制:
environment create uat-env --topology.yml
# => 创建出一个名为`uat-env` 的完整环境(集成多个节点)
项目实例
最后给大家提供一个实际的项目案例。在本项目中,团队中任何一个人员修改并提交了代码(这里包括生产代码、测试代码、验收用例、部署脚本、配置文件等),都会在“部署流水线”上触发一个新的流程:
这样任何人所作的修改都会得到一个渐进的增强,流水线走的越远,团队得到的信心就越强。这种信心不仅仅只限于我们的代码功能实现的正确性,更重要是对我们的产品能够顺利部署上线的信心。
总结
在自动化测试和持续集成之上,我们通过“部署流水线”可以实现持续交付的能力。可能对于国内某些项目团队,特别是遗留系统,还需要付出很多努力。但是可以逐步实施,一步一步打造每一个过程。云和虚拟化技术给我们提供了一个自动化基础设施管理的能力,很大的帮助了我们持续交付中的每一个过程,最终达到了将团队每一个成员的工作能够顺利的转化为线上的商业价值。