go 执行sh失败_为容器化的 Go 程序搭建 CI
本文介紹如何使用 Jenkins 的聲明式 pipeline 為一個簡單的 Golang web 應用搭建 CI 環境。如果你還不太了解 Jenkins 及其聲明式 pipeline,請先參考筆者的?Jenkins 系列文章(http://t.cn/E5sTkGM),或者直接到?Jenkins 官網進行學習。說明:本文的演示環境為 ubuntu 16.04。
準備 Jenkins 環境
鑒于篇幅原因,本文不再介紹 Jenkins 環境的搭建。本文演示的 demo 只要求 Jenkins server 連接了一個帶有 go 標簽的 agent,該 agent 上安裝了 docker:
如果你希望可以收到 CI 中的郵件通知,請配置 Jenkins 郵件通知中的 SMTP server。
demo 程序
筆者創建了一個簡單的 Golang web 程序用于演示,大家可以從這里下載該程序。
app.go
app.go 文件包含主程序,其內容如下:
)func getNameLen(name string) int {return len(name)
}func sayHello(w http.ResponseWriter, r *http.Request) {
? ?message := r.URL.Path
? ?message = strings.TrimPrefix(message, "/")
? ?message = "Hello " + message + " : " + fmt.Sprintf("%d", getNameLen(message))
? ?w.Write([]byte(message))
}func main() {
? ?http.HandleFunc("/", sayHello)if err := http.ListenAndServe(":8088", nil); err != nil {panic(err)
? ?}
}
該程序的功能非常簡單,如果你在 url 中域名后面的部分添加了自己的名字,它會向你問好并計算出你名字的長度:
app_test.go
app_test.go 文件包含了函數 getNameLen() 的單元測試:
package mainimport ("testing")func Test_GetNameLen_1(t *testing.T) {if l := getNameLen("nick"); l != 4 {
? ? ? ?t.Error("test failed, the length of nick is not correct.")
? ?} else {
? ? ? ?t.Log("test passed.")
? ?}
}func Test_GetNameLen_2(t *testing.T) {if l := getNameLen(""); l != 0 {
? ? ? ?t.Error("test failed, the length of empty string is not correct.")
? ?} else {
? ? ? ?t.Log("test passed.")
? ?}
}
Dockerfile
Dockerfile 文件用于構建 docker 鏡像,其內容如下:
FROM golang:1.11.0WORKDIR /go/src/gowebdemo/
RUN go get -d -v golang.org/x/net/html
COPY app.go .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o gowebdemo .
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=0 /go/src/gowebdemo .
EXPOSE 8088
CMD ["./gowebdemo"]
在準備好上面的內容后,讓我們開始 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: "gopro build failed
Project: ${env.JOB_NAME}
Build Number: ${env.BUILD_NUMBER}
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: "gopro build success
Project: ${env.JOB_NAME}
Build Number: ${env.BUILD_NUMBER}
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 內容如下:
#!/bin/bashset -xgo get -d -v golang.org/x/net/html
go get -u github.com/jstemmer/go-junit-report
go test -v 2>&1 > tmp
status=$?$GOPATH/bin/go-junit-report < tmp > test_output.xmlexit ${status}
該腳本執行單元測試操作,并把運行單元測試命令的結果作為腳本運行的結果返回。這一點很重要,我們就是通過這種方式來知道單元測試是否完全通過,如果沒有完全通過就讓該次持續集成失敗,而停止后續的操作。同時使用 go-junit-report 組件把單元測試的結果保存為 junit 格式的文件 test_output.xml。junit '*.xml' 則可以分析該單元測試的結果,并以圖表的方式展示:
stage('Build')
該部分執行腳本 buildapp.sh,其內容如下:
#!/bin/bashset -exdocker build -t gowebdemo .# remove all none tag imagesif [ ! -z "$(docker images -q --filter 'dangling=true')" ]; then
?docker rmi $(docker images -q --filter "dangling=true")fi
首先執行 docker build -t gowebdemo . 命令,以 Dockerfile 中的指令構建應用程序并打包為容器鏡像。然后移除系統中沒有標簽的鏡像釋放磁盤空間。
在比較正式的環境中,一般會把構建好的容器鏡像推送到私有的鏡像庫中,這里為了簡化過程,就把鏡像存放在 agent 上,并在下一步中在 agent 上部署一個應用的實例。
stage('DeployApp')
該部分執行腳本 deployapp.sh,腳本的內容如下:
#!/bin/bashset -ex# remove the current app instanceif [ -n "$(docker ps -aq -f name=nickwebdemo)" ]; then? ?docker rm -f nickwebdemofi# run a new app instance
docker run -d \
? ?-p 8088:8088 \
? ?--name nickwebdemo \
? ?--restart=always \
? ?gowebdemo
腳本先檢查是不是已經有同名的容器實例,如果有就先刪除掉該實例,然后運行一個新的實例。
在最后的 post 部分,我們根據該次持續集成的狀態來發送不同的郵件通知,比如整個過程沒有錯誤發生,單元測試也都通過了,就發送成功的通知,否則發送失敗的通知。
配置 Jenkins Job
在 Jenkins 中創建 pipeline 類型的 Job,并設置從 SCM 獲得 pipeline 腳本:
因為筆者放置代碼的庫是公開的,所以只用指定代碼庫的路徑就可以了,不需要添加相關的認證信息。
現在就可以觸發 CI 過程了,下圖是筆者機器上運行完成后的截圖:
雖然只有兩個單元測試的 case,但顯示的結果還不錯!并且 web app 被成功的部署到了 agent 上。
checkin 代碼進行演示
下面我們配置 Jenkins 每隔一分鐘檢查一次代碼是否有變更,有的話就觸發 CI。在 Build Triggers 中選擇 Poll SCM,然后輸入 5 個由空格分隔的 * 號:
保存該的配置,接下來讓我們添加一個單元測試的 case:
func Test_GetNameLen_3(t *testing.T) {if l := getNameLen("andrew"); l != 5 {? ? ? ?t.Error("test failed, the length of andrew string is not correct.")
? ?} else {
? ? ? ?t.Log("test passed.")
? ?}
}
這里筆者故意算錯了字符串 "andrew" 的長度,checkin 這段代碼,然后看看 Jenkins 中持續集成的過程:
持續集成的過程被自動觸發了,但是由于單元測試中有失敗的 case 導致整個過程都失敗了,并且單元測試后面的過程都沒有被執行:
如果你正確配置了郵件服務器并且把 Jenkinsfile 中的郵件地址改成的你自己的郵件地址,那么不管持續集成是成功還是失敗你都會收到相關的通知。現在把失敗的單元測試修改正確,再提交一次,這樣就開啟了我們的持續集成之旅!
總結
本文只是介紹了一個非常簡單的 demo 場景,但是一旦一個簡單的環境能夠運行起來了,你就可以不斷的往上添磚加瓦,比如創建集成測試的環境,添加集成測試,并最終銷毀集成測試環境等內容,最終讓它成為一個能夠滿足需求的持續集成流水線。
參考:
Building a CI for Golang test(https://blog.codingecho.com/2018/05/30/building-a-ci-for-golang-test/)
Building a CI system for Go, with Jenkins(https://medium.com/@zarkopafilis/building-a-ci-system-for-go-with-jenkins-4ab04d4bacd0)
出處|http://t.cn/E5sjhEj
這是一條正經的分割線docker+k8s 課程第一天視頻:
鏈接: https://pan.baidu.com/s/1_R26fmIZ1aC4KiiW0owObw?
提取碼: sxbg?
51Reboot 2019 最新課程招生信息
Python 零基礎入門課程
此課程為面授班和網絡班,一共 15 個課時,每周上一個全天,歷時4個月。附加:錄播視頻+筆記+答疑2019-6月份開課Python 自動化運維進階課程
此課程為面授班和網絡班,一共 15 個課時,每周上一個全天,歷時4個月。附加:錄播視頻+筆記+答疑2019-4月份開課現在報名即可享受早鳥價
關于課程的具體內容想要了解的, 掃碼加小助手咨詢
www.51reboot.com
任何問題掃碼咨詢長按掃碼可咨詢
總結
以上是生活随笔為你收集整理的go 执行sh失败_为容器化的 Go 程序搭建 CI的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 葱须煮水的功效与作用、禁忌和食用方法
- 下一篇: 白胡椒粉的功效与作用、禁忌和食用方法