17_Go CI WITH Go & Docker on Gitlab ======================== ###### tags: `Go` `2019鐵人賽` 往往需求派下來了, 我們把程式寫完了, 或者Dockerfile(or docker-compose)寫好了. 總要推上版控的, 應該大部分都是Git吧!! 去年開始接觸Gitlab的我, 也開始接觸CI這部份的操作. # CI - Continuous Integration 持續整合 以前沒有CI的時候, 大家寫完程式, 到開始作整合編譯. 可能都是一兩週後的事情了, 甚至一個月. 因為以前大家只關心自己的部份是否能正常, 並不在意整合在一起是否能正常編譯. 直到很後面才會來煩惱修正. ![](https://i.imgur.com/IJkSSOi.jpg) 所以Agile文化裡提出CI這概念, 希望程式有變動, 就趕緊作整合, 確保整個系統依然能正常運作, 也就是新增的部份, 並不會影響到原有的部份, 能持續且自動地進行驗證. 這驗證包含了 - 建置 build - 測試 test - 程式碼分析 source code analysis - 其他 **要談敏捷, 果然還是要寫自動測試先!!! 不然都是在打嘴泡** # [Gitlab-CI](https://docs.gitlab.com/ee/ci/) > [和艦長一起 30 天玩轉 GitLab ](https://ithelp.ithome.com.tw/users/20120986/ironman/2733), 今年剛好有社團大神寫Gitlab介紹的文 Gitlab有提供CI/CD的功能 ![](https://i.imgur.com/1SZoLSM.png) ## .gitlab-ci.yml 寫個簡單的Gin程式, 然後推上gitlab repo. `main.go` ```go=1 package main import ( "github.com/gin-gonic/gin" ) func main() { r := gin.Default() r.GET("/ping", func(c *gin.Context) { c.JSON(200, gin.H{ "message": "pong", }) }) r.Run() } ``` Push成功之後, 先去該專案左側的Settings -> [Runners](https://docs.gitlab.com/runner/install/) 做設定, 這步是讓自己的某台電腦成為Gitlab CI用的build server. https://docs.gitlab.com/runner/install/ 然後, 在專案根目錄下新增`.gitlab-ci.yml` ```yaml= image: golang:latest variables: ARTIFACTS_DIR: artifacts GO_PROJECT: gitlab.com/username/projectname before_script: - go version - export GO111MODULE=on - mkdir -p ${CI_PROJECT_DIR}/${ARTIFACTS_DIR} stages: - build - test gotest: stage: test script: - go test ./... gobuild: stage: build script: - go build -o ${CI_PROJECT_DIR}/${ARTIFACTS_DIR}/main artifacts: paths: - ${ARTIFACTS_DIR} expire_in: 1h ``` Push 上遠端後, 會看到CI/CD -> Pipelines 跟 Jobs有東西出現了. ![](https://i.imgur.com/GgBDF4I.png) ![](https://i.imgur.com/4ZeEKHY.png) 途中的`a216460a`就是我們剛剛commit的log. Pipeline就是yml中的stages, 裡面寫的都是各個Job. 接著定義Job的內容 gobuild這job是屬於build stage的job gotest這job是屬於test stage的job. 一個stage可以定義多個job. ![](https://i.imgur.com/n7pa2xy.png) 接著來說明這`.gitlab-ci.yml`的內容 gitlab-ci就是借助docker的方式來進行編譯. 所以一開始要告訴它編譯的基底image. 然後有些各job要共用的資料我就設定成[variable] before_script 是用來定義所有job執行前要執行的命令. script, 就是執行該job的腳本或是命令. artifacts, 執行成功後, 編譯出來的產出物. expire_in, 就是設置該artifact被上傳到Gitlab開始的儲存時間. 內容其實就都是我們平常輸入的指令而已 把他們放在對應的階段, 如果是同階段, 不同順序. yaml內的`-` 表示的是陣列, 就以`-`開頭空一格後, 把指令放後面. 跑完後, 應該會有artifact能下載了, 解壓縮開來會是我們指定平台的Go二進制檔, 這時候就能人工拿去佈署了XD ![](https://i.imgur.com/CIYUUZH.png) # Gitlab CI + Docker Hub ![](https://i.imgur.com/buraSrR.png) 昨天不是寫好了Dockerfile(或寫的是docker-compose)? Gitlab一樣能對docker做CI. ## [Docker Hub](https://hub.docker.com/) 先來這裡註冊並且開個repository. ![](https://i.imgur.com/3eI596n.png) 記著自己的username跟repository name . 先來Gitlab 的Settings -> CI/CD -> Variables這裡 設定docker hub帳號 ![](https://i.imgur.com/8QepP9x.png) 這裡的變數會被帶到`.gitlab-ci.yml`內. 然後要改一下gitlab-runner的設定, 加入`privileged = true` ``` [runners.docker] privileged = true ``` 來改寫`.gitlab-ci.yml` 這裡我使用docker-in-docker (dind)的方式 > [Gitlab Docker Build](https://docs.gitlab.com/ee/ci/docker/using_docker_build.html) ```yaml= image: docker:latest variables: DOCKER_HOST: tcp://docker:2375/ DOCKER_DRIVER: overlay2 # See https://github.com/docker-library/docker/pull/166 DOCKER_TLS_CERTDIR: "" IMAGE_TAG: tedmax100/gitlabtest:latest services: - name: docker:dind entrypoint: ["env", "-u", "DOCKER_HOST"] command: ["dockerd-entrypoint.sh"] before_script: - echo "${DOCKERHUB_PWD}" | docker login -u "${DOCKERHUB_USER}" --password-stdin stages: - deploy dockerdeploy: stage: deploy script: - docker build -t $IMAGE_TAG . - docker push $IMAGE_TAG ``` ![](https://i.imgur.com/9mwlylO.png) ![](https://i.imgur.com/ghgmdt6.png) Docker image被成功的推上去了 !! 接著之後都是Deploy server的事情了XD 未來再提到佈署的部份. # 結語 從Node轉到來Go花了點時間摸熟依賴包的關係. 還有goroutine跟channel. 畢竟Node主要思維是單執行緒的思考邏輯, 就也沒鎖(Lock)這類的需要作考量. 兩者都很簡單入門, 但為了分散式架構還是讓自己來學Go. 畢竟我在職場上是純後端XD 今年第一次參加鐵人賽, 能完賽真的很有成就感. 短跑比賽結束了, 但長跑比賽還在繼續. 明年打算繼續, 應該會帶著前端專案與Gin有更多串接. 感謝各位的閱讀與支持, 之後有更多文章會分享在[小弟個人網誌](https://tedmax100.github.io)上. 想學的技術太多, 應該會先鎖定在[Pixi](https://www.pixijs.com/)跟[ES](https://www.elastic.co/)上吧. ![](https://i.imgur.com/LxsC1zd.png)