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
Related
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/
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.
When I use multi stage in Docker, it cannot find the container config file, but when I use only the first stage, the container works fine. I need this as I'm going to reduce the size for the deployment phase, but I couldn't find a solution. I have no problem reading the config file in my local.
Here is my docker file
FROM golang:1.18.3-alpine AS builder
WORKDIR /app/policy-manangement-service
ENV GO111MODULE=on
COPY go.mod .
COPY go.sum .
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix nocgo -o /policy-manangement-service ./cmd/policy-management
FROM alpine:latest
COPY --from=builder /policy-manangement-service ./
ENTRYPOINT ["./policy-manangement-service"]
and my error:
{"app-name":"policy-management-service","error":"Config File \"local\" Not Found in \"[/configs]\"","level":"fatal","message":"Error while reading configs","time":"2022-07-12T14:27:58.486529597Z"}
my project structure:
cmd
- policy-manangement
-- main
configs
- local.yaml
internal
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
I'm building a multi-stage Dockerfile for my go project.
FROM golang:latest as builder
COPY ./go.mod /app/go.mod
COPY ./go.sum /app/go.sum
#exporting go1.11 module support variable
ENV GO111MODULE=on
WORKDIR /app/
#create vendor directory
RUN go mod download
COPY . /app/
RUN go mod vendor
#building source code
RUN go build -mod=vendor -o main -v ./src/
FROM alpine:latest
RUN apk --no-cache add ca-certificates
COPY --from=builder /app/main /app/main
WORKDIR /app/
ARG port="80"
ENV PORT=$port
EXPOSE $PORT
CMD ["./main"]
When I'm running the image, it throws error:
standard_init_linux.go:207: exec user process caused "no such file or directory"
I've verified that the 'main' file exist in /app/main.
I also tried to give executable permission by adding
chmod +x /app/main
but still it doesn't work.
What can possibly be wrong?
The "latest" version of the golang image is debian based, which uses libc. Alpine uses musl. If you do not compile with CGO_ENABLED=0, networking libraries will link to libc and the no such file or directory error point to a missing library. You can check these shared library links with ldd /app/main. A few solutions I can think of:
compile your program with CGO_ENABLED=0
switch your build image to FROM golang:alpine
change your second stage to be FROM debian