七叶笔记 » golang编程 » 为容器化的 Go 程序搭建 CI

为容器化的 Go 程序搭建 CI

本文介绍如何使用 Jenkins 的声明式 pipeline 为一个简单的 Golang web 应用搭建 CI 环境。如果你还不太了解 Jenkins 及其声明式 pipeline, 请先参考笔者的 Jenkins 系列文章( ,或者直接到 Jenkins 官网( 进行学习。说明:本文的演示环境为 ubuntu 16.04。

准备 Jenkins 环境

鉴于篇幅原因,本文不再介绍 Jenkins 环境的搭建。本文演示的 demo 只要求 Jenkins server 连接了一个带有 go 标签的 agent,该 agent 上安装了 docker

如果你希望可以收到 CI 中的邮件通知,请配置 Jenkins 邮件通知中的 SMTP server。

demo 程序

笔者创建了一个简单的 Golang web 程序用于演示,大家可以从这里下载该程序。

app.go

app.go 文件包含主程序,其内容如下:

该程序的功能非常简单,如果你在 url 中域名后面的部分添加了自己的名字,它会向你问好并计算出你名字的长度:

app_test.go

app_test.go 文件包含了函数 getNameLen() 的单元测试:

Dockerfile

Dockerfile 文件用于构建 docker 镜像,其内容如下:

在准备好上面的内容后,让我们开始 CI 的配置。

Jenkinsfile

为了实现 pipeline as code,我们把配置 Jenkins 的 pipeline 内容保存到 Jenkinsfile 文件中,并和代码一起 checkin 到代码中。该 demo 的 Jenkinsfile 内容如下:

pipeline {
 agent {
 label 'go'
 }
 stages {
 stage('UnitTest') {
 steps {
  script  {
 if( sh(script: 'docker run --rm -v $(pwd):/go/src/gowebdemo -w /go/src/gowebdemo golang:1.11.0 /bin/bash -c "/go/src/gowebdemo/rununittest.sh"', returnStatus: true ) != 0 ){
 currentBuild.result = 'FAILURE'
 }
 }
  junit  '*.xml'
 script {
 if( currentBuild.result == 'FAILURE' ) {
 sh(script: "echo unit test failed, please fix the errors.")
 sh "exit 1"
 }
 }
 }
 }
 stage('Build') {
 steps {
 sh './buildapp.sh'
 }
 }
 stage('Deploy') {
 steps {
 sh './deployapp.sh'
 }
 }
 }
 post {
 failure {
 mail bcc: '', body: "<b>gopro build failed</b><br>Project: ${env.JOB_NAME} <br>Build Number: ${env.BUILD_NUMBER} <br> URL de build: ${env.BUILD_URL}", cc: '', charset : 'UTF-8', from: '', mimeType: 'text/ html ', replyTo: '',  subject : "ERROR CI: Project name -> ${env.JOB_NAME}", to: "your email address";
 }
 success {
 mail bcc: '', body: "<b>gopro build success</b><br>Project: ${env.JOB_NAME} <br>Build Number: ${env.BUILD_NUMBER} <br> URL de build: ${env.BUILD_URL}", cc: '', charset: 'UTF-8', from: '', mimeType: 'text/html', replyTo: '', subject: "SUCCESS CI: Project name -> ${env.JOB_NAME}", to: "your email address";
 }
 }
}

 

label ‘go’

agent 中的 label 指定该 pipeline 运行在带有 go 标签的 agent 上。

stage(‘UnitTest’)

该部分运行代码中的单元测试,并根据单元测试的结果确定是否继续执行后面的流水线操作。其中的脚本文件 rununittest.sh 内容如下:

该脚本执行单元测试操作,并把运行单元测试命令的结果作为脚本运行的结果返回。这一点很重要,我们就是通过这种方式来知道单元测试是否完全通过,如果没有完全通过就让该次 持续集成 失败,而停止后续的操作。同时使用 go-junit-report 组件把单元测试的结果保存为 junit 格式的文件 test_output.xml。junit ‘*.xml’ 则可以分析该单元测试的结果,并以图表的方式展示:

stage(‘Build’)

该部分执行脚本 buildapp.sh,其内容如下:

首先执行 docker build -t gowebdemo . 命令,以 Dockerfile 中的指令构建应用程序并打包为容器镜像。然后移除系统中没有标签的镜像释放磁盘空间。

在比较正式的环境中,一般会把构建好的容器镜像推送到私有的镜像库中,这里为了简化过程,就把镜像存放在 agent 上,并在下一步中在 agent 上部署一个应用的实例。

stage(‘DeployApp’)

该部分执行脚本 deployapp.sh,脚本的内容如下:

脚本先检查是不是已经有同名的容器实例,如果有就先删除掉该实例,然后运行一个新的实例。

在最后的 post 部分,我们根据该次持续集成的状态来发送不同的邮件通知,比如整个过程没有错误发生,单元测试也都通过了,就发送成功的通知,否则发送失败的通知。

配置 Jenkins Job

在 Jenkins 中创建 pipeline 类型的 Job,并设置从 SCM 获得 pipeline 脚本:

因为笔者放置代码的库是公开的,所以只用指定代码库的路径就可以了,不需要添加相关的认证信息。

现在就可以触发 CI 过程了,下图是笔者机器上运行完成后的截图:

虽然只有两个单元测试的 case,但显示的结果还不错!并且 web app 被成功的部署到了 agent 上。

checkin 代码进行演示

下面我们配置 Jenkins 每隔一分钟检查一次代码是否有变更,有的话就触发 CI。在 Build Triggers 中选择 Poll SCM,然后输入 5 个由空格分隔的 * 号:

保存该的配置,接下来让我们添加一个单元测试的 case:

这里笔者故意算错了字符串 “andrew” 的长度,checkin 这段代码,然后看看 Jenkins 中持续集成的过程:

持续集成的过程被自动触发了,但是由于单元测试中有失败的 case 导致整个过程都失败了,并且单元测试后面的过程都没有被执行:

如果你正确配置了邮件服务器并且把 Jenkinsfile 中的邮件地址改成的你自己的邮件地址,那么不管持续集成是成功还是失败你都会收到相关的通知。

现在把失败的单元测试修改正确,再提交一次,这样就开启了我们的持续集成之旅!

总结

本文只是介绍了一个非常简单的 demo 场景,但是一旦一个简单的环境能够运行起来了,你就可以不断的往上添砖加瓦,比如创建 集成测试 的环境,添加集成测试,并最终销毁集成测试环境等内容,最终让它成为一个能够满足需求的持续集成流水线。

参考:

Building a CI for Golang test ()

Building a CI system for Go, with Jenkins ()

出处|

相关文章