0

How do I get go build command in Docker to use module cache or vendor directory on every build unless the dependencies have changed?

I've tried both of these approaches with inconsistent results:

How can I persist go 1.11 modules in a Docker container? ^ this doesn't work, I believe because I'm using the Docker "builder" pattern.

https://medium.com/@monirz/golang-dependency-solution-with-go-module-and-docker-8967da6dd9f6 ^ this should work, but just doesn't for some reason...

I'm working on a server and for every little change I make to the go source code it makes sense that I need to recompile, but it does not make sense that that step should then also have to re-download all the dependencies again, every time.

I am building this server as a go module, here is my current Dockerfile:

FROM golang:1.14 AS builder

# Add the source
WORKDIR /app
COPY . .

# Statically compile our app for use in a distroless container
RUN CGO_ENABLED=0 go build -mod vendor -ldflags="-w -s" -v -o app .

# A distroless container image with some basics like SSL certificates
# https://github.com/GoogleContainerTools/distroless
FROM gcr.io/distroless/static

# Copy over binary and words dir
COPY --from=builder /app/app /app

ENTRYPOINT ["/app"]

I've also tried adding the -mod=vendor flag to the go command and it doesn't alter the behavior... which it should already be using that flag automatically anyway if 1.14 detects vendor dir in the module path (which is there).

DjH
  • 1,448
  • 2
  • 22
  • 41
  • Is your vendor/modules.txt file correct? – JimB May 22 '20 at 14:36
  • @JimB looks correct to me – DjH May 22 '20 at 14:50
  • Off the top of my head: doesn't `go mod vendor` create a vendor tree with symlinks to the actual files, which are outside of your docker build context, effectively causing your container not to contain the deps? – Elias Van Ootegem May 22 '20 at 15:03
  • @EliasVanOotegem it packages all source code in the `vendor` directory that it creates in the module root dir. At least, that's how it appears to me, and that `vendor` dir is being copied over along with all the other files in the first step of the Dockerfile – DjH May 22 '20 at 15:05
  • @DjH: "looks correct" means running `go mod vendor` from the root of the `main` package produces no changes? – JimB May 22 '20 at 15:15
  • @JimB correct. On further testing, I deleted the vendor directory and ran the docker build command with the `-mod vendor` flag set explicitly and it failed. So it appears that maybe the vendor dir is being used... but it's still rebuilding all modules every time there is a code change (which it doesn't do outside of the container) so maybe I mistook that for it being ignored? Does that sound correct to you? – DjH May 22 '20 at 15:24
  • @DjH, changes in the code change that docker layer, so there's no build artifacts to reuse. – JimB May 22 '20 at 15:34

1 Answers1

0

The vendor file was being used, it just didn't seem like it because although it was not re-downloading all modules on build it was re-building them on every build. The issue appears to be trying to use the builder pattern, I have altered my development compose file to handle everything in the compose yaml and will reserve the builder pattern Dockerfile for production (where it only really matters anyway).

Now using the following my development builds are way faster and don't appear to recompile every module on every build:

docker-compose.yaml

version: "3.7"

services:
  nginx:
    container_name: nginx
    image: nginx:alpine
    restart: unless-stopped
    ports:
      - 8000:80
    depends_on:
      - api
    volumes:
      - ./container_spec/nginx.conf:/etc/nginx/nginx.conf
      - ./container_spec/cors_support:/etc/nginx/cors_support

  api:
    image: golang:1.14
    container_name: api
    restart: always
    working_dir: /app
    volumes:
      - .:/app
      - cache:/go
    expose:
      - 8080
    command: go run main.go

volumes:
  cache:
DjH
  • 1,448
  • 2
  • 22
  • 41