【Rancher企业容器云平台】基于Docker的构建流程 (第一部分) - 持续集成及测试(上)

GemmaElphin 8年前

来自: http://dockone.io/article/1022

作者 : Usman Ismail, Bilal Sheikh (Rancher labs)

译者:雷伟VivianLei(Cloudsoar云舒网络)

Rancher Labs是一家容器技术基础设施提供商,总部位于美国硅谷,Rancher是一个高效易用的企业容器云平台。云舒网络为Rancher labs的战略合作伙伴 , 携手Rancher Labs 共同实践研究Docker容器生态圈。2016,Rancher | China 将会正式与大家见面!

在过去的一年里,我们写了很多文章,关于如何在docker上运行不同stack(译者注:在Rancher中,一个stack通常对应一个应用,它包含了一系列容器,及容器之间的关系描述),如:Magento, Jenkins, Prometheus等。然而,容器化部署不仅仅用于定义应用stack。在本系列文章中,我们将讨论端到端的开发流程,包括在流程的各个阶段如何平衡Docker和Rancher。具体涉及到:代码构建,测试,打包,持续集成及部署,以及在生产环境中管理应用stack。您也可以同时下载本系列的电子书。

{网址: 文档下载处 }

首先,我们从代码构建开始。一般来说,代码的构建/编译不是很大的问题,因为大部分语言和编译代码的工具已很清楚,且有很完善的文档说明。然后,随着项目和团队规模增长,模块之间依赖关系变得复杂,如何确保代码质量的同时,保证代码构建的一致性和稳定性,将成为更大的挑战。在本文中,我们将讨论如何用Docker去实现CI(持续集成)和测试的最佳实践。

一.构建系统扩展带来的挑战

首先,我们来看下维护构建系统所面临的一些挑战:

第一是依赖管理:当开发人员将库集成到源代码中时,需要注意的是,如何保证项目中所有模块使用的同一个库版本,且当库版本升级时,如何能及时将新版本提交到项目中的所有模块。

第二是环境依赖管理。包括IDE及IDE配置,工具版本(如maven, python版本)及相关配置,如代码静态分析规则,代码格式化模版。环境依赖管理的麻烦在于,项目中的不同模块会有依赖冲突,与代码级冲突不同的是,模块依赖冲突更难或是不能解决。比如,最近的一个项目里,我们用fabric进行自动化部署,用s75pxd上传Artifacts到Amazon S3。不幸的是,fabric的最新版本需要python 2.7,但s75pxd需要python2.6。如要兼顾两者,我们要么切换到s75pxd beta版本,要么用fabric的老版本。

最后,每个大型项目需要面临的一个主要问题是构建时间。随着项目规模扩大、复杂性增加,需要的语言也越来越多(我当前的项目使用Java, Groovy, Python和Protocol Buffers IDL)。测试中不同组件也会相互依赖,比如,测试时不能在同一时间运行共享数据库中相同的数据(译者注:同样的系统环境,同一共享数据库,不同人员同时测试时可能会引起数据冲突,后面实例会提到如添加同一用户时,会导致用户冲突)。这样,我们需要在测试执行前确保初始状态,测试完后再清理状态。这将减慢开发进度。

二. 解决方案和最佳实现

为解决上述问题,一个好的构建系统需要达到如下要求:

· 可重复性

q能在不同的开发机器和自动构建服务器中,生成/创建有一致依赖关系的构建环境。

· 集中化管理

所有开发机器和构建服务器的构建环境,是来自于同一个代码仓库中心或服务器,且环境设置能及时更新。

· 隔离性

项目的各个子模块相互隔离,而不是相互依赖。

· 并行性

并行构建子模块,提高构建效率。

【可重复性】

大多数语言和开发框架支持自动化依赖管理。如Maven用于Java,python用pip,ruby用bundler。这些工具比较类似,当你提交一个索引文件(pop.xml, requirements.txt或gemfile)到源代码控制器中,工具会自动下载相关依赖到构建机器中。我们测试后需集中管理这些文件,并提交到源代码控制服务器中。然而,仍需管理环境依赖,如是否安装了maven, python, ruby的正确版本。Maven自动检测依赖的更新,但对pip和bundler,我们必须通过脚本来触发更新。

【集中化管理】

为了安装依赖管理工具和脚本,大部分小团队通过文档来描述。当团队扩张时,如何保持实时依赖更新将会是关键。另外,构建环境的OS和平台不同,也会引起工具的安装差异。当然,你可以用配置管理工具,如puppet或chef(译者注:puppet和chef均为跨平台配置管理工具)去管理安装包的依赖和配置文件的设置;提前测试后再提交并推送到所有开发者。同时puppet和chef也有一些不足:

1. 安装配置不简单,且完整功能的版本都是收费的;

2. 有自己单独的语言进行配置(译者注:如chef基于ruby语言);

3. 配置管理工具不具备隔离功能,工具版本冲突和并行测试仍是一个问题;

【隔离性】

为保证组建隔离,减少构建时间,可以用虚拟机自动化系统,如vagrant(译者注:vagrant是一个基于Ruby的工具,用于创建和部署虚拟化开发环境)。vagrant 能创建并运行不同组建环境的虚拟机(盒子),这样可以保证隔离和并行测试。vagrant的配置文件提交到源代码控制器中,且推送到所有开发人员。另外,虚拟机(盒子)能用于测试,部署到Atlas,便于开发人员下载。不足在于:

1. 将需要更高层次的配置描述去设置vagrant;

2. 虚拟机也是一个非常重量级的隔离解决方案:虽然只需要一个测试运行环境或编译环境,但每个虚拟机运行了一个完整的OS和网络栈,还需提前分配内存和磁盘资源;

尽管有各种问题,用依赖管理工具(maven, pip, rake),配置管理工具(puppet, chef) 和虚拟化工具(vagrant),也能建立一个稳定、集中管理的构建系统,不是所有的项目都需要所有这些工具,但是任何长时间运行的大项目都需要自动化到这个程度。

三. 利用Docker进行系统构建

无需投入大量时间和资源,Docker和其生态系统能帮助我们支持上述工具。在本节中,我们将通过如下步骤来为应用创建集中化构建环境。

1. 集中化构建环境

2. 用docke打包应用

3. 用Docker compose生成构建环境

在本文及后续的文章中,为方便描述,我们将使用go-messenger作为示例应用。

在github上下载此应用。此系统的主要数据流如下图所示,由两个组件构成:RESTful认证服务器,用Golang所写;会话管理,用于接收来自客户端的TCP长连接和客户端之间的路由信息。本文将重点关注RESTful认证服务(go-auth)。它包含了多个无状态的web服务器和数据库集群,数据库集群用于存储用户信息。

  1. 集中化构建系统

    首先,需要创建一个包含了构建系统所需工具的容器镜像,此镜像的dockerfile如下所示,也可在此处下载。因为应用是用Go所写,所以我们是基于官方的golang镜像,安装了godep依赖管理工具。如果你的项目是用java语言,同样你可以基于java基本镜像,安装maven来代替godep。

然后添加一个编译脚本,包含了构建和测试代码的过程。脚本如下所示:

1. 使用godep restore下载依赖包;

2. 用go fmt格式化源码;

3. 用go test运行测试;

4. 用go build编译项目;

为确保可重复性,我们用docker构建一个有版本的容器镜像,可以从Dockerhub上下载此镜像,或通过Dockerfile构建。至此,所有的开发人员(和构建机器)都能通过此容器,用以下命令来构建任何go工程。

上面这条命令,我们运行了一个docker,镜像为usman/go-builder,版本1.4。且通过-v 将源代码mount到容器中,通过-e 配置了环境变量SOURCE_PATH。在此示例工程中,为测试go-builder,你可用以下命令来产生一个名为 go-auth的可执行文件,存放于go-auth工程的root目录下:

隔离构建工具带来的另一个好处是,可以很容易更换构建工具和其配置。如在上面的例子里,我们用的是golang1.4,用以上命令将go-build:1.4更改为go-build:1.5,可以很快测试本工程中是否能使用golang1.5。

为集中管理镜像,我们可以将此构建容器的最新版本设置为固定版本,这样所有开发者可直接使用go-builder:latest 来构建源代码。如果工程中用到构建工具的不同版本,使用不同的容器构建即可,不用担心在一个构建环境管理多个版本的问题。比如,用支持多版本的官方python镜像,可以解决前面的python问题(译者注:上文所描述的fabric和s75pxd对python不同版本的依赖问题:“fabric的最新版本需要python 2.7,但s75pxd需要python 2.6”)。

  1. 用Docker打包应用

    如果你想将二进制打包到容器,添加如下内容的 Dockerfile,运行“docker build -t go-auth”即可。在dockerfie中,将二进制输出到一个新的容器;暴露9000端口来接入连接;配置运行二进制的入口参数。因为Go二进制是自包含的,我们用现有ubuntu镜像即可。如你的项目需要一些依赖包,也可一同打包进容器。如生产一个war文件时就用tomcat容器。

  1. 用Docker compose创建build环境

    到现在为止,我们已经完成项目构建、实现可重复性、集中管理且隔离各种组件。我们还可以将构建流程扩展到集成测试中,这也突出了docker并行化加速构建的能力。

    测试不能并行的一个主要原因在于共享数据库。在本示例项目中,用MySQL存储用户信息,也存在着类似的问题。测试新用户注册时,第一次测试注册新用户,当运行第二次测试时,由于注册相同的用户而导致用户冲突错误。这就只能在完成一次测试后,清空注册用户再开始新一轮测试。

为设置隔离的并行构建,我们可以定义docker compose模版(docker-compose.yml),如下所示。其中定义了一个数据库服务,使用MySQL官方镜像并配置了一些环境变量。然后产生了一个GoAuth服务,用已将应用打包的容器,并将数据库容器连接到此容器中。

通过运行docker-compose up,先将应用环境跑起来,然后通过运行如下curl命令来模拟集成测试,第一次运行会返回200表示成功,第二次将返回409表示冲突。最后,运行docker-compose rm来清理应用环境。

为了运行应用多个相互独立的版本,需要更新docker compose模版,添加相同配置的服务database1和Goauth1,Goauth1唯一需要更改的是端口从9000:9000到9001:9000,保证应用暴露的端口不相冲突。完整的模版可在此下载。当再运行docker-compose up时,就能并行的运行两个集成测试了。同样的,当工程有多个独立的子模块时,此并行化的方式也可用于加速构建系统中,如多模块的maven工程。

(未完待续)

====================================

【温馨提示】

Rancher | China 推出" 【Rancher | 实战群】 ",群内汇集了Rancher中国最强技术精英团队及业内技术派高人,宗旨是为了大家实时与Rancher创始团队面对面,交流Rancher实战技术!同时欢迎各位分享自己的经验、疑难问题,我们将定期邀请分享嘉宾做各类话题分享及回顾,共同实践研究Docker容器生态圈。

对Rancher和Docker技术感兴趣、或对本文中细节需继续探讨的朋友,欢迎加入本群参与讨论,欢迎大家加入!

*加群方法:

关注【云舒网络】公众号

留言”我要加群“