Golang x CI x CD

Posted by Kakashi on 2016-07-03

前言

最近公司大刀闊斧採用 Golang 重寫架構,話說 CI & CD 這件事在軟體開發中已經算是少不了的一環,而從以前弄 python、 nodejs 一路到 Golang,好像都缺了一篇筆記,以至於常常踩了雷又開始回想到底是怎麼回事,所以才有這篇文章稍微紀錄一下,如何串這些工具。

啊啊啊,使用到的 DevOps 工具

流行用語還是要提一下!!!

  • Git 我最熟悉的版本控制系統
  • Github 全世界最大工程師社交平台
  • Jenkins 老爺爺來幫你做 CI & CD,叫他幫你做 build code, testing, 和 deploy 就對了啊(握拳)
  • Docker 載得動很多鐵箱子的鯨魚(實在厲害)

一切的起源,設定 Github 與 jenkins 的連動

公司專案習慣使用 Github flow,依照慣例要發出 Pull Request (PR) 給同事 Review 前,需要確保這個 PR 不會讓 Staging branch 爛掉,這邊就需要採用老爺爺的力量了,流程如下:

  1. 起手式,要確保 Jenkins 上面需要的 plugins 有裝好

  2. 在 Jenkins 上面建立一個 ProjectName-PR 的專案,在原始碼控制那邊填上 Github 的網址

    jenkins_config

    記得將 Branches to build 這邊改成 ${ghprbActualCommit}

  3. 在 jenkins -> config settings 那邊修改 GitHub Pull Request Builder ,記得要使用可以讀取該 Repo 的帳號,增加好後可以用 Test Credentials 去測試到底能不能連接。

    jenkins_pull_request_builder

  4. 在建構觸發程序那邊,使用 Github pull request builder

    jenkins_trigger

  5. 在 Github 中設定好 jenkins 的 webhook

    github_webhook

    記得選擇 trigger by Pull request (很重要!!!!)

  6. 接著就可以發個 PR 看看 jenkins 有沒有反應惹!

Golang 的 testing & coverage

我們公司的 Golang project 的建置,基本上是用 makefile 去管理,這邊有如何產生 testing & coverage 的飯粒:

1
2
3
4
5
6
7
8
9
testgen:
go get github.com/tebeka/go2xunit
go test $(TEST_PACKAGES) -v | tee test_output
go2xunit -fail -input test_output -output tests.xml

covgen:
go get github.com/axw/gocov/gocov
go get github.com/AlekSi/gocov-xml
gocov test $(TEST_PACKAGES) | gocov-xml > coverage.xml

使用 go2xunit 套件產生 Jenkins/Hudson 可以吃的 xunit report,另外用 gocov & gocov-xml 產生 Cobertura 格式,但實測結果發現只能產生 coverage 的 % 數,卻沒辦法看到是哪一行,這邊還要找時間修一下。

搭配 Docker 產生 report

在 Jenkins 裡面的建置使用鯨魚招式:

1
2
3
4
5
docker run -v $WORKSPACE:/mnt/$WORKSPACE -w /mnt/$WORKSPACE --rm=true golang:1.6-onbuild bash -c "\
make clean; \
make; \
make test; \
make covgen;"

最後把 report 生出來並且將狀態打回 github & slack 上。


進入 CD 的節奏

昨天才看到一段話覺得很有意思

continuous delivery doesn’t mean every change is deployed to production ASAP.
It means every change is proven to be deployable at any time.

所以我們都要確保 testing 的品質高上大,程式碼壯壯才能真正達成 CD 這塊,而不是有做成自動化就好了!

流程

PR merged 進 Dev branch 後 -> Github 通知 Jenkins 產生 Staging 的 Image -> 部署到 Staging server 上

會在 Jenkins 上面再開一個專案 ProjectName-staging 處理這個 task,記得 github 也要加上新的 webhook

github_webhook

Jenkins 上面的建置觸發程序使用 Build when a change is pushed to GitHub

建置 Golang image

建構 Golang image 其實是個蠻有趣的題目,在參考 building-minimal-docker-containers-for-go-applications 後,我們也決定使用 static build 的 golang binary ,並且建構極小的 golang image,一來可以達成縮小 image 佔用的空間,二來可以達成快速部署的功效。

我們一樣可以用剛剛樓上那招,只是 build binary 的時候加上以下參數

1
CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main .

並且將 Dockerfile 寫成

1
2
3
FROM scratch
ADD main /
CMD ["/main"]

就可以產生出極小的 docker images (大約10 ~ 20MB),接著把這個 image 打上 jenkins build tag & latest tag 後,推上 docker registry。

部署

我們採用 Jenkins 的 Send build artifacts to SSH Plugin 連結到 staging server,並且讓他重拉新的 image,接著重啟該 container ,如此一來就完成整個 CI/CD 的流程。

後記

這篇文章省掉很多 slack integration 的部分,單純是因為作者太懶,而這方面最近蠻流行的,大家一定都比我還熟(誤),這篇文主要是幫自己記憶一下該做哪些事情,如果有哪邊寫漏了,可以留言告知喔,感謝大家<($ $)>