Forward the shell from the base image to child image - docker

My projects need to access private go modules and to access to modules it needs a GOPROXY.
So I've create an image from base image golang alpine
PROXY IMAGE:
FROM golang:1.16.4-alpine3.13 AS builder
ARG GITLAB_LOGIN
ARG GITLAB_TOKEN
modules.
WORKDIR /app
ENV GO111MODULE="auto"
ENV GONOSUMDB=*.someting.text
ENV GOPROXY=https://proxy.golang.org,direct
ENV GOPRIVATE="gitlab.something.text"
#ARG GOPROXY=http://localhost:41732,https://proxy.golang.org,direct
RUN apk add --no-cache git
RUN echo "machine gitlab.something.text login ${GITLAB_LOGIN} password ${GITLAB_TOKEN}" > ~/.netrc
COPY ["go.mod", "go.sum", "./"]
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build \
-installsuffix 'static' \
-o /app/proxy .
FROM scratch AS final
COPY --from=builder /app /app
CMD [ "/app/proxy"]
And the alpine image has a shell so I can execute commands with RUN.
But then I want to use my proxy image as a base image.
IMAGE THAT USES THE PROXY IMAGE:
FROM proxy:latest
COPY go.mod go.sum ./
RUN go mod download && go mod verify
COPY . .
RUN go build -o bin/app .
CMD [ "/app" ]
And then the image that is using the proxy image cannot access the shell.
So my question is, is there a way to forward/pass the shell from the base alpine image through the proxy image so the images that use the proxy image can access the shell that is provided by the alpine image.
Cause the error I get in the image that is using the proxy image as the base is
CI runtime create failed: container_linux.go:380: starting container process caused: exec: "/bin/sh": stat /bin/sh: no such file or directory: unknown
but of course this error does not happen in the proxy image cause it's base image is alpine.

The final stage of the proxy image is built FROM scratch. As you note this contains absolutely nothing, not even a shell, so if you want to use it as a base image, you need to change this to something else:
FROM golang:1.16.4-alpine3.13 AS builder
...
FROM alpine:3.13 # AS final
COPY --from=builder /app/proxy /usr/local/bin/proxy
CMD ["proxy"]
However, your Go build sequence produces a static binary; if it can run successfully as the only thing in a FROM scratch image then it can run successfully in any Linux environment. You can COPY --from an image as well as a build stage. It might be easier to not use the proxy as a base image, but instead copy it into other images where you need it:
FROM golang # not the proxy
# Get the proxy binary from the other image
COPY --from=proxy /app/proxy /usr/local/bin
# Build the Go application as above
COPY go.mod go.sum ./
...

Related

How to handle make missing from alpine docker image?

i have Makefiles on my app , and all my commands on Makefile
i put this on Dockerfile:
# start from the latest golang base image
FROM golang:alpine
RUN apk update && apk add --no-cache gcc && apk add --no-cache libc-dev
# Set the current working Directory inside the container
WORKDIR /app
# Copy go mod and sum files
COPY go.mod go.sum ./
# Download all dependencies. they will be cached of the go.mod and go.sum files are not changed
RUN go mod download
# Copy the source from the current directory to the WORKDIR inisde the container
COPY . .
# Build the Go app
RUN go build .
# Exporse port 3000 or 8000 to the outisde world
EXPOSE 3000
# Command to run the executable
CMD ["make", "-C", "scripts", "test" ]
CMD ["make", "-C", "scripts", "prod" ]
and got
docker: Error response from daemon: OCI runtime create failed:
container_linux.go:349: starting container process caused "exec:
\"make\": executable file not found in $PATH": unknown.
is it possible to run make -c scripts test in Docker? how to correct way to use this command in Docker?
in dockerfile i run golang:alpine
if you wish to avoid adding all dependencies of make to your alpine image and keep the shipping container small in size:
build your binary outside ur container and copy only the shippable binary to ur alpine container
build your binary in a normal golang container then copy the binary to a small shippable alpine container
you could give https://github.com/go-task/task a try, does not require too many dependencies compared to installing make in ur alpine container and replace ur make file with task file.

Golang based docker image build works but not scratch based image

I'm able to run a docker image of a web app when using golang:1.13 base, but not when using scratch. The working Dockerfile is:
FROM golang:1.13 AS builder
WORKDIR /app
COPY . .
RUN go build -o server
FROM golang:1.13
COPY --from=builder /app/server /app/server
COPY --from=builder /app/credentials/service-account.json /app/credentials/service-account.json
ENTRYPOINT ["/app/server"]
But when I change the final image base to scratch (line 6) like this:
FROM golang:1.13 AS builder
WORKDIR /app
COPY . .
RUN go build -o server
FROM scratch # <-- CHANGED
COPY --from=builder /app/server /app/server
COPY --from=builder /app/credentials/service-account.json /app/credentials/service-account.json
ENTRYPOINT ["/app/server"]
I get a standard_init_linux.go:211: exec user process caused "no such file or directory" error.
To build the docker image, I use docker build -t myimage ., and to run the image, I use docker run --rm -p 8080:8080 myimage:latest.
The app is a Go based web API that uses Gin framework, and GCP Service Account to access GCP services (the JSON file that I copy on build.)
Provided you are not using CGO (as #jakub mentioned), try disabling CGO in your build.
So change this line in your Dockerfile:
#RUN go build -o server
RUN CGO_ENABLED=0 go build -o server

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

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

Why is my final docker image in this multi-stage build so large?

After reading about the enormous image size reductions that are possible with multi-stage docker builds, I'm attempting to slim down the image size for a Dockerfile I have for building a Go binary. My Dockerfile is below.
# Configure environment and build settings.
FROM golang:alpine AS buildstage
ARG name=ddmnh
ENV GOPATH=/gopath
# Create the working directory.
WORKDIR ${GOPATH}
# Copy the repository into the image.
ADD . ${GOPATH}
# Move to GOPATH, install dependencies and build the binary.
RUN cd ${GOPATH} && go get ${name}
RUN CGO_ENABLED=0 GOOS=linux go build ${name}
# Multi-stage build, we just use plain alpine for the final image.
FROM alpine:latest
# Copy the binary from the first stage.
COPY --from=buildstage ${GOPATH}/${name} ./${name}
RUN chmod u+x ./${name}
# Expose Port 80.
EXPOSE 80
# Set the run command.
CMD ./ddmnh
The resulting image, however, doesn't seem to be size reduced at all. I suspect that the golang:alpine image is being included somehow. Below is a screenshot of the results of running docker build . on the Dockerfile above.
The alpine:latest image is only 4.15MB. Adding the size of the compiled binary (which is relatively small) I would expect no more than, say, maybe 15MB for the final image. But it's 407MB. I'm clearly doing something wrong!
How can I adjust my Dockerfile to produce an image of less size?
Buried deep in the Docker documentation I found that my ARG and ENV definitions were cleared when I started the final FROM. Redefining them solved the issue:
# Configure environment and build settings.
FROM golang:alpine AS buildstage
ARG name=ddmnh
ENV GOPATH=/gopath
# Create the working directory.
WORKDIR ${GOPATH}
# Copy the repository into the image.
ADD . ${GOPATH}
# Move to GOPATH, install dependencies and build the binary.
RUN cd ${GOPATH} && go get ${name}
RUN CGO_ENABLED=0 GOOS=linux go build ${name}
# Multi-stage build, we just use plain alpine for the final image.
FROM alpine:latest
ARG name=ddmnh
ENV GOPATH=/gopath
# Copy the binary from the first stage.
COPY --from=buildstage ${GOPATH}/${name} ./${name}
RUN chmod u+x ./${name}
# Expose Port 80.
EXPOSE 80
# Set the run command.
CMD ./ddmnh

Resources