Build a Lightweight and Optimize Docker Images for Go Application
Overview
This article will guide you through the process of building a lightweight Docker image for a Golang application. We’ll practices the techniques to minimize the Docker image size. We’ll leverage multi-stage builds and a minimal base image.
Prerequisites:
Before proceed with the tutorial, make sure you have the following prerequisites in place:
- Golang Installed: Ensure that you have Golang installed on your local development machine.
- Docker Installed: You’ll need Docker installed on your system to create and build Docker images.
Step 1: Set Up Your Golang Application
Create a file named main.go
with the following content:
// main.go
package main
import (
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.GET("/", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "Hello, Go Docker World!",
})
})
r.Run(":8080")
}
Create Go Module and Download Dependencies
Run the following commands in the terminal to set up a Go module, which will manage the project’s dependencies it will create package and add it to your go.mod
file along with its checksum in the go.sum
file:
go mod init my-go-app
go get -d ./...
Step 2: Write the Dockerfile
Create a file named Dockerfile
in the same directory as your main.go
file. This file will define the steps needed to build your optimize Docker image.
### Step 1: Build stage
FROM golang:1.20-alpine as builder
WORKDIR /app
# Copy the Go module files and download dependencies
COPY go.mod go.sum ./
RUN go mod download
# Copy the application source code and build the binary
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o myapp
###
## Step 2: Runtime stage
FROM scratch
# Copy only the binary from the build stage to the final image
COPY --from=builder /app/myapp /
# Set the entry point for the container
ENTRYPOINT ["/myapp"]
In this Dockerfile, this use a multi-stage build to separate the build environment from the runtime environment, which helps create a lightweight image.
- In the build stage, use the official Golang Docker image as the base image (
golang:1.20-alpine
). This is a minimal Alpine Linux-based image with Golang pre-installed. - Set the working directory to
/app
, where our Golang application code will be copied. - Copy only the
go.mod
andgo.sum
files to leverage Docker's build cache for faster dependency resolution. Rungo mod download
to download the Go module dependencies. - Next, copy the entire application source code into the container and build the binary using
go build
. TheCGO_ENABLED=0
flag disables the use of CGo, resulting that won't have external runtime dependencies. - In the runtime stage, use the
scratch
base image, which is the most minimal base image possible, or optionally you can also usealpine.
- Finally, copy only the built binary (
myapp
) from the build stage into the runtime image and set it as the entry point for the container.
Step 4: Build the Docker Image
To build the Docker image, open a terminal, navigate to the directory containing the Dockerfile
, and run the following command:
docker build -t my-go-app .
Then after that you can see the image is stored on you local, you can check the image by following docker command:
docker images
Comparison
Below is the comparison between build process using multi-stage using alpine image and Scratch image, and non multi-stage using golang alpine base image.
The comparison of these three Docker images demonstrates the significance of choosing the appropriate build approach and base image for Go applications. By adopting multi-stage builds and utilizing lightweight base images like Alpine or Scratch, you can significantly reduce the size of your Docker images.
Conclusion
Multi-stage builds and use minimal base image offer substantial benefits over traditional builds. They lead to smaller and more optimized Docker images.