Daniel Alderman
Daniel Alderman

Reputation: 147

How to add local dependencies in Go Docker multi-stage build?

How can I add local dependencies from a folder within the same Go project?

I have the following directory structure:

.
├── backend
│   ├── Dockerfile
│   ├── conduit
│   │   └── get_data.go
│   ├── main.go
│   ├── main_test.go
│   ├── storage
│   │   ├── create_client.go
│   │   └── read_data.go
├── cron_job
│   ├── Dockerfile
│   ├── main.go
│   └── main_test.go

And Dockerfile:

FROM golang:1.10.1 as builder
ADD . github.com/dalderman77/phacts/backend/conduit
ADD . github.com/dalderman77/phacts/backend/storage
WORKDIR /go/src/github.com/dalderman77/phacts/cron_job
# Get dependencies
RUN go get -d -v cloud.google.com/go/datastore golang.org/x/net/context
COPY . .
# Compile Go binary
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main .

# Create smaller lightweight container
FROM alpine:latest  
RUN apk --no-cache add ca-certificates
WORKDIR /root/
# Copy binary from previous stage
COPY --from=builder /go/src/github.com/dalderman77/phacts/cron_job/main .
CMD ["./main"]  

What I have so far is based on Use multi-stage builds

The issue is that compiled binary is dependent on code from the backend directory. It compiles fine locally but I am unsure how to correctly add these dependencies so they can be compiled within the container:

main.go:9:2: cannot find package "github.com/dalderman77/phacts/backend/conduit" in any of:
        /usr/local/go/src/github.com/dalderman77/phacts/backend/conduit (from $GOROOT)
        /go/src/github.com/dalderman77/phacts/backend/conduit (from $GOPATH)
main.go:10:2: cannot find package "github.com/dalderman77/phacts/backend/storage" in any of:
        /usr/local/go/src/github.com/dalderman77/phacts/backend/storage 

Upvotes: 0

Views: 1683

Answers (1)

Nikita Sokolov
Nikita Sokolov

Reputation: 51

In Dockerfile this command is used to compile your code:

RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main .

This will make your binary static, allowing you to perform second step of your build, since you can now start with tiny image (alpine in your case, but you can even try using empty image) without relying on any dependency inside your container.

In your example, you're adding ca-certificates to the final image:

RUN apk --no-cache add ca-certificates

You may need this in case you're making HTTPS requests in your application, since alpine container doesn't have any certificates inside its file system. In general, any static assets required for your application (e.g. images, html/email templates, scripts etc.) must be added to resulting image via COPY or ADD command.

Also please have a look at this outstanding guide on how to build minimal docker containers.

Edit: Your problem with the build happens because cron_job can't locate packages from backend, since you don't copy backend to the container in the correct $GOPATH folder. To solve this problem, you need to vendor your dependencies using dependency manager tool. The current official one is dep.

Restoring dependencies for cron_job (locally on your machine, NOT inside your container) will create vendor subfolder, where every needed dependency for your project will be stored. Now when you copy cron_job to the container, build will also look at vendor folder to check for missing dependencies.

Upvotes: 2

Related Questions