Dockerfile for Go and chromedp - docker

I'm trying to implement a Dockerfile to contain both my go binary and also chromedp. I manage to build the image and I can run the image and the go binary runs as expected, all but chromedp.
Thanks in advance!
Error message i recieve:
Error exec: "google-chrome": executable file not found in $PATH running chromedp
Dockerfile
# syntax=docker/dockerfile:1
##
## Build
##
FROM golang:1.17-bullseye as build
WORKDIR /app
COPY . ./
RUN go mod download
COPY *.go ./
RUN go build -o /docker-scraper
EXPOSE 8080
FROM chromedp/headless-shell:latest
CMD ["/docker-scraper"]
##
## Deploy
##
FROM gcr.io/distroless/base-debian11
WORKDIR /
COPY --from=build /docker-scraper /docker-scraper
EXPOSE 8080
USER nonroot:nonroot
ENTRYPOINT ["/docker-scraper"]

Error exec: "google-chrome": executable file not found in $PATH running chromedp
This is because you did not run your go program in chromedp/headless-shell. You define multi-stage builds, but with this, only the last stage will be act as the base image of final image.
This means your go program in fact runs in gcr.io/distroless/base-debian11, not headless-shell.
To learn how to run your own program in headless-shell, you could refers to its official document:
When using chromedp/headless-shell as a base image to build an image that runs your own program, You could experience zombie process problem. To reap zombie processeses, use dumb-init or tini on your Dockerfile's ENTRYPOINT
FROM chromedp/headless-shell:latest
...
# Install dumb-init or tini
RUN apt install dumb-init
# or RUN apt install tini
...
ENTRYPOINT ["dumb-init", "--"]
# or ENTRYPOINT ["tini", "--"]
CMD ["/path/to/your/program"]
A minimal workable example as next.
main.go:
package main
import (
"context"
"log"
"fmt"
"time"
"github.com/chromedp/chromedp"
)
func main() {
ctx, cancel := chromedp.NewContext(
context.Background(),
chromedp.WithLogf(log.Printf),
)
defer cancel()
ctx, cancel = context.WithTimeout(ctx, 15*time.Second)
defer cancel()
err := chromedp.Run(ctx,
chromedp.Navigate(`https://golang.org/pkg/time/`),
)
if err != nil {
fmt.Println(err)
}
fmt.Println("done")
}
Dockerfile:
FROM golang:latest as build
WORKDIR /go/src/app
COPY ./main.go .
RUN go mod init docker-scraper; go mod tidy
RUN go build
FROM chromedp/headless-shell:latest
RUN apt-get update; apt install dumb-init -y
ENTRYPOINT ["dumb-init", "--"]
COPY --from=build /go/src/app/docker-scraper /tmp
CMD ["/tmp/docker-scraper"]
docker-compose.yaml:
version: '3'
services:
goservice:
build: .
Execution:
$ docker-compose up
Recreating chromedp-docker_goservice_1 ... done
Attaching to chromedp-docker_goservice_1
goservice_1 | done
chromedp-docker_goservice_1 exited with code 0
You could see no error about google-chrome now.

Related

app_1 | standard_init_linux.go:211: exec user process caused "exec format error"

I have a golang application and I created dockerfile, which make multi-stake build.
But I catch this error app_1 | standard_init_linux.go:211: exec user process caused "exec format error" every time when try to do docker-compose up.
Dockerfile:
# Start from the latest golang base image
FROM golang:1.13 as builder
# Set the Current Working Directory inside the container
WORKDIR /memesbot
# Copy go mod and sum files
COPY go.mod go.sum ./
# Download all dependencies. Dependencies will be cached if the go.mod and go.sum files are not changed
RUN go mod download
# Copy the source from the current directory to the Working Directory inside the container
COPY . .
# Build the Go app
RUN go build -o /memesbot/cmd/main .
######## Start a new stage from scratch #######
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
# Copy the Pre-built binary file from the previous stage
COPY --from=builder /memesbot/cmd/main .
RUN chmod +x ./main
# Command to run the executable
CMD ["./main"]
And docker-compose:
version: '3'
services:
app:
build:
context: .
dockerfile: Dockerfile
ports:
- "7777:7777"
environment:
TELEGRAM_TOKEN: xxxx
How can I fix this error?

starting container process caused "exec: \"go\": executable file not found in $PATH": unknown

I am trying to containerize and as well as start my Go lang application using Docker-compose,
The image is built successfully according to the logs but my container does not for docker-compose up and it throws the following error to my console.
Cannot start service app: OCI runtime create failed: container_linux.go:346: starting container process caused "exec: \"go\": executable file not found in $PATH": unknown
Here is what my Docker file looks like.
ARG GO_VERSION=1.13
FROM golang:${GO_VERSION}-alpine AS builder
# We create an /app directory within our
# image that will hold our application source
# files
RUN mkdir /raedar
# Create the user and group files that will be used in the running container to
# run the process as an unprivileged user.
RUN mkdir /user && \
echo 'nobody:x:65534:65534:nobody:/:' > /user/passwd && \
echo 'nobody:x:65534:' > /user/group
# Install git.
# Git is required for fetching the dependencies.
# Allow Go to retrieve the dependencies for the buld
RUN apk update && apk add --no-cache ca-certificates git
RUN apk add --no-cache libc6-compat
# Force the go compiler to use modules
ENV GO111MODULE=on
ADD . /raedar/
WORKDIR /raedar/
RUN go get -d -v golang.org/x/net/html
COPY go.mod go.sum ./
COPY . .
# Compile the binary, we don't want to run the cgo
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o bin/main cmd/app/main.go
# Final stage: the running container.
FROM scratch AS final
WORKDIR /root/
# Import the user and group files from the first stage.
COPY --from=builder /user/group /user/passwd /etc/
# Import the Certificate-Authority certificates for enabling HTTPS.
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
# Import the compiled executable from the first stage.
COPY --from=builder /raedar/bin/main .
EXPOSE 8080
# Perform any further action as an unprivileged user.
USER nobody:nobody
# Run the compiled binary.
CMD ["./main"]
The error shows you're trying to run go, not ./main:
exec: \"go\": executable file not found in $PATH
A matching Dockerfile would have the line CMD ["go", "./main"] rather than CMD ["./main"].
So either you're unexpectedly building a different Dockerfile, or you're changing the command when you run a container with that image. In particular, if you're using docker-compose, make sure you're not setting command: go ./main or entrypoint: go, either of which could cause this behavior.

Can't get Docker container to run on localhosts it says "connection reset"?

Can't get Docker container to run on localhosts it says "connection reset" when going to localhost:8080.
Here is what I do know so bear with me:
The code runs locally when I run it and I am able to see the http://localhost:8080 page
The Docker build command completes with no errors
Error when curling the server:
curl -X GET http://localhost:8080
curl: (52) Empty reply from server
docker run -d -p 8080:8080 --name goserver -it goserver
The Dockerfile:
FROM golang:1.9.2
ENV SRC_DIR=/go/src/
ENV GOBIN=/go/bin
WORKDIR $GOBIN
# Add the source code:
ADD . $SRC_DIR
RUN cd /go/src/;
RUN go get github.com/gorilla/mux;
CMD ["go","run","main.go"]
#ENTRYPOINT ["./main"]
EXPOSE 8080
Here is the go code:
package main
import (
"fmt"
"net/http"
"github.com/gorilla/mux"
)
func main() {
r := mux.NewRouter()
r.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "<h1>This is the homepage. Try /hello and /hello/Sammy\n</h1>")
})
r.HandleFunc("/hello", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "<h1>Hello from Docker!\n</h1>")
})
r.HandleFunc("/hello/{name}", func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
title := vars["name"]
fmt.Fprintf(w, "<h1>Hello, %s!\n</h1>", title)
})
http.ListenAndServe(":8080", r)
}
You're starting your image in detached (-d) mode - this is why you don't see error messages. There are few issues with the Dockerfile, it should be fixed with #andre answer, but most probably you forgot to rebuild the image and didn't see the effect.
I'm submitting this answer to suggest some improvements of your Dockerfile:
# first stage - builds the binary from sources
FROM golang:1.12.14-alpine3.10 as build
# using build as current directory
WORKDIR /build
# Add the source code:
COPY main.go ./
# install build deps
RUN apk --update --no-cache add git
# downloading dependencies and
# building server binary
RUN go get github.com/gorilla/mux && \
go build -o server .
# second stage - using minimal image to run the server
FROM alpine:3.10
# using /app as current directory
WORKDIR /app
# copy server binary from `build` layer
COPY --from=build /build/server server
# binary to run
CMD "/app/server"
EXPOSE 8080
I've split your Dockerfile into two stages: build and run. Build stage is responsible for building the server binary, run stage is responsible for running it. See https://docs.docker.com/develop/develop-images/multistage-build/
Then I combined multiple RUNs into single one: go get github.com/gorilla/mux && go build -o server . to avoid creating redundant layers.
I fixed WORKDIRs and give them readable semantical names.
Don't forget to rebuild it with docker build . -t goserver and run it with
docker run -p 8080:8080 --name goserver goserver
If everything is fine, and you're ready to (and you need to) start in the detach mode, then add -d flag.
Also, you may want to check Dockerfile best practices.
your WORKDIR is wrong, based on how you are setting your CMD
change your WORKDIR to SRC_DIR instead of GOBIN and it will work
You could also run go install main.go on your Dockerfile
go install will create the executable and move it to the bin folder
here is an example of a working Dockerfile:
FROM golang:1
ENV SRC_DIR=/go/src/
ENV GOBIN=/go/bin
WORKDIR $SRC_DIR
# Add the source code:
ADD . $SRC_DIR
RUN go get github.com/gorilla/mux;
RUN go install main.go
WORKDIR $GOBIN
ENTRYPOINT ["./main"]
EXPOSE 8080
What was happening is: your CMD was failing because the WORKDIR was pointing to the bin folder.
A few side notes:
Don't do: RUN cd, as per: Docker : RUN cd ... does not work as expected

How to run a docker container created with go binary?

I am trying to create a docker container with a Dockerfile and a go file binary.
I have two files in my folder: Dockerfile and main, where the latter is a binary of my simple go file.
Contents of Dockerfile:
FROM golang:1.11-alpine
WORKDIR /app
COPY main /app/
RUN ["chmod", "+x", "/app/main"]
ENTRYPOINT ["./main"]
I tried following steps:
sudo docker build -t naive5cr .
sudo docker run -d -p 8080:8080 naive5cr
The error which i see in thru "docker logs " :
standard_init_linux.go:207: exec user process caused "no such file or directory"
my go file content [i think it is irrelevant to the problem]:
func main() {
http.HandleFunc("/", index)
http.ListenAndServe(port(), nil)
}
func port() string {
port := os.Getenv("PORT")
if len(port) == 0 {
port = "8080"
}
return ":" + port
}
the binary "main" runs as expected when run standalone. so there is no problem with the content of go file.
You need to compile with CGO_ENABLED=0 to prevent links to libc on Linux when networking is used in Go. Alpine ships with musl rather than libc, and attempts to find libc result in the no such file or directory error. You can verify this by running ldd main to see the dynamic links.
You can also build on an Alpine based host to link to musl instead of libc. The advantage of a completely statically compiled binary is the ability to run on scratch, without any libraries at all.
go compiles down to native code, so make sure to build your go code on the Docker image, instead of copying the binary to the docker image.
e.g.
FROM golang:1.11-alpine
WORKDIR /app
ADD . /app
RUN cd /app && go build -o goapp
ENTRYPOINT ./goapp
Also as a bonus, here is how to create really tiny Docker images with multistage Docker builds:
FROM golang:1.11-alpine AS build-env
ADD . /src
RUN cd /src && go build -o goapp
FROM alpine
WORKDIR /app
COPY --from=build-env /src/goapp /app/
ENTRYPOINT ./goapp

Can't `go get` dependencies during docker build

I'm fairly new to both Docker and go, so this might be something obvious, but my google searches haven't found anything.
I'm trying to build a simple go program with docker, but I'm havign trouble with dependencies.
go file:
package main
import (
"fmt"
"log"
"html"
"net/http"
"github.com/gorilla/mux"
)
func hello(writer http.ResponseWriter, r *http.Request) {
path := mux.Vars(r)["rest"]
fmt.Fprintf(writer, "Hello, %q", html.EscapeString(path))
}
func main() {
router := mux.NewRouter().StrictSlash(true)
router.HandleFunc("/{rest:.*}", hello)
log.Println("Listening...")
log.Fatal(http.ListenAndServe(":8080", router))
}
Docker file:
FROM golang:latest
RUN mkdir /app
ADD ./HelloWorld.go /app/
WORKDIR /app
RUN go get ./*.go
RUN go build -o main .
CMD ["/app/main"]
Error:
Sending build context to Docker daemon 6.482MB
Step 1/7 : FROM golang:latest
---> 138bd936fa29
...
Step 5/7 : RUN go get ./*.go
---> Running in 1e29844961a2
HelloWorld.go:9:5: 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)
The command '/bin/sh -c go get ./*.go' returned a non-zero code: 1
You may use my Dockerfile as a base. First stage produces working image. That’s enough for many cases.
https://github.com/lisitsky/go-site-search-string/blob/light-docker/Dockerfile
If you want to shrink image size from ~800MB to about ~10-20MB use second stage too.
Just use $GOPATH everywhere in your path to build your images, e.g. $GOPATH/src/app instead of /app
Full example of multistage build:
FROM golang:alpine as builder
RUN apk add --update git
RUN mkdir -p $GOPATH/src/build
ADD . $GOPATH/src/build/
WORKDIR $GOPATH/src/build
RUN go get ./...
RUN go build -o main .
FROM alpine
RUN adduser -S -D -H -h /app appuser
USER appuser
COPY --from=builder /go/src/build/main /app/
WORKDIR /app
CMD ["./main"]

Resources