将Golang项目依赖预编译到缓存中

8
简而言之,我的当前用例涉及在Docker容器内动态创建一个Golang插件。编译需要用户的一些新输入(这就是为什么它不是预先编译的原因),但依赖关系是静态的,不会改变。
目前,在Docker容器内从头开始完成整个编译(尽管使用了 go mod download 来减少时间)。我注意到 go build 命令最终会编译大量依赖项,这增加了插件编译的非平凡时间,影响了我的应用程序的可用性。
是否有 Go 支持的方法或命令来遍历 go.mod 文件并填充 GOCACHE 目录?有了这样一个命令,我会在 Dockerfile 中运行它,使 Docker 镜像包含所有已编译的构建依赖关系的缓存。
我尝试过的:
1. go mod download :这只下载依赖项;它不编译它们。 2. 我已经实现了一个临时解决方案:我创建了一个简单的 main.go,导入了所有依赖项,并在我的 Dockerfile 中运行 go build 来填充缓存。正如提到的那样,这确实解决了我的问题,但感觉有些巧妙。此外,如果将来依赖项发生更改,也需要有人进行更改,这并不理想。 3. 我在网上看到很多关于 CI/CD 的答案。使用 CI/CD,容器只需要将一个分区挂载到主机上,其中包含运行后保留的缓存。这不能解决我立即构建容器本身的问题。

另一个你可以尝试的技巧是将本地缓存文件夹挂载到容器中,以便可以重复使用。 - kichik
1
相关问题:https://github.com/golang/go/issues/45474 和 https://github.com/golang/go/issues/27719 - Paramtamtаm
解决方法#2看起来并不太像hacky。我不会因此失眠。 - code_monk
由于您提到插件依赖项是静态的(不依赖于用户输入),因此在构建镜像时,插件(模板)代码很可能会被复制到 Docker 镜像中,然后进行 Go 构建。稍后,(模板)源代码可能会根据用户输入进行修改并重新编译。 - Sushil
1个回答

3

由于golang存储库中的问题(12)仍然未解决,我想我们所能做的只有“黑客”了。因此,我们可以像这样为依赖项缓存和预编译创建一个单独的docker层:

FROM golang:1.19-buster as builder

COPY ./go.* /src/

WORKDIR /src

# burn the modules cache
RUN set -x \
    # cache go dependencies
    && go mod download \
    # pre-compile common dependencies
    && mkdir /tmp/gobin \
    && for p in $(go list -m -f '{{if and (not .Indirect) (not .Main)}}{{.Path}}/...@{{.Version}}{{end}}' all); do \
      GOBIN=/tmp/gobin go install $p; \
    done \
    && rm -r /tmp/gobin

COPY . /src

RUN go build ...

如果不使用这个技巧,构建(docker buildx build --platform linux/amd64,linux/arm64 ...)需要约9分钟,使用它只需要大约6分钟(节省30%)。但是预编译步骤变长了约40%。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接