[教學] 用Drone, Kubernetes跟Helm,以及RBAC來建置你的CI/CD流程 — Part 2
CI/CD with Drone, Kubernetes and Helm — Part 2
有陣子沒寫文章,翻譯點東西來練習練習。最近公司要導入Drone/K8s/Helm,加上本來公司大多數的解決方案都放在Google Cloud 上,於是開始動手在新的專案做測試。不過這幾個解決方案都算新的,也很難找到適合的說明一次全部到位。剛好看到 這篇 在介紹這個組合,不但救了我幾天的時間,還幫我暖了一下這幾個技術的概念,聯絡了一下作者,決定來翻譯這篇文章。不過應該很難避免加入自己的話啦,英文的部分除非比較常見的翻譯法,否則還是以原文為主。另外原作者列了許多參考連結,在此就沒有再針對那些連結在做翻譯了

介紹
這是系列的第二篇,第一篇請看此。在第一篇的時候我們看到怎麼開始一個k8s的叢集、怎麼部署Tiller、怎麼運用Helm跟Tiller互動,並且部署一個Drone的服務。我們也用cert-manager讓我們的Drone支援HTTPS
在這篇文章我們會看到怎麼用Go的專案來建立一個有效的工作流程(Pipeline),如何建置一個docker的鏡像(image)並且根據不同的事件透過我們的Drone把他推送到Google Cloud Registry
Go Project
Simple Project
TL;DR: 你可以看原作者提供的Sample程式 here. (註,其實你也可以隨意找一個docker化的專案)
我們會在一個測試用的go專案上實作。所以就先在你的版控系統上先開一個新的Repo,clone之後開一個main.go的檔案
package mainimport (
"net/http" "github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
)func main() {
r := gin.Default() r.GET("/health", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"alive": true})
}) if err := r.Run("127.0.0.1:8080"); err != nil {
logrus.WithError(err).Fatal("Couldn't listen")
}
}
這個專案會新建一個簡單的伺服器,監聽 127.0.0.1:8080,然後有個路由/health永遠回覆200
Tooling and Docker
就像前面文章所提到的,我們會用dep來處理我們的go 相依套件然後我們會有一個多步驟的Dockerfile
首先,我們就先安裝dep,然後在我們的repo裡初始化
$ go get -u github.com/golang/dep/cmd/dep
$ dep init
Locking in (a5b47d3) for transitive dep gopkg.in/yaml.v2
Locking in master (22d885f) for transitive dep github.com/gin-contrib/sse
Locking in (c88ee25) for transitive dep github.com/ugorji/go
Locking in v8.18.1 (5f57d22) for transitive dep gopkg.in/go-playground/validator.v8
Locking in master (1a580b3) for transitive dep golang.org/x/crypto
Locking in master (7c87d13) for transitive dep golang.org/x/sys
Locking in (57fdcb9) for transitive dep github.com/mattn/go-isatty
Using ^1.2.0 as constraint for direct dep github.com/gin-gonic/gin
Locking in v1.2 (d459835) for direct dep github.com/gin-gonic/gin
Locking in (5a0f697) for transitive dep github.com/golang/protobuf
Using ^1.0.5 as constraint for direct dep github.com/sirupsen/logrus
Locking in v1.0.5 (c155da1) for direct dep github.com/sirupsen/logrus現在我們有兩個多的檔案:Gopkg.lock 跟 Gopkg.toml。我們也有一個vendor目錄。好,我們的套件就初始化好了,接下來就建立我們的 Dockerfile:
# Build step
FROM golang:latest AS buildRUN mkdir -p $GOPATH/src/github.com/Depado/dummy
ADD . $GOPATH/src/github.com/Depado/dummy
WORKDIR $GOPATH/src/github.com/Depado/dummy
RUN go get -u github.com/golang/dep/cmd/dep
RUN dep ensure -vendor-only
RUN CGO_ENABLED=0 go build -o /dummy# Final step
FROM alpineRUN apk update
RUN apk upgrade
RUN apk add ca-certificates && update-ca-certificates
RUN apk add --update tzdata
RUN rm -rf /var/cache/apk/*COPY --from=build /dummy /home/
ENV TZ=Europe/Paris
WORKDIR /home
ENTRYPOINT ./dummy
EXPOSE 8080
我們也在我們的.gitignore裡面把vendor/給去除,避免每次都會全部複製過去一次。(註:要不要在git裡面放vendor這件事有蠻多主張的,在go還沒提出更好的套件管理方法之前,就先觀望一下吧)
$ docker build .
...
Successfully built 9a763d6aa971
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
<none> <none> 9a763d6aa971 1 minutes ago 23.5MB很好,現在我們有一個小型的docker映像,裡面有我們的go程式,而且也準備可以把它部署到k8s上了
Drone 工作流
基本工作流
就像前一篇文章提到的,Drone的做法跟Travis一樣,你在Repo的根目錄建立一個.drone.yml
workspace:
base: /go
path: src/github.com/Depado/dummypipeline:
prerequisites:
image: "golang:latest"
commands:
- go version
- go get -u github.com/golang/dep/cmd/dep
- dep ensure -vendor-only
build:
image: "golang:latest"
commands:
- go build
這是一個最基本包含dep的工作流。第一步,先使用golang:latest這個映像檔、顯示go的版本、安裝dep、安裝套件。第二步就是建置我們的程式
Linter
(註,因為我個人用了另外的工具,所以linter這段我並沒有實作)這個部分我們會使用兩個linter,一個是gometalinter,這是一個蠻多人使用的linter,另外一個是 golangci-lint,這個比較新,但他的效能跟gometalinter相比快非常多,所以就自己選一個吧
gometalinter
linter:
image: "golang:latest"
commands:
- go get -u github.com/alecthomas/gometalinter
- gometalinter --install --force
- gometalinter --disable=gotype --vendor --deadline=5m ./...golangci-lint
linter:
image: "golang:latest"
commands:
- go get -u github.com/golangci/golangci-lint/cmd/golangci-lint
- golangci-lint run意見
雖然原作者喜歡gometalinter,但他用了golangci-lint之後就沒有回去了。輸出的結果更好、速度更快,所以他個人強烈建議後者
推送到GCR
設定
這個部分我們會開始用Drone,以及Drone的密鑰。首先你要先確認你安裝了drone CLI, 而且你正確的設定好了。你可以執行下面的程式來確定你已經設定好了
$ drone info
User: you
Email: you@yourmail.comGoogle Container Registry是一個私有的Docker容器雲,我們會拿它來放我們建立好的docker映像檔。因為他是私有的,所以我們必須要給予他必要的權限,方法就是透過一個服務帳戶的方式。這個帳戶並不會放在我們的k8s叢集裡,而是會被我們拿來授權給我們的Drone
所以首先先到 IAM Console 來建立一個新的服務帳戶。你可以自由的命名它,但必須給他Storage Admin這個權限,點選產生新JSON key,下載他、並且儲存他。

Latest
接下來我們會用一個Drone叫做 Google Container Registry plugin的plugin 。
所以接下來就把這個步驟新增到我們的.drone.yml吧
gcr:
image: plugins/gcr
repo: project-id/dummy
tags:
- latest
- "${DRONE_COMMIT_SHA}"
secrets: [google_credentials]
when:
event: push
branch: master記得把上面的project-id換成你自己的專案ID,這個步驟表示的是:
如果一個commit被push到master的branch,並且推送到我們的GCR,我們會使用google_credentials密鑰來取用他。我們目前還沒有建立 google_credentials 這個密鑰。所以回到你的terminal,我們用drone的工具在新增這個密鑰到我們的drone(註:免費版的Drone強迫你每個Repo的密鑰要各自設定,超級惱人),並且我們限制他只會在這個plugins/gcr這個映像裡面被使用
$ drone secret add --image plugins/gcr --repository Depado/dummy \
--name google_credentials --value @your_key.json接下來我們可以commit我們的檔案然後測試他了,如果沒意外的話,你會在你的GCR裡面看到一個建立好的映像檔,並且有一個latest tag
Release
現在我們把我們的映像檔上傳到GCR了,那如果我們想要處理tag時候的情形呢?我們也可以用底下的方式來做一樣的行為
tagged_gcr:
image: plugins/gcr
repo: project-id/dummy
tags:
- "${DRONE_TAG##v}"
- "${DRONE_COMMIT_SHA}"
- latest
secrets: [google_credentials]
when:
event: tag
branch: master現在這個流程只會在我們有tag在我們的commit的時候執行,他也會在這個映像檔裡面加入${DRONE_TAG##v} tag。這個意思是Drone會把v這個詞移除掉,例如我們tag某個commit叫v1.0.1,在映像檔的tag會叫做1.0.1
總結
現在你的 .drone.yml看起來會像這樣
workspace:
base: /go
path: src/github.com/Depado/dummypipeline:
prerequisites:
image: "golang:latest"
commands:
- go version
- go get -u github.com/golang/dep/cmd/dep
- dep ensure -vendor-only linter:
image: "golang:latest"
commands:
- go get -u github.com/golangci/golangci-lint/cmd/golangci-lint
- golangci-lint run build:
image: "golang:latest"
commands:
- go build gcr:
image: plugins/gcr
repo: project-id/dummy
tags:
- latest
- "${DRONE_COMMIT_SHA}"
secrets: [google_credentials]
when:
event: push
branch: master tagged_gcr:
image: plugins/gcr
repo: project-id/dummy
tags:
- "${DRONE_TAG##v}"
- "${DRONE_COMMIT_SHA}"
- latest
secrets: [google_credentials]
when:
event: tag
branch: master
現在我們的drone會檢查我們的code是不是通過所有的linter,確定我們的專案可以被編譯,建置docker映像檔,並且根據不同的事件將他上傳到GCR上。
下一篇文章你會看到怎麼替我們的應用程式建立一個Helm Chart。上一篇文章我們已經看到怎麼使用一個人加件好的Chart,接下來我們就可以看到我們怎麼自動化這段流程了!
系列文連結如下: