17_Go CI WITH Go & Docker on Gitlab
========================
###### tags: `Go` `2019鐵人賽`
往往需求派下來了,
我們把程式寫完了, 或者Dockerfile(or docker-compose)寫好了.
總要推上版控的, 應該大部分都是Git吧!!
去年開始接觸Gitlab的我, 也開始接觸CI這部份的操作.
# CI - Continuous Integration 持續整合
以前沒有CI的時候, 大家寫完程式, 到開始作整合編譯.
可能都是一兩週後的事情了, 甚至一個月.
因為以前大家只關心自己的部份是否能正常, 並不在意整合在一起是否能正常編譯.
直到很後面才會來煩惱修正.

所以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的功能

## .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有東西出現了.


途中的`a216460a`就是我們剛剛commit的log.
Pipeline就是yml中的stages, 裡面寫的都是各個Job.
接著定義Job的內容
gobuild這job是屬於build stage的job
gotest這job是屬於test stage的job.
一個stage可以定義多個job.

接著來說明這`.gitlab-ci.yml`的內容
gitlab-ci就是借助docker的方式來進行編譯.
所以一開始要告訴它編譯的基底image.
然後有些各job要共用的資料我就設定成[variable]
before_script 是用來定義所有job執行前要執行的命令.
script, 就是執行該job的腳本或是命令.
artifacts, 執行成功後, 編譯出來的產出物.
expire_in, 就是設置該artifact被上傳到Gitlab開始的儲存時間.
內容其實就都是我們平常輸入的指令而已
把他們放在對應的階段, 如果是同階段, 不同順序.
yaml內的`-` 表示的是陣列, 就以`-`開頭空一格後, 把指令放後面.
跑完後, 應該會有artifact能下載了, 解壓縮開來會是我們指定平台的Go二進制檔,
這時候就能人工拿去佈署了XD

# Gitlab CI + Docker Hub

昨天不是寫好了Dockerfile(或寫的是docker-compose)?
Gitlab一樣能對docker做CI.
## [Docker Hub](https://hub.docker.com/)
先來這裡註冊並且開個repository.

記著自己的username跟repository name .
先來Gitlab 的Settings -> CI/CD -> Variables這裡
設定docker hub帳號

這裡的變數會被帶到`.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
```


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/)上吧.
