如何与 Golang、Docker 和 GitLab CI 交朋友
让我们首先演示一下我们将用作示例的简单应用程序:
package main
import (
"log"
"net/http"
"github.com/gorilla/mux"
)
var port = "8080"
func main() {
log.Fatal(http.ListenAndServe(":"+port, router()))
}
func router() http.Handler {
r := mux.NewRouter()
r.Path("/greeting").Methods(http.MethodGet).HandlerFunc(greet)
return r
}
func greet(w http.ResponseWriter, req *http.Request) {
_, _ = w.Write([]byte("Hello, world!"))
}
另外,我们添加一个小测试:
package main
import (
"net/http"
"net/http/httptest"
"testing"
)
func TestRouter(t *testing.T) {
w := httptest.NewRecorder()
req := httptest.NewRequest(http.MethodGet, "/greeting", nil)
router().ServeHTTP(w, req)
expected := "Hello, world!"
actual := w.Body.String()
if expected != actual {
t.Fatalf("Expected %s but got %s", expected, actual)
}
}
这很简单。我想强调的是,我们有一个外部依赖项。为了管理它(以及未来的依赖项),我们将使用dep:
dep init -v -no-examples
为了证明一切正常,我们在本地运行测试、构建并试用我们的应用程序:
$ go test ./...
ok gitlab.com/hypnoglow/example-go-docker-gitlab 0.016s
$ go build -o app .
$ ./app
# and in another terminal:
$ curl http://localhost:8080/greeting
Hello, world!
看起来很简单。现在我们要将我们的应用程序docker化。
问题:我们如何将依赖项传递给 docker 构建过程?
-
选项 1:使用命令将
vendor
目录与源代码一起传递。COPY
-
选项 2:使用在构建时安装依赖项
RUN dep ensure
。
第一种选择比第二种选择有几个好处:
- 我们(通常)已经在本地安装了供应商,何必在构建时再安装它们呢?这样可以加快
docker build
进程。 - 如果存在私有依赖项,例如你的包位于另一个私有仓库中,你需要将 git 凭据传递给 docker 构建过程,以便 go 工具能够获取这些依赖项。在第一种方案中,你不会遇到这样的问题。
因此,我们创建以下 Dockerfile:
FROM golang:1.10-alpine3.7 as build
WORKDIR /go/src/app
COPY . .
RUN go build -o app
FROM alpine:3.7
COPY --from=build /go/src/app/app /usr/local/bin/app
ENTRYPOINT ["/usr/local/bin/app"]
接下来,我们需要一个.gitlab-ci.yml
文件来运行测试并在推送时构建镜像。我们该如何完成这些任务呢?
好吧,我们需要一个作业来安装依赖项,因为上面我们决定不在 Dockerfile 中安装它们。即使安装了,我们也需要安装它们才能运行测试。因此,我们创建一个作业dep
来安装依赖项,并将vendor
目录存储为 GitLab 工件。在其他作业中,我们将dep
作业添加为依赖项,GitLab 会将之前存储的依赖项vendor
直接提取到我们的项目目录中。
variables:
PACKAGE_PATH: /go/src/gitlab.com/hypnoglow/example-go-docker-gitlab
stages:
- dep
- test
- build
# A hack to make Golang-in-Gitlab happy
.anchors:
- &inject-gopath
mkdir -p $(dirname ${PACKAGE_PATH})
&& ln -s ${CI_PROJECT_DIR} ${PACKAGE_PATH}
&& cd ${PACKAGE_PATH}
dep:
stage: dep
image: golang:1.10-alpine3.7
before_script:
- apk add --no-cache curl git
- curl -sSL https://github.com/golang/dep/releases/download/v0.5.0/dep-linux-amd64 -o /go/bin/dep
- chmod +x /go/bin/dep
- *inject-gopath
script:
- dep ensure -v -vendor-only
artifacts:
name: "vendor-$CI_PIPELINE_ID"
paths:
- vendor/
expire_in: 1 hour
test:
stage: test
dependencies:
- dep
image: golang:1.10-alpine3.7
before_script:
- *inject-gopath
script:
- go test ./...
build:
stage: build
dependencies:
- dep
image: docker:17
services:
- docker:dind
script:
- docker build -t app .
最后,我们检查管道:
大功告成!接下来的步骤,比如将构建好的镜像推送到 Docker 仓库,就留给读者练习吧。🙂
完整示例可在GitLab 存储库中找到。
谢谢!这是我在dev.to上的第一篇文章,希望你喜欢。
由于英语不是我的母语,我对于任何语法和语言错误深表歉意。如果您发现任何错误,请在评论中指出! 🤓
文章来源:https://dev.to/hypnoglow/how-to-make-friends-with-golang-docker-and-gitlab-ci-4bil