I'm building a multistage Docker image of a golang microservice and I want to make it very thin using busybox as base image to run the final executable.
The image is correctly built, but when I run it I get this error:
standard_init_linux.go:211: exec user process caused "no such file or directory"
I'm working on my Ubuntu laptop, so this error has nothing to do with the Windows OS as many other questions report.
This is my image.
# build stage
FROM golang:1.15.3 AS build-stage
RUN mkdir /build
ADD . /build/
WORKDIR /build
RUN go mod download
RUN go test ./...
RUN go build -o goapp .
# final stage
FROM busybox
WORKDIR /app
COPY --from=build-stage /build/goapp /app/
CMD ["./goapp"]
A very simplified version of my project folder could be:
project
├── Dockerfile
├── go.mod
├── go.sum
├── main.go
└── other-packages
You are building your app with CGO_ENABLED=1 (this is Go's default setting for builds) - and transplanting that executable to a docker image where there is no compatible library support (glibc, dns resolver etc.).
To ensure your build does not rely on any external libraries - statically binding all dependencies - and can then be deployed to a SCRATCH docker image or busybox - disable CGO during your build phase:
RUN CGO_ENABLED=0 go build -o goapp .
I guess there's a difference between the libc of the two images.
As introduced in description of busybox image, there are several libc variants, which is distinguished by image tags.
The libc of golang:1.15.3 is glibc (it is FROM the corresponding version of debian:buster), so you should use busybox:glibc on your final stage.
The default busybox:latest is using uclibc. They are incompatible. Check the sha256 digest of busybox:latest and busybox:uclibc.
Related
I have go web application and im trying to deploy on Docker but im keep getting this messages. Im Running this on Windows 10 enterprise 11th Gen Intel(R) Core(TM) i7-1185G7 # 3.00GHz 64 bit
Docker file
FROM golang:latest
RUN mkdir /app
COPY bin/main /app/main
WORKDIR /app
CMD ["/app/main"]
MAKEFILE
GOOS=linux
GOARCH=amd64
build:
go build -o bin/main main.go
run:
go run main.go
compile:
echo "Compiling for every OS and Platform"
GOOS=linux GOARCH=arm go build -o bin/main-linux-arm main.go
GOOS=linux GOARCH=arm64 go build -o bin/main-linux-arm64 main.go
GOOS=freebsd GOARCH=386 go build -o bin/main-freebsd-386 main.go
im running this commands
go build -o bin/main main.go
docker build -t tiny .
docker run -p 127.0.0.1:8080:8080 tiny
error im getting: exec /app/main: exec format error
go file: log.Fatal(http.ListenAndServe (":8080",nil))
When your Dockerfile says
COPY bin/main /app/main
you're copying a main binary built on your (Windows) host system into your (Linux) Docker image. Since the operating systems don't match, you're getting that "exec format error" message.
It's very common when you're building a Docker image to do the actual compilation step in the Dockerfile itself. In a compiled language like Go, a further extremely common approach is to use a multi-stage build so that the Go toolchain itself isn't part of the final image; this gives you a much smaller image and doesn't republish your source code to end users.
FROM golang:1.19-bullseye AS build
WORKDIR /app
COPY ./ ./
RUN go build -o main ./
FROM debian:bullseye
COPY --from=build /app/main /usr/local/bin/main
CMD ["main"]
This approach ignores your Makefile, which isn't really using any Make features (for example, there is nowhere one rule depends on another, and there is nowhere you might skip a rule execution if its output file already exists). This particular Dockerfile builds a dynamically-linked binary, and makes sure the build and execution phase are using the same major version of the same Linux distribution, and therefore the same system C library.
I'm running into a problem with docker that I can't fix by looking at other references, documentation, etc. and since i'm a beginner with Docker I try my luck here. I'm working in a Next.js project that is using Docker to build the app. I'm using the example documentation of Next.js, and that works if I have my Dockerfile in the root of my project. However, I want to put it in a folder called etc and use it from there. This is giving me problems, because docker can't find the files that i'm trying to copy to the working directory, see error below.
Structure
.
├── etc
│ └── Dockerfile
├── package.json
└── yarn.lock
Command
docker build etc/
Error
failed to compute cache key: "/yarn.lock" not found: not found
Dockerfile
FROM node:16-alpine AS deps
# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.
RUN apk add --no-cache libc6-compat
WORKDIR /app
COPY package.json yarn.lock ./
RUN yarn install --frozen-lockfile
I've tried a bunch of things, such as changing the files and paths. Also, in the documentation they mention the -f flag, but that doesn't work for me either because I get the error "docker build" requires exactly 1 argument. when running docker build -f etc/Dockerfile. Is that outdated? Anyway, my question is how to build my app with docker when my dockerfile is not in the root of the project but in a child folder like etc/.
You have forgotten the dot at the end of the command docker build -f etc/Dockerfile .
I have a problem when I build with docker compose an application with local dependencies to create a docker image.
My Dockerfile:
FROM golang:alpine AS build
ENV GOPATH=$GOPATH
#GOPROXY
ENV GOPROXY=http://proxy.golang.org
ENV GO111MODULE=on
WORKDIR $GOPATH/src/github.com/julianskyline/motorcars-core-business
COPY . .
# Set OS as linux
RUN GOOS=linux go build -o $GOPATH/bin/github.com/julianskyline/motorcars-core-business main.go
FROM alpine
COPY --from=build $GOPATH/bin/github.com/julianskyline/motorcars-core-business $GOPATH/bin/github.com/julianskyline/motorcars-core-business
ENTRYPOINT [ "/go/bin/motorcars-core-business" ]
My go.mod
module github.com/julianskyline/motorcars-core-business
go 1.15
replace (
github.com/julianskyline/errors => /home/julianmarin/proyectos/go/src/github.com/julianskyline/errors
github.com/julianskyline/motorcars-db => /home/julianmarin/proyectos/go/src/github.com/julianskyline/motorcars-db
github.com/julianskyline/motorcars-models => /home/julianmarin/proyectos/go/src/github.com/julianskyline/motorcars-models)
Projects are in the same folder:
$GOPATH/src/github.com/julianskyline/errors
$GOPATH/src/github.com/julianskyline/motorcars-core-business
enter image description here
The go build/run work fine.
Error sudo docker-compose build:
Step 6/9 : RUN GOOS=linux go build -o $GOPATH/bin/github.com/julianskyline/motorcars-core-business main.go
---> Running in 45227441dfdd
go: github.com/julianskyline/errors#v0.0.0-00010101000000-000000000000 (replaced by /home/julianmarin/proyectos/go/src/github.com/julianskyline/errors): reading /home/julianmarin/proyectos/go/src/github.com/julianskyline/errors/go.mod: open /home/julianmarin/proyectos/go/src/github.com/julianskyline/errors/go.mod: no such file or directory
The command '/bin/sh -c GOOS=linux go build -o $GOPATH/bin/github.com/julianskyline/motorcars-core-business main.go' returned a non-zero code: 1
ERROR: Service 'api' failed to build
NOTE: The file /home/julianmarin/proyectos/go/src/github.com/julianskyline/errors/go.mod exists!
The Dockerfile is in $GOPATH/src/github.com/julianskyline/motorcars-core-business which means that the COPY . . within it will only copy $GOPATH/src/github.com/julianskyline/motorcars-core-business into the docker image.
The go.mod contains replace directives that reference folders not under $GOPATH/src/github.com/julianskyline/motorcars-core-business (e.g. $GOPATH/src/github.com/julianskyline/errors); this leads to compilation errors because those folders are not present within the docker image.
To resolve this you can:
Copy the entire julianskyline folder into the image (by moving Docker file into the parent folder, specifying the context on the command line or using docker-compose).
Remove the replace directives and letting go pull the files from github.
Posting answer as this was requested in the comments; I believe the comments provided sufficient info for the OP.
I built a web app with echo. Some source in server.go is
package main
import ...
type TemplateRenderer struct {
templates *template.Template
}
func (t *TemplateRenderer) Render(w io.Writer, name string, data interface{}, c echo.Context) error {
if viewContext, isMap := data.(map[string]interface{}); isMap {
viewContext["reverse"] = c.Echo().Reverse
}
return t.templates.ExecuteTemplate(w, name, data)
}
func main() {
e := echo.New()
e.Static("/static", "static")
renderer := &TemplateRenderer{
templates: template.Must(template.ParseGlob("public/views/*.html")),
}
e.Renderer = renderer
e.GET("/", func(c echo.Context) error {
return c.Render(http.StatusOK, "index.html", map[string]interface{}{})
})
e.Logger.Fatal(e.Start(":8080"))
}
Project tree
├── helper
│ └── string.go
└─── site
├── Dockerfile
├── go.mod
├── go.sum
├── server.go
├── public
│ └── views
│ └── index.html
└── static
I can run go run server.go to start server, everything works well. But got error if run it with docker.
Dockerfile
FROM golang:1.15.2-alpine3.12 AS builder
WORKDIR /app
COPY . .
WORKDIR /app/site
RUN CGO_ENABLED=0 GOOS=linux go build -o server
FROM alpine:3.12
COPY --from=builder /app/site /bin/.
ENTRYPOINT [ "server" ]
Built a docker image by
docker build -t gcr.io/${PROJECT_ID}/myapp -f site/Dockerfile .
Run app in docker
docker run --rm -p 8080:8080 gcr.io/${PROJECT_ID}/myapp
panic: html/template: pattern matches no files: `public/views/*.html`
goroutine 1 [running]:
html/template.Must(...)
/usr/local/go/src/html/template/template.go:372
main.main()
/app/site/server.go:76 +0x2af
It seems the public folder didn't been copied into image. But there wasn't any error when built the image. What's wrong?
Firstly, you are using a multi-stage Docker build which is perfect for only copying the compiled binary to the final image. In your Dockerfile, however, you are copying the entire build directory - the binary server as well as all the source.
Secondly, your main problem is when the image is run as a container, the default work directory is / - and thus any paths within your server are not find the html files in /bin/public.
If you ever need to debug a docker image - especially if it is an image based on a linux distro like alpine- simply:
docker run -it myimage /bin/sh
Anyway the 2 simple fixes to your docker:
FROM golang:1.15.2-alpine3.12 AS builder
WORKDIR /app
COPY . .
WORKDIR /app/site
RUN CGO_ENABLED=0 GOOS=linux go build -o server
FROM alpine:3.12
COPY --from=builder /app/site/server /bin
COPY --from=builder /app/site/public /public
ENTRYPOINT [ "/bin/server" ]
In Go 1.16 you can compile these files into the binary itself. So you'll need to upgrade the Go toolchain on your host system, and also the FROM line in your build stage in the Dockerfile. Go 1.16 adds the embed package and a new //go:embed directive to support this.
First, you need to tell the compiler to embed the template files, building a filesystem object:
import "embed"
// templateFiles contains the raw text of the template files.
//go:embed public/views/*.html
var templateFiles embed.FS
Then, when you go to use it, Go 1.16 also adds a corresponding ("html/template").ParseFS function:
renderer := &TemplateRenderer{
templates: template.Must(template.ParseFS(templateFiles)),
}
Now all of the files are embedded in the binary itself, and you shouldn't get "file not found" type errors. You might consider copying only the compiled binary and not anything else into the final image.
# Upgrade to Go 1.16
FROM golang:1.16-alpine3.12 AS builder
# Unchanged from original
WORKDIR /app
COPY . .
WORKDIR /app/site
RUN CGO_ENABLED=0 GOOS=linux go build -o server
FROM alpine:3.12
# Only copy the compiled binary and not the source tree
COPY --from=builder /app/site/server /bin
# Generally prefer CMD to ENTRYPOINT
CMD [ "server" ]
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