缓存" go get"在docker build中

时间:2016-09-01 18:43:29

标签: unit-testing go docker docker-compose

我想在docker-compose脚本中封装我的golang单元测试,因为它依赖于几个外部服务。我的应用程序有很多依赖项,因此go get需要一段时间。

如何以允许docker容器构建的方式缓存包,而不必在每次要测试时都下载所有依赖项?

我的Dockerfile:

FROM golang:1.7

CMD ["go", "test", "-v"]

RUN mkdir -p /go/src/app
WORKDIR /go/src/app

COPY . /go/src/app
RUN go-wrapper download
RUN go-wrapper install

每次我想运行单元测试时,我都会在以下脚本上运行docker-compose up --build backend-test

version: '2'
services:
  ...
  backend-test:
    build:
      context: .
      dockerfile: Dockerfile
    image: backend-test
    depends_on:
      ...

但是每次我想要运行测试时都会调用go-wrapper download,并且需要很长的时间才能完成。

解决方案?提前谢谢!

2 个答案:

答案 0 :(得分:4)

我个人使用govendor。它根据golang供应商惯例将您的依赖项保存在项目内的供应商目录中。这仍然需要在构建时复制到docker镜像。

但是有很好的理由不给供应商。例如,当您构建pkg时,您不应该是供应商。当你有不同的pkg使用不同版本的依赖项时,事情会变得混乱。这可以通过仅销售可执行文件来解决。

因此,如果您有充分的理由不向供应商提供,您可以分开几步。 按正确顺序排列会加快速度。

您可以使用一些get.sh依赖项命令创建一个shell脚本(go get)。 (你可以把它们放在你的Dockerfile中,但它们有一个行限制)

go get github.com/golang/protobuf/proto
go get github.com/pborman/uuid
go get golang.org/x/net/context
go get golang.org/x/net/http2
go get golang.org/x/net/http2/hpack

然后在Dockerfile中首先复制并执行shell脚本。 每次更新get.sh时,它都将完全重建。它仍然运行got get ./...以确保所有依赖项都在那里。但如果在get.sh脚本中添加了所有内容,您将获得不错的速度提升。

FROM golang:1.6

RUN mkdir -p /go/src/app

COPY get.sh /go/src/app

WORKDIR /go/src/app

RUN bash get.sh

COPY . /go/src/app

RUN go get ./...

CMD go test -v

一般的想法是,你经常更改Dockerfile中较低的内容,以及在顶部非常不变的内容。即使您必须添加另一个或两个命令。 Docker将逐行进行,直到找到需要重建的内容,然后在此之后执行每一行。

答案 1 :(得分:0)

我一直在寻找你的问题的答案,但具有讽刺意味的是我找到了一个问题(如何快速运行码头测试)。如果您真的想要快速测试,理想情况下,在运行容器时应该完全避免重建容器。但是等一下,如何将新的源代码放到容器上?卷我的朋友,卷。以下是我设置的方法:

搬运工-compose.dev.yml:

backend-test:
  volumes:
    - .:/path/to/myapp

当然,/ path / to / myapp是图像中的路径。你必须明确地传递这个图像为dev:

docker-compose up -f docker-compose.dev.yml

但是现在,当你运行测试时,你不再使用docker-compose,你将使用docker exec:

docker exec -it backend-test go test

如果你这样做,后端测试容器中的src dir将始终是最新的,因为它实际上是一个已安装的卷。附加到正在运行的容器并运行测试应该比每次启动一个新容器快得多。

编辑:评论者正确地指出,这只会避免在依赖关系没有改变时重建图像(不需要go get)。好消息是它不仅可以避免重建,还可以避免重启。当我像这样测试时,我添加一个依赖项,我通常直接从我的测试控制台go get。让go get在您的容器中工作可能有点棘手,但一种方法是通过mounting SSH_AUTH_SOCK将您的ssh代理转发到您的容器。遗憾的是,您无法在构建期间安装卷,因此如果您希望构建目标能够在运行测试之前提取新的依赖关系,则可能需要在映像中包含某种部署密钥。但是,我的回答主要是分离构建和测试,以避免完整构建,直到你准备好生成最终的工件。

那就是说,我认为我可能明白我没有以你提出的方式回答这个问题。在ruby中,答案就像复制Gemfile和Gemfile.lock,并运行bundle install --deploy一样简单,然后复制您已更改的代码。我个人并不介意在添加依赖项时重建的成本,因为99%的更改仍然不会涉及重建。也就是说,您可以考虑使用golang的新Bundler启发的依赖管理器:dep。安装dep后,我很确定您可以将Gopkg.tomlGopkg.lock复制到工作目录中,运行dep ensure,然后复制代码。这只会在更新Gopkg时拉出依赖关系 - 否则docker将能够重用已安装先前依赖项的缓存层。很抱歉长时间编辑!