[英]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.toml
和Gopkg.lock
复制到您的工作目录中,运行dep ensure
Gopkg.lock
,然后复制您的代码。 这只会在Gopkg更新后拉取依赖关系-否则docker将能够在安装了先前依赖关系的情况下重用缓存的层。 抱歉,长时间编辑!
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.