DEV Community

Asanka Boteju
Asanka Boteju

Posted on

Docker Images for Go (Golang) Small, Faster Docker Images and Security

During the weekend I was doing some research on docker images specifically to be used with Go (Golang) applications. hence, thought of sharing the interesting findings as that might be useful to someone who's exploring the same for their tech project works.

Here, I will be elaborating a few different ways we can build an image for Golang and also highlight some of the security considerations that we need to take into account when picking one.

For this exercise I was using a simple Rest API developed using Go (Golang) using the Gin Framework.

The Gin framework is a popular web framework for Go (Golang) that is designed to be fast, easy to use, and highly efficient.
 
Here's a brief summary of its key features and characteristics;

Key Features

  • Performance: Gin is known for its high performance. It is one of the fastest Go web frameworks, providing a minimal overhead compared to other frameworks.

  • Fast HTTP Router: Gin uses a fast HTTP router and supports routing with methods like GET, POST, PUT, DELETE, etc. It also supports middleware and route grouping.

  • Middleware Support: Gin provides a way to use middleware to handle tasks such as logging, authentication, and other pre- or post-processing of requests.

  • JSON Validation: The framework offers built-in support for JSON validation and binding request data to Go structs, making it easier to work with JSON payloads.

  • Error Handling: Gin has a structured way of handling errors and provides a central error management system, allowing you to handle errors gracefully.

  • Template Rendering: While Gin is primarily designed for API development, it supports HTML template rendering if needed.

  • Request Handling: Supports different methods for request handling including form data, JSON payloads, and URL parameters.

  • Built-in Debugging: Gin provides detailed error messages and debugging information that can be useful during development.

Key Components

  • Router: The core component that handles routing of HTTP requests to the appropriate handlers.

  • Context: A structure that carries the request and response data, and
    provides methods to handle them. It's used extensively within handlers.

  • Engine: The primary instance of the Gin application, which is configured with routes, middleware, and other settings.

  • Middleware: Functions that execute during the request lifecycle, allowing you to perform tasks like logging, authentication, and more

Enough about Gin Framework :) now let's move into the main topic and talk about the Tests carried out.

Test 1 (Regular Docker Build)

In this Test we will be using a Official Standard/Regular Go Base Image

# Official Go Base Image
FROM golang:1.21.0

# Create The Application Working Directory
WORKDIR /app

# Copy and Download Dependencies
COPY go.mod go.sum .
RUN go mod download

# Copy Source and Build The Application
COPY . .
RUN go build -o main .

# Expose The Port
EXPOSE 8081
CMD ["./main"]
Enter fullscreen mode Exit fullscreen mode

Image Size

Image description


Test 2 (Multi-Stage Docker Build with Alpine Image)

In this Test we will be use the Alpine version of the Go Base Image which is slightly more lightweight

# Official Go Apline Base Image
FROM golang:1.21.0-alpine as builder

# Create The Application Directory
WORKDIR /app

# Copy and Download Dependencies
COPY go.mod go.sum .
RUN go mod download

# Copy The Application Source & Build
COPY . .
RUN go build -o main .

# Final Image Creation Stage
FROM alpine:3.19

WORKDIR /root/

# Copy The Built Binary
COPY --from=builder /app/main .

# Expose the port
EXPOSE 8081
CMD ["./main"]

Enter fullscreen mode Exit fullscreen mode

Image Size
Image description

Here you can see that with a multi-staged build the image sizes are significantly reduced.


Test 3 (Distroless Build)

In this Test we will be using a Googles Distroless Go Base Image. Distroless images are known for being lightweight and secure containing only the minimal files required. In this these debug shells and unnecessary packages removed. therefore, you sacrifice the flexibility of having a package manager and a shell.

# Build Stage
FROM golang:1.21.0 as builder

# Set The Application Directory
WORKDIR /app

# Copy and Download Dependencies
COPY go.mod go.sum .
RUN go mod download

# Copy The Application Source and Build the application
COPY . .
RUN CGO_ENABLED=0 go build -o main .

# Final Image Creation Stage
FROM gcr.io/distroless/static-debian12

# Copy the built binary
COPY --from=builder /app/main /
CMD ["/main"]
Enter fullscreen mode Exit fullscreen mode

Image Size
Image description


CGO_ENABLED=0: This is an environment variable setting that disables CGO (C-Go). CGO is a feature of Go that allows Go packages to call C code. Setting CGO_ENABLED=0 ensures that the build does not depend on any C libraries, producing a fully static binary. This is useful for creating lightweight and portable Go binaries that can run on any system without requiring additional dependencies.

Putting it all together, RUN CGO_ENABLED=0 go build -o main . means that Docker will execute a command to build a Go application in the current directory, producing a static binary named main that does not depend on any C libraries.

Summary

Image Size
Image description

Even though the Distroless images are slightly bigger than the alpine images the security threat/vulnerability consideration might compel to choose Distroless!. Hence think about all these facts and pick the one that matches your requirements

Alpine with Multi-Stage Builds is a good choice if you need more control over the build process, and if compatibility issues with musl libc are not a concern. It provides flexibility and is smaller than many other base images, but still includes more components than Distroless images which can lead to potential security vulnerabilities/threats.

Google Distroless Images are ideal for maximizing security and minimizing the attack surface. They provide a very minimal runtime environment, which can be beneficial for production systems where security is a priority. However, you sacrifice the flexibility of having a package manager and a shell.

Recommendation:

Use Alpine if you need flexibility and control over the build environment, and if compatibility issues with musl libc are not a problem.

Use Google Distroless if security and minimizing the attack surface are your top priorities, and you can ensure that your application has all its dependencies bundled properly.

Hope this was interesting and thank you for your time!

Top comments (1)

Collapse
 
kcq profile image
Kyle Quest

In addition to creating container images from "scratch" another option is to use DockerSlim ( github.com/mintoolkit/mint ) where you can point to the first version of your image and it'll create a minimal container images for you from "scratch" with your application binary and any other files your app actually needs.

Take a look at the examples repo. It has several Go application examples and many more: github.com/mintoolkit/examples