How to pre-build all required modules and cache them

Viewed 832

When buliding a Docker image, I know we can add a layer to cache dependencies. But the dependency needs to be built. This step is quite time-consuming, on my machine it takes about 30 seconds to build sqlite3 alone.

I also know I can use go build github.com/mattn/go-sqlite3 to build a specific dependency, but is there any way to pre-build all the dependencies list in go.mod?

I found the same question about this here, but there is no answer.

2 Answers

Docker provides documentation on this exact topic here. The suggestion is to structure your Dockerfile like this:

FROM --platform=${BUILDPLATFORM} docker.io/golang:1.16.7-alpine AS build
ARG TARGETOS
ARG TARGETARCH
WORKDIR /src
ENV CGO_ENABLED=0
COPY go.* .
RUN go mod download
COPY . .
RUN --mount=type=cache,target=/root/.cache/go-build \
GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -o /out/example .

FROM scratch
COPY --from=build /out/example /

A lot of that is boilerplate you can remove if you're only building for a single architecture; the parts related to caching are really only:

FROM docker.io/golang:1.16.7-alpine AS build
WORKDIR /src
COPY go.* .
RUN go mod download
COPY . .
RUN --mount=type=cache,target=/root/.cache/go-build go build -o /out/example .

FROM scratch
COPY --from=build /out/example /

This mounts a cache directory on /root/.cache/go-build, which is the default location for the go build cache. The first time you build your image it will populate this cache. Subsequent builds will re-use the cached files.

For this to work, you must build with DOCKER_BUILDKIT=1, i.e.:

DOCKER_BUILDKIT=1 docker build -t myimage .

Or use docker buildx:

docker buildx build -t myimage .

I have tested this out locally and it seems to work as intended (I have verified that in builds other than the first one, the go-build cache directory is populated prior to running go build).

This isn't tested within Docker, but should work. It can likely be optimized further, though, or modified to work in more limited build environments

RUN go mod download && go list -f '{{ join .Deps "\n" }}' ./... | sort -u | grep -v '<your package import path>' | xargs go build
Related