Building Go binaries with Docker

Depending on your target architecture, you may wish to build your Go binaries with Docker to maintain a reproducible build, limit your build size, and minimize the attack vectors for your service. Using multistage Docker builds can help us to accomplish this task.

To perform these actions, you must have a recent version of Docker installed. The multistage builds feature that we are going to use requires Docker version 17.05 or higher in both the daemon and the client. You can find the most recent version of Docker for your OS, as well as instructions on how to install it, at https://docs.docker.com/install/.

Consider the following simple package that logs a debug message to the screen:

package main
import "go.uber.org/zap"
func main() {
zapLogger: = zap.NewExample()
defer zapLogger.Sync()
zapLogger.Debug("Hi Gophers - from our Zap Logger")
}

If we want to build this and execute it within a Docker container while minimizing dependencies, we can use a multistage Docker build. To do so, we can perform the following steps:

  1. Initialize the current directory as the root of a module by executing the following:
go mod init github.com/bobstrecansky/HighPerformanceWithGo/11-deploying-go-code/multiStageDockerBuild
    1. Add the vendor repositories by executing the following:
    go mod vendor

    We now have all of the required vendor packages (in our case, the Zap logger) available in our repository.  This can be seen in the following screenshot:

    1. Build our zapLoggerExample Docker container.  We can build our container using the following Dockerfile:
    # Builder - stage 1 of 2
    FROM golang:alpine as builder
    COPY . /src
    WORKDIR /src
    RUN CGO_ENABLED=0 GOOS=linux go build -mod=vendor -o zapLoggerExample
    # Executor - stage 2 of 2
    FROM alpine:latest
    WORKDIR /src/
    COPY --from=builder /src/zapLoggerExample .
    CMD ["./zapLoggerExample"]
    Please note that we use the golang:alpine image for building the Go binary, as it's one of the simplest Docker images that contains the necessary elements to successfully build our Go binary. We use the alpine:latest image for executing the Go binary, as it's one of the simplest Docker images that contains the necessary elements to successfully run our Go binary.

    In this Dockerfile example, we are using a multistage Docker build to build and execute our binary. In stage 1 of 2 (the builder stage), we use a golang alpine image as a base.  We copy all of our files from our current directory into the /src/ directory on the Docker container, we make /src/ our working directory, and we build our Go binary.  Disabling cgo, building for our Linux architecture, and adding the vendor directory we created in step 1 can all help minimize build size and time.  

    In stage 2 of 2 (the executor stage), we use a basic alpine Docker image, make /src/ our working directory, and copy the binary we built in the first stage to this Docker container. We then execute our logger as the final command within this Docker build.

    1. After we have our necessary dependencies together, we can build our Docker container. We do this by performing the following command:
    docker build -t zaploggerexample .
    1. Once we have our Docker container built, we can execute it by performing the following command:
    docker run -it --rm zaploggerexample

    In the following screenshot, you can see our build and execution steps being completed:

    Building our Go programs in multistage Docker containers can be helpful in creating reproducible builds, limiting binary size, and minimizing the attack vectors for our services by using only the bits and pieces we need.

    ..................Content has been hidden....................

    You can't read the all page of ebook, please click here login for view all page.
    Reset
    3.143.4.181