英文:
How to prevent docker build from redownloading copied Go vendor
问题
我正在尝试通过复制整个目录(包括vendor
)来加快Dockerfile
的运行速度,因为在我所在的地方重新下载依赖项大约需要10分钟以上的时间。但是当我尝试运行它时,它总是一遍又一遍地重新下载vendor
,与在本地运行go mod vendor
时的行为不同:
FROM golang:1.14-alpine AS builder
RUN apk --update add ca-certificates git make g++
ENV GO111MODULE=on
WORKDIR /app
RUN go get github.com/go-delve/delve/cmd/dlv
COPY . .
RUN go mod vendor
ARG COMMIT_HASH
ENV COMMIT_HASH=${COMMIT_HASH}
ARG BUILD_DATE
ENV BUILD_DATE=${BUILD_DATE}
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
go build \
-o app
FROM golang:1.14-alpine
WORKDIR /app
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
COPY --from=builder /go/bin/dlv /
COPY --from=builder /app/app .
COPY --from=builder /app/db ./db
EXPOSE 8080 63342
CMD [ "/dlv", "--listen=:63342", "--headless=true", "--api-version=2", "--accept-multiclient", "exec", "./app" ]
之前使用以下代码(不包括vendor
)也很慢:
COPY go.mod .
COPY go.sum .
RUN go mod download -x
COPY . .
尝试使用以下代码也没有起作用:
COPY vendor /go/pkg/mod
COPY vendor /go/pkg/mod/cache/download
COPY go.mod .
COPY go.sum .
RUN go mod download -x
COPY . .
如何强制它使用复制的vendor
目录而不是一遍又一遍地重新下载?
所期望的行为是:
- 当本地有
vendor
目录(使用了go mod vendor
)时,docker build
应该使用它。 - 但是当在CI环境中(因为
vendor/*/*
没有提交到代码仓库)或者开发者没有vendor/*/*
时,它应该重新下载所有依赖项(我并不在意,因为他们有良好的带宽)。
go mod vendor
命令是为CI环境和没有使用go mod vendor
的开发者准备的。
英文:
I'm trying to make Dockerfile
runs faster by copying whole directory (including vendor
, because redownloading dependencies took about 10m+ where I live), but when I tried to run it, it always redownload vendor again and again, unlike when go mod vendor
in local:
FROM golang:1.14-alpine AS builder
RUN apk --update add ca-certificates git make g++
ENV GO111MODULE=on
WORKDIR /app
RUN go get github.com/go-delve/delve/cmd/dlv
COPY . .
RUN go mod vendor
ARG COMMIT_HASH
ENV COMMIT_HASH=${COMMIT_HASH}
ARG BUILD_DATE
ENV BUILD_DATE=${BUILD_DATE}
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
go build \
-o app
FROM golang:1.14-alpine
WORKDIR /app
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
COPY --from=builder /go/bin/dlv /
COPY --from=builder /app/app .
COPY --from=builder /app/db ./db
EXPOSE 8080 63342
CMD [ "/dlv", "--listen=:63342", "--headless=true", "--api-version=2", "--accept-multiclient", "exec", "./app" ]
previously using this (without vendor) also slow:
COPY go.mod .
COPY go.sum .
RUN go mod download -x
COPY . .
trying with this also didn't work:
COPY vendor /go/pkg/mod
COPY vendor /go/pkg/mod/cache/download
COPY go.mod .
COPY go.sum .
RUN go mod download -x
COPY . .
how to force it to use copied vendor directory instead of redownloading again and again?
so the expected behavior are:
- when local have
vendor
(usedgo mod vendor
), thedocker build
should use it - but when on CI (since
vendor/*/*
not committed to the repo) or developer that doesn't havevendor/*/*
it should probably redownload everything (I don't really care, since they have good bandwidth)
the go mod vendor
command is for the CI and devs that haven't use go mod vendor
答案1
得分: 4
go mod vendor
只有在本地没有准备好的依赖项时,才会从网络上下载依赖项。否则,它只会将依赖项复制到vendor
文件夹中,而不访问网络。所以,在这里,你的问题是由于多次构建期间未重用go mod cache
引起的。
作为解决方案,你可以使用buildkit缓存解决方案,下面是一个最小示例:
main.go:
package main
import _ "github.com/jeanphorn/log4go"
func main() {
}
Dockerfile:
# syntax = docker/dockerfile:1.3
FROM golang:1.14-alpine AS builder
RUN apk --update add git
ENV GO111MODULE=on
WORKDIR /app
COPY main.go /app
RUN go mod init hello
RUN --mount=type=cache,mode=0755,target=/go/pkg/mod go get github.com/go-delve/delve/cmd/dlv && go get github.com/jeanphorn/log4go
RUN --mount=type=cache,mode=0755,target=/go/pkg/mod go mod vendor
第一次执行:
$ export DOCKER_BUILDKIT=1
$ docker build --progress=plain -t abc:1 . --no-cache
#16 [builder 6/7] RUN --mount=type=cache,mode=0755,target=/go/pkg/mod go get github.com/go-delve/delve/cmd/dlv && go get github.com/jeanphorn/log4go
#16 sha256:ae394bc67787799808175eada48c5f4e09101b6e153d535ddb5e4040fbf74395
#16 1.941 go: downloading github.com/go-delve/delve v1.7.1
#16 4.296 go: found github.com/go-delve/delve/cmd/dlv in github.com/go-delve/delve v1.7.1
......
#16 23.78 go: finding module for package github.com/toolkits/file
#16 23.96 go: downloading github.com/toolkits/file v0.0.0-20160325033739-a5b3c5147e07
#16 24.17 go: found github.com/toolkits/file in github.com/toolkits/file v0.0.0-20160325033739-a5b3c5147e07
#16 DONE 27.3s
第二次执行:
$ export DOCKER_BUILDKIT=1
$ docker build --progress=plain -t abc:1 . --no-cache
#15 [builder 6/7] RUN --mount=type=cache,mode=0755,target=/go/pkg/mod go get github.com/go-delve/delve/cmd/dlv && go get github.com/jeanphorn/log4go
#15 sha256:bee74f92ceb79cce449b9702c892cb39815461981838f6b63d500414be87c21d
#15 1.467 go: found github.com/go-delve/delve/cmd/dlv in github.com/go-delve/delve v1.7.1
#15 7.511 go: github.com/jeanphorn/log4go upgrade => v0.0.0-20190526082429-7dbb8deb9468
#15 7.533 go: finding module for package github.com/toolkits/file
#15 7.675 go: found github.com/toolkits/file in github.com/toolkits/file v0.0.0-20160325033739-a5b3c5147e07
#15 DONE 8.7s
你可以看到第一次运行生成的golang mod cache
已经在第二次运行中被重用,而不需要从互联网下载,现在它在主机上的效果相同。
注意:我没有建议直接将任何主机上的缓存绑定到容器上,我认为这是不可移植的。
英文:
go mod vendor
only download dependency from network if the dependency not ready in local. Otherwise, it will just copy dependency to the folder vendor
without access network. So here, your issue comes from the go mod cache
not be reused during multiple build.
As a solution, you could use buildkit cache solution, next is a minimal example:
main.go:
package main
import _ "github.com/jeanphorn/log4go"
func main() {
}
Dockerfile:
# syntax = docker/dockerfile:1.3
FROM golang:1.14-alpine AS builder
RUN apk --update add git
ENV GO111MODULE=on
WORKDIR /app
COPY main.go /app
RUN go mod init hello
RUN --mount=type=cache,mode=0755,target=/go/pkg/mod go get github.com/go-delve/delve/cmd/dlv && go get github.com/jeanphorn/log4go
RUN --mount=type=cache,mode=0755,target=/go/pkg/mod go mod vendor
1st Execution:
$ export DOCKER_BUILDKIT=1
$ docker build --progress=plain -t abc:1 . --no-cache
#16 [builder 6/7] RUN --mount=type=cache,mode=0755,target=/go/pkg/mod go get github.com/go-delve/delve/cmd/dlv && go get github.com/jeanphorn/log4go
#16 sha256:ae394bc67787799808175eada48c5f4e09101b6e153d535ddb5e4040fbf74395
#16 1.941 go: downloading github.com/go-delve/delve v1.7.1
#16 4.296 go: found github.com/go-delve/delve/cmd/dlv in github.com/go-delve/delve v1.7.1
......
#16 23.78 go: finding module for package github.com/toolkits/file
#16 23.96 go: downloading github.com/toolkits/file v0.0.0-20160325033739-a5b3c5147e07
#16 24.17 go: found github.com/toolkits/file in github.com/toolkits/file v0.0.0-20160325033739-a5b3c5147e07
#16 DONE 27.3s
2nd Execution:
$ export DOCKER_BUILDKIT=1
$ docker build --progress=plain -t abc:1 . --no-cache
#15 [builder 6/7] RUN --mount=type=cache,mode=0755,target=/go/pkg/mod go get github.com/go-delve/delve/cmd/dlv && go get github.com/jeanphorn/log4go
#15 sha256:bee74f92ceb79cce449b9702c892cb39815461981838f6b63d500414be87c21d
#15 1.467 go: found github.com/go-delve/delve/cmd/dlv in github.com/go-delve/delve v1.7.1
#15 7.511 go: github.com/jeanphorn/log4go upgrade => v0.0.0-20190526082429-7dbb8deb9468
#15 7.533 go: finding module for package github.com/toolkits/file
#15 7.675 go: found github.com/toolkits/file in github.com/toolkits/file v0.0.0-20160325033739-a5b3c5147e07
#15 DONE 8.7s
You could see golang mod cache
generated by 1st run already be reused by 2nd run without downloading from internet, now it effects as same when you do it on host.
NOTE: I didn't suggest to directly bind any cache on host to container, it's not portable I think.
答案2
得分: 0
不用担心,我从@zasdaym那里得到了答案。
COPY go.mod go.sum source.go
COPY vendor ./vendor
RUN go build -mod vendor -o out.exe
它不会重新下载。
英文:
Nevermind, got answer from @zasdaym
COPY go.mod go.sum source.go
COPY vendor ./vendor
RUN go build -mod vendor -o out.exe
it would not redownload
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论