您可以捐助,支持我们的公益事业。

1元 10元 50元





认证码:  验证码,看不清楚?请点击刷新验证码 必填



  求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Modeler   Code  
会员   
 
   
 
 
     
   
 订阅
  捐助
穷人的持续集成与持续交付
 
作者:慎独慎行 来源:CSDN 发布于 2016-2-23
   次浏览      
 

服务器端应用的持续交付

本文将使用一些免费的服务来为你的项目搭建持续交付平台,这些服务包括

持续集成环境

持续部署环境

服务端应用托管

以及一些可以用于本地开发使用的开源工具如:

基于Node的构建monitor

Heroku的命令行工具

Travis CI的命令行工具

除此之外,我们在过程中编写的脚本还可以用以本地构建,如果你的团队中正好已经有CI工具/CD工具,将这些脚本集成进去也是一件非常容易的事情。

背景知识

软件的度量

传统的管理方法论,在软件开发这个领域来说基本上是不工作的。软件项目的不确定性使得人们畏惧,管理者希望通过一些数字,指标来让自己感到某种虚幻的“掌控感”。软件行数,测试覆盖率,代码故障率等数字的名声基本上已经很糟了,经常有人拿来讽刺那些追求虚幻掌控感的“领导”。

但是有一个数字,即使最顽固的“自由主义者”也会认为是有意义的,那就是周期时间(cycle time)。简而言之,就是一个需求从产生到最终上线所需要的时间。其中包括了需求分析,设计,编码,测试,部署,运维等活动,可能还会包含后续的监控。

其实不论是瀑布模型,还是迭代开发的方式,或者其他的方法论,周期时间的缩短都是至关重要的。而具体到周期内,单纯的开发时间变长或者测试时间变长都无关紧要。比如项目A的开发时间是测试时间的2倍,项目B则恰恰反过来,这并不能说A做的比B好,真正有意义的是A的周期时间是否比B更短。

单纯改善项目过程中的某一个阶段的时间,可能并不能达到预期的目的。局部优化并不一定会带来全局的优化。换言之,通过某些策略来提高软件测试的效率未必能减少周期时间!。

持续交付

传统情况下,企业要进行软件开发,从用户研究到产品上线,其中会花费数月,甚至数年(我的一位印度同事给我聊起过,他的上家公司做产品,从版本启动到版本上线需要整整两年时间!)。而且一旦软件需求发生变更,又有需要数月才能将变更发布上线。除了为变更提交代码外,还有很多额外的回归测试,发布计划,运维部门的进度等等。而市场机会千变万化,在特定的时间窗口中,企业的竞争者可能早已发布并占领了相当大的市场份额。

在软件工程领域,人们提出了持续交付(continuous delivery)的概念,它旨在减少周期时间,强调在任何时刻软件都处于可发布状态。采用这种实践,我们可以频繁,快速,安全的将需求的变化发布出来,交由真实世界的用户来使用,在为用户带来价值的同时,我们也可以快速,持续的得到反馈,并激励新的变化产生(新的商业创新,新的模式等)。

持续交付包含了自动化构建,自动化测试以及自动化部署等过程,持续改进开发流程中的问题,并促进开发人员,测试人员,运维人员之间的协作,团队可以在分钟级别将变更发布上线。

持续交付相关技术及实践

版本控制(配置管理)

持续集成CI

自动化测试

构建工具及构建脚本

部署流水线

团队通过版本控制来进行协作,所有的代码会在持续集成环境中编译,代码静态检查/分析,自动化测试(还可能产生报告等)。除此之外,CI还还需要有自动化验收测试,自动化回归测试等。

持续交付则更进一步,它将环境准备,持续集成,自动化部署等放在了一起。通过全自动(有些过程可以设置为手动,比如发布到产品环境)的方式,使得软件可以一键发布。如果上线后发现严重defect,还支持一键回滚的机制(其实就是将之前的一个稳定版本做一次发布,由于发布流程已经经过千锤百炼,所以发布本身就变得非常轻松,安全)

这篇文章中,我们会使用git+github作为版本控制工具,travis-ci作为持续集成环境,gradle作为构建工具,Heroku作为应用的部署环境。这些工具都是免费服务,如果你需要更高级的功能(比如更多的并发数,更大的数据库),则可以选择付费套餐。不过对于我们平时的大部分side project来说,免费服务已经足够。

实例

我在《前后端分离了,然后呢?》这篇文章中,提到了一个叫做bookmarks的应用,这个应用是一个前后端分离的非常彻底的应用。

我们这里会再次使用这个应用作为实例,并采用不同的两个免费服务(travis-ci和snap-ci)来完成持续部署环境的搭建。

bookmarks服务器

bookmarks-server是一个基于spring-boot的纯粹的API,它可以被打包成一个jar包,然后通过命令行启动运行。在本文中,我们我们将会将这个server部署到heroku平台上。

首先需要定义一个Procfile,这个是我们应用的入口,heroku根据这个文件来明确以何种方式来启动我们的应用:

web: java -Dserver.port=$PORT -jar build/libs/bookmarks-server-0.1.0.jar --spring.profiles.active=staging

由于我们在本地使用的使用mysql,而heroku默认的是postgres数据库,因此需要在application.yml中额外配置

<code>spring:
profiles: staging

datasource:
driverClassName: org.postgresql.Driver
url: ${JDBC_DATABASE_URL}
username: ${DATABASE_USER}
password: ${DATABASE_PASS}

jpa:
database_platform: org.hibernate.dialect.PostgreSQLDialect
hibernate:
ddl-auto: update
</code>

有了这些配置后,我们需要创建一个heroku应用:

$ heroku create
Created http://quiet-atoll-8237.herokuapp.com/ | git@heroku.com:quiet-atoll-8237.git

创建之后,我们可以在界面上对这个应用进行一些配置(当然,也可以通过命令行,具体参看heroku help)。为了支持数据库,需要为我们的应用添加一个postgres的AddOn。添加之后,heroku会为我们提供一个postgres的连接地址,格式大概是这样:

postgres://username:password@host:port/database

然后我们需要在Heroku的配置界面中配置一些环境变量:

这样,当应用部署到Heroku上之后,我们的应用就可以读到这些配置了(注意application.yml中的环境变量JDBC_DATABASE_URL)。

搭建持续集成环境

持续集成环境,这里我们选用最简单的travis-ci,它可以很容易的与github集成。

在项目X中定义一个.travis.yml的文件

将你的代码push到github上

绑定github帐号到travis

在travis中启用项目X

这个.travis.yml因项目而异,我们这里的项目是spring-boot,所以只需要指定java即可:

language: java

如果是java项目,并且项目中有build.gradle,travis-ci会自动执行gradle check任务。

自动化部署

当CI运行成功之后,我们需要travis-ci帮我们将应用程序发布到heroku上,这时候需要做一些修改。最简单的方式是直接安装travis-ci的命令行工具到本地:

$ gem install travis -v 1.8.0 --no-rdoc --no-ri

然后通过heroku的auth:token命令获得heroku的token,在加密并写入.travis.yml:

$ heroku auth:token
00xxxxxxxxxxxxx55d11dbd0cxxxxxxxxxxfe067

$ travis encrypt 00xxxxxxxxxxxxx55d11dbd0cxxxxxxxxxxfe067 --add

当然可以合并为一条命令:

$ travis encrypt $(heroku auth:token) --add

将加密过的token存入.travis.yml文件。最后的结果大致如下:

language: java
deploy:
provider: heroku
api_key:
secure: ...
app: quiet-atoll-8237

注意此处的app,正是我们的App的名字。另外,还需要给build.gradle添加一个名叫stage的task,travis在deploy时需要这个task:

task stage {
dependsOn build
}

这样,我们只需要在本地的一个提交,一切都会自动化起来:

travis会执行gradle check

gradle check会编译并运行自动化测试

travis会部署应用到heroku上

heroku会自动重启服务

我们可以在本地进行简单的测试(注意此处我们的staging环境的URL):

$ curl https://quiet-atoll-8237.herokuapp.com/api/feeds -s | jq .
[
{
"id": 1,
"url": "http://icodeit.org/2016/01/how-to-summarize-privious-project/",
"title": "如何持久化你的项目经历",
"author": "icodit.org",
"summary": "通常来说,下项目总是一件比较高兴的事(大部分团队还会一起吃个饭庆祝一下)。",
"publishDate": "2016-01-07"
},
{
"id": 2,
"url": "http://icodeit.org/2015/11/get-started-with-reflux/",
"title": "你为什么应该试一试Reflux?",
"author": "icodit.org",
"summary": "React在设计之初就只关注在View本身上, 其余部分如数据的获取,事件处理等,全然不在考虑之内。",
"publishDate": "2016-01-09"
}
]

完整的代码在这里。

其他

CI monitor

node-build-monitor是一个非常容易配置,使用的CI monitor,我们只需要进行简单地配置,就可以将travis的状态可视化出来

{
"monitor": {
"interval": 2000,
"numberOfBuilds": 12,
"debug": true
},
"services": [
{
"name": "Travis",
"configuration": {
"slug": "abruzzi/bookmarks-server"
}
}
]
}

不过这个工具会在有网络异常时自动终止,我们可以通过一个简单的脚本来在它终止时自动重启:

#!/bin/bash

until node app/app.js
do
echo "restarting..."
done

小结

通过travis和heroku这样的免费服务,我们就可以轻松的将自己的项目做到持续集成+持续交付。我们后端的服务相对来说是比较容易的,但是涉及到一个前后端分离的架构,如何做到静态内容的托管,打包,部署,并和后端API集成起来,我会在下一篇文章中详细解释。

客户端程序的的持续交付

上篇文章介绍了如何使用一些免费的服务来实现服务器端API的持续集成、持续交付环境的搭建。有了服务端,自然需要有消费者,在本文中我们将使用另外一个工具来实现纯前端的站点的部署。

其中包括:

持续集成(单元测试,集成测试等)

持续部署/持续交付

静态站点托管

除此之外,我们还会涉及到:

自动化UI测试site_prism

静态站点的发布脚本

aws的命令行工具

我们的应用最后看起来是这样子的。

技术选型

我们在本文中,将采取另外一套免费服务来完成环境的搭建

1.ThoughtWorks出品的Snap CI作为持续集成/持续交付环境

2.AWS的S3作为应用发布的地方

Snap CI是一个非常易于使用的持续交付环境,由于很多关于持续集成,持续交付的概念和实践都跟ThoughtWorks有关,所以这个产品对于构建,流水线,部署等等的支持也都做的非常好。

S3是亚马逊的云存储平台,我们可以将静态资源完全托管在其上。S3的另一个好处是它可以将你的文件变成一个Web Site,比如你的目录中有index.html,这个文件就可以作为你的站点首页被其他人访问。这个对于我们这个前后端分离项目来说非常有用,我们的css,js,font文件,还有入口文件index.html都可以托管于其上。

实例

在本文的例子中,我们将定义3个stage。Snap CI将一次发布分为若干个stage,每个stage只做一件事情,如果一个stage失败了,后边的就不会接着执行。

我们的3个stage分别为:

单元测试

集成测试

部署

准备工作

bookmarks-frontend是一个纯前端的应用,它会消费后端提供的API,但是其实它并不知道(也不应该知道)后端的API部署在什么地方:

$(function() {
var feeds = $.get(config.backend+'/api/feeds');
var favorite = $.get(config.backend+'/api/fav-feeds/1');

$.when(feeds, favorite).then(function(feeds, favorite) {

//...
});
});

由于我们在本地开发时,需要backend指向本地的服务器,而发布之后,则希望它指向上一篇文章中提到的服务器,因此我们需要编写一点构建脚本来完成这件事儿:

var backend = 'http://quiet-atoll-8237.herokuapp.com';

gulp.task('prepareConfig', function() {
gulp.src(['assets/templates/config.js'])
.pipe(replace(/#backend#/g, 'http://localhost:8100'))
.pipe(gulp.dest('assets/script/'));
});

gulp.task('prepareRelease', function() {
gulp.src(['assets/templates/config.js'])
.pipe(replace(/#backend#/g, backend))
.pipe(gulp.dest('assets/script/'));
});

我们定义了两个gulp的task,本地开发时,使用prepareConfig,要发布时,使用prepareRelease,然后定义一个简单的模板文件config.js:

module.exports = {
backend: '#backend#'
}

然后可以很简单的包装一下,方便本地开发和发布:

gulp.task('dev', ['prepareConfig', 'browserify', 'concatcss']);
gulp.task('build', ['prepareConfig', 'script', 'css']);
gulp.task('release', ['prepareRelease', 'script', 'css']);

这样,我们在本地开发时,只需要简单的执行:

$ gulp

即可。而在发布阶段,只需要执行:

$ gulp release

单元测试

我们在Snap CI上将github上的代码库关联起来,然后添加一个名叫unit-test的stage,指定这个stage对应的命令为:

npm install
gulp

这样,每当我们有新的提交之后,Snap CI都会拿到新代码,并执行上述命令,如果执行成功,则本地构建成功。

集成测试

由于采取的是前后端分离的策略,我们的应用可以完全独立与后端进行开发,因此我们设置了一个fake server,具体细节可以参考我之前的博客,也可以看源码。不过这里我们要为集成测试编写一个脚本,并在Snap CI上执行。

#!/bin/bash

export PORT=8100
bundle install

# launch the application
echo "launch the application"
ruby app.rb 2>&1 &
PID=$!

# wait for it to start up
sleep 3

# run the rspec tests and record the status
rspec
RES=$?

# terminate after rspec
echo "terminate the application"
kill -9 $PID

# now we know whether the rspec success or not
exit $RES

这个脚本中,首先安装所有的gems,然后启动fake server并将这个server放置在后台运行,然后执行rspec。当rspec测试执行完成之后,我们终止服务进行,然后返回结果状态码。

这里使用了capybara和poltergeist来做UI测试,capybara会驱动phantomjs来在内存中运行浏览器,并执行定义好的UI测试,比如此处,我们的UI测试:

require 'spec_helper'

describe 'Feeds List Page' do
let(:list_page) {FeedListPage.new}

before do
list_page.load
end

it 'user can see a banner and some feeds' do
expect(list_page).to have_banner
expect(list_page).to have_feeds
end

##...
end

部署

首先需要在S3上创建一个bucket,命名为bookmarks-frontend。然后为其设置static website hosting,这时候AWS会assign一个新的域名给你,比如http://bookmarks-frontend.s3-website-us-west-2.amazonaws.com/。

然后你需要将这个bucket设置成public,这样其他人才可以访问你的bucket。

有了这个之后,我们来编写一个小脚本,这个脚本可以将本地的文件上传至S3。

#!/bin/bash

# install gulp and its dependencies
npm install

# package stuff, and point the server to the right place
gulp release

# upload the whold folder
aws s3 cp public/ s3://bookmarks-frontend \
--recursive \
--region us-west-2 \
--acl public-read

aws命令是aws command line提供的,另外我们需要在环境变量中设置AWS提供给你的token:

AWS_ACCESS_KEY_ID=xxxxxxxxxx
AWS_SECRET_ACCESS_KEY=xxxxxxxxxx

然后我们就可以将本地的public目录递归的上传到S3的对应目录了!

完整的代码可以在此处下载。

总结

我们前端的持续交付也介绍完了。现在前后端应用完全独立,发布也互不影响。不论是服务器端新增加了API,还是添加了新数据,客户端的发布都不受影响;同样,修改样式,添加新的JavaScript也完全不会影响后端。更重要的是,所有的发布都是一键式的,开发者只需要一个git push就可以享受这些免费服务提供的自动构建,自动化测试以及自动部署的功能。

   
次浏览       
相关文章

为什么要做持续部署?
剖析“持续交付”:五个核心实践
集成与构建指南
持续集成工具的选择-装载
相关文档

持续集成介绍
使用Hudson持续集成
持续集成之-依赖管理
IPD集成产品开发管理
相关课程

配置管理、日构建与持续集成
软件架构设计方法、案例与实践
单元测试、重构及持续集成
基于Android的单元、性能测试
最新课程计划
信息架构建模(基于UML+EA)3-21[北京]
软件架构设计师 3-21[北京]
图数据库与知识图谱 3-25[北京]
业务架构设计 4-11[北京]
SysML和EA系统设计与建模 4-22[北京]
DoDAF规范、模型与实例 5-23[北京]

集成与构建指南
项目管理:Maven让事情变得简单
持续集成工具hudson
持续集成
Maven权威指南
程序集(UML中的包)之间循环
更多...   


产品发布管理
配置管理方法、实践、工具
多层次集成配置管理
使用CC与CQ进行项目实践
CVS与配置管理
Subversion管理员


海航股份 重构及持续集成
电研华源 设计原理、建模与重构
软件配置管理日构建及持续集成
单元测试、重构及持续集成
中国软件研发中心 单元测试与重构
单元测试、重构和持续集成实践
罗克韦尔 C++单元测试+重构+Gtest
更多...