繁体   English   中英

在Docker构建中缓存“获取”

[英]Cache “go get” in docker build

我想将golang单元测试封装在docker-compose脚本中,因为它取决于多个外部服务。 我的应用程序具有很多依赖关系,因此需要一段时间才能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 ,这需要很短的时间才能完成。

解决方案? 提前致谢!

我个人使用govendor 根据golang供应商约定,它将依赖项保留在项目内的供应商目录中。 在构建时,仍然需要将其复制到您的Docker映像。

但是有很好的理由不与供应商合作。 例如,当您构建pkg时,您不应供应商。 当您使用不同版本的依赖项使用不同的pkg时,情况会变得混乱。 只有供应商提供的可执行文件可以解决此问题。

因此,如果您有充分的理由不愿与供应商合作,则可以分开几个步骤。 按照正确的顺序放置它们可以加快处理速度。

您可以使用一些用于获取依赖项的go get命令来创建Shell脚本( get.sh )。 (您可以将它们放在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会逐行处理,直到找到需要重建的内容,然后再进行每一行。

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

docker-compose.dev.yml:

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

当然,/ path / to / myapp是图像中的路径。 您必须为开发人员显式传递此图像:

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

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

docker exec -it backend-test go test

如果正确执行此操作,则后端测试容器中的src目录将始终是最新的,因为它实际上是已装入的卷。 事实证明,连接到运行中的容器并运行测试要比每次旋转一个新容器快得多。

编辑:Commenter正确指出,这仅避免在您依赖项没有更改时避免重建图像(无需go get )。 令人高兴的是,它不仅避免了重建,而且还避免了重新启动。 当我像这样进行测试并添加依赖项时,通常只go get直接从测试控制台go get即可。 它可以是一个有点棘手得到go get到工作中的容器内,但一个方法是通过您的容器转发你的SSH代理安装SSH_AUTH_SOCK 遗憾的是,您无法在构建期间挂载卷,因此,如果您希望构建目标能够在运行测试之前提取新的依赖项,则可能需要在映像中包括某种部署密钥。 但是,我的回答的重点是分离构建和测试,以避免完整构建,直到您准备生成最终工件为止。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM