Golang, Docker, external packages, not finding path - docker

My dockerfile:
FROM golang:1.14
RUN mkdir /app
ADD . /app
WORKDIR /app
RUN go build -o main .
CMD ["/app/main"]
error:
main.go:11:2: cannot find package "github.com/gorilla/mux" in any of:
/usr/local/go/src/github.com/gorilla/mux (from $GOROOT)
/go/src/github.com/gorilla/mux (from $GOPATH)
My PATH in GOPATH is
GOPATH=/Users/pstrom/go
I'm coming from a javascript background and there you run NPM INSTALL which adds all external packages to directory node_modules in same directory as the project.
Is there any similar command in Go? Can't find any. I don't want add any PATH in docker, because I wanna run it from anywhere.
How do I handle external packages in Docker in Go?

See the comments too.
It's possible you need to create a go.mod file which functions like package.json. If you don't have a go.mod file but just want to get going, you can go mod init x in the directory alongside main.go and Dockerfile. Then, to force packages to be added to go.mod, you can just go run . (or go run main.go).
Then:
FROM golang:1.15
WORKDIR /app
COPY go.mod .
RUN go mod download
COPY . .
RUN go build -o main .
ENTRYPOINT ["/app/main"]
I recommend bumping to Go 1.15
WORKDIR creates the directory if not present so you skip the mkdir
/app is outside of ${GOPATH} which is correct when using modules
COPY >> ADD (my preference)
go mod download gets dependencies defined in go.mod
COPY . . everything else, may just need to be COPY main.go .
ENTRYPOINT >> CMD and the container will default to running your binary

Related

Understanding this Docker file - Where are the files being copied

Being new to docker files. I am trying to understand how docker build is working here.
I currently have a folder that has two files in it. The folder is called foo
The structure of the folder is as follows. It has two files in it the first file is main.go and the other file next to it is Dockerfile as shown in the diagram below
Foo
|_main.go
|_go.mod
|_go.sum
|_Dockerfile
My Dockerfile currently looks like this and the command docker build -t .. is called from inside the folder Foo.
FROM golang:1.18-alpine AS builder
# Create and change to the app directory.
WORKDIR /app
COPY go.* ./
RUN go mod download
# Copy local code to the container image.
COPY . ./
RUN go build -v -o my app
....
Now this is a fairly simple Dockerfile. Here is what I understand.
1- golang:1.18-alpine is the base image
2- In the container a folder called app will be created
3- go.mod and go.sum will be copied to ./ path of the container (probably home) of the container.
4- Run go mod download will be called from inside the /app folder correct ?
My question is basically for no. 4. go mod download called from inside /app folder ? If so how does that work because from my understanding is that the /app folder is so far empty ? When did the app folder get populated ? In the statement COPY go.* ./ what path is ./ is that the home ?
In line WORKDIR /app, your current path will be set to /app. If the directory don't exist then it will be created beforehand.
Next COPY go.* ./, this matches all of files start with go. will be copied to /app directory in the docker container. So your docker /app should look like this :
/app
| go.mod
| go.sum
Again with COPY . ./, you are copying all files from current directory to /app directory of your docker container. It will replace already existing files in the contrainer. /app will look like this:
/app
| main.go
| go.mod
| go.sum
| Dockerfile
Last with RUN go build -v -o myapp, you are building the app using go and saving binary file myapp.

'No required module provides package' when building Go docker image

My Dockerfile is below:
# syntax=docker/dockerfile:1
FROM golang:1.18-alpine
WORKDIR /app
COPY go.mod ./
COPY go.sum ./
RUN go mod download
COPY *.go ./
RUN go build -o /datapuller
EXPOSE 8080
CMD [ "/datapuller" ]
I tried to build with $ docker build --tag datapuller .
But got error:
main.go:13:2: no required module provides package gitlab.com/mycorp/mycompany/data/datapuller/dbutil; to add it:
go get gitlab.com/mycorp/mycompany/data/datapuller/dbutil
main.go:14:2: no required module provides package gitlab.com/mycorp/mycompany/data/datapuller/models; to add it:
go get gitlab.com/mycorp/mycompany/data/datapuller/models
How to solve this, I can run directly with go run main.go just fine.
My main.go's import is below. I think the imports caused this problem:
package main
import (
"encoding/json"
client "github.com/bozd4g/go-http-client"
"github.com/robfig/cron/v3"
"github.com/xuri/excelize/v2"
"gitlab.com/mycorp/mycompany/data/datapuller/dbutil"
"gitlab.com/mycorp/mycompany/data/datapuller/models"
"gorm.io/gorm"
)
func main() {
...
Because the associated package needs to be pulled when building.
Docker may be missing the necessary environment variables to pull these packages.
It is recommended that you use the go mod vendor command,then build image
FROM golang:1.18-alpine
ADD . /go/src/<project name>
WORKDIR /go/src/<project name>
RUN go build -mod=vendor -v -o /go/src/bin/main main.go
RUN rm -rf /go/src/<project name>
WORKDIR /go/src/bin
CMD ["/go/src/bin/main"]
When you copy your source code into the image, you only copy files in the current directory
COPY *.go ./ # just the current directory's *.go, not any subdirectories
It's usually more common to copy in the entire host source tree, maybe using a .dockerignore file to cause some of the source tree to be ignored
COPY ./ ./
Otherwise you need to copy the specific subdirectories you need into the image (each directory needs a separate COPY command)
COPY *.go ./
COPY dbutil/ dbutil/
COPY models/ models/

Can't build Golang in dockerfile

I have this structure of my project:
https://i.stack.imgur.com/SqqDh.png
And this is my Dockerfile:
FROM golang:1.19
ADD . /go/src/myapp
WORKDIR /go/src/myapp
RUN go mod init cloudmeta
RUN go get github.com/go-sql-driver/mysql
RUN go get -u github.com/gin-gonic/gin
RUN go build -o bin/cloudmeta
CMD [ "bin/cloudmeta" ]
When I trying to build my docker-container I have this error:
package cloudmeta/backend/handlers is not in GOROOT (/usr/local/go/src/cloudmeta/backend/handlers)
When building Go code in docker, you shouldn't use go mod init. Take a look at the following example dockerfile from docker docs:
# syntax=docker/dockerfile:1
FROM golang:1.16-alpine
WORKDIR /app
COPY go.mod ./
COPY go.sum ./
RUN go mod download
COPY *.go ./
RUN go build -o /docker-gs-ping
EXPOSE 8080
CMD [ "/docker-gs-ping" ]
The docker docs guide goes into more depth but to summarise things:
You should copy your go.mod and go.sum files into your project directory in the image.
Now you can run the go mod download command to install the go modules required.
Then you need to copy your source code into the image.
Now you can compile your source code with the go build command.

Golang cannot find package in Docker image

So, I am trying to dockerize a golang application with different directories containing supplementary code for my main file.
I am using gorilla/mux. The directory structure looks like this.
$GOPATH/src/github.com/user/server
|--- Dockerfile
|--- main.go
|--- routes/
handlers.go
|--- public/
index.gohtml
It works on my host machine with no problem. The problem is that when I try to deploy the docker image it does not run and exits shortly after creation. I have tried changing the WORKDIR command in my dockerfile to /go/src and dump all my files there, but still no luck. I have also tried the official documentation on docker hub. Doesn't work either.
My Dockerfile.
FROM golang:latest
WORKDIR /go/src/github.com/user/server
COPY . .
RUN go get -d github.com/gorilla/mux
EXPOSE 8000
CMD ["go","run","main.go"]
My golang main.go
package main
import (
"github.com/gorilla/mux"
"github.com/user/server/routes"
"log"
"net/http"
"time"
)
func main(){
//...
}
I get this error message when I check the logs of my docker image.
Error Message
main.go:5:2: cannot find package "github.com/user/server/routes" in any of:
/usr/local/go/src/github.com/user/server/routes (from $GOROOT)
/go/src/github.com/user/server/routes (from $GOPATH)
Try the following Docker file:
# GO Repo base repo
FROM golang:1.12.0-alpine3.9 as builder
RUN apk add git
# Add Maintainer Info
LABEL maintainer="<>"
RUN mkdir /app
ADD . /app
WORKDIR /app
COPY go.mod go.sum ./
# Download all the dependencies
RUN go mod download
COPY . .
# Build the Go app
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main .
# GO Repo base repo
FROM alpine:latest
RUN apk --no-cache add ca-certificates curl
RUN mkdir /app
WORKDIR /app/
# Copy the Pre-built binary file from the previous stage
COPY --from=builder /app/main .
# Expose port 8000
EXPOSE 8000
# Run Executable
CMD ["./main"]
Here, we are creating an intermediate docker builder container, copying the code into it, build the code inside the builder container and then copy the binary image to the actual docker.
This will help in both having all the dependencies in the final container and also, the size of the final image will be very small

Build go dependencies in separate Docker layer

I'm trying to speed up Docker builds of my Go app. Right now, it's spending maybe 60s just building dependencies (it's a k8s controller, so there are a lot).
One very important constraint: my project depends on private GitHub repos. I do go mod vendor outside docker build, where I have creds for the repos set up.
My Dockerfile right now is roughly:
FROM golang:1.12
WORKDIR /src
COPY . .
RUN go build -mod=vendor
...
Even without having to download the deps, that build takes a while because it rebuilds several hundred packages every docker build.
What I'd like to do is something like:
FROM golang:1.12
WORKDIR /src
# these shouldn't change very often
COPY go.mod go.sum vendor ./
RUN go build -mod=vendor <all dependency packages>
COPY . .
RUN go build -mod=vendor
...
I tried parsing go.mod, but of course that lists modules, not packages. I tried go list but never managed to get a working incantation.
I've got a nasty hack that seems to work:
FROM golang:1.12
WORKDIR /src
COPY go.mod go.sum ./
COPY vendor/ ./vendor
RUN go build -mod=vendor $(cat deps|grep -v mypackage | grep -v internal)
COPY . .
RUN go build -mod=vendor
...
go list -f '{{join .Deps "\n"}}' > deps
docker build .
Docker documentation has a guide specific to Go docker images (Build your Go image).
It works as follows:
# Layer for dependency installation
COPY go.mod go.sum ./
RUN go mod download
# Layer for app build
COPY . .
RUN go build -o main .

Resources