如何防止 Docker 构建过程中重新下载复制的 Go 依赖包?

huangapple go评论103阅读模式
英文:

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目录而不是一遍又一遍地重新下载?

所期望的行为是:

  1. 当本地有vendor目录(使用了go mod vendor)时,docker build应该使用它。
  2. 但是当在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:

  1. when local have vendor (used go mod vendor), the docker build should use it
  2. but when on CI (since vendor/*/* not committed to the repo) or developer that doesn't have vendor/*/* 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

huangapple
  • 本文由 发表于 2021年9月2日 05:03:40
  • 转载请务必保留本文链接:https://go.coder-hub.com/69020535.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定