Building Small Docker Images with Golang

Building Small Docker Images with Golang

Building Small Docker Images with Golang - In this post, we are going to see how we can reduce the size of our final docker image size for our golang app.

To achieve this, we are going to be using the scratch docker image. This is going to reduce the size of the final image by over 95%.

Why? By reducing the size of our final docker image, we are reducing the attack surface area. This means that attackers have less libraries, tools to exploit in our docker image. For this to be possible, we will need to statically compile our go application, so that it doesn’t need any external libraries.

To achieve this, we are going to use two stages to build our final docker image. In the first stage, we are going to use the official image for Golang, to statically compile our go application. Then, in the second stage, we will copy our statically compiled binaries from the first stage. To get started, we are going to create a Dockerfile at the root of our project. We are going to name it scratch.dockerfile – feel free to change the filename, if you wish to.

NB: If you are new to docker and containers in general, I suggest you start by reading the following post here for the basics. You can also learn how to get started with docker here.

Demo Application

In order to make a meaningful demo, I needed a barebone Golang application. Therefore, I used the basic example from Gin Web Framework which can be found here. I didn’t make any modification to the above code. I am confident enough you can replace it with your own custom code, without any modifications.

Stage  1 – Building the Golang Application

In this stage, we are going to compile our go application statically. But first things first, inside our dockerfile, we will start by setting the base image for docker. We will be using the latest available image for Golang; hence no tag. We will also name our stage as builder, this makes it easier to refer to it in the next stage.

FROM golang as builder

Next, we are going to change our working directory, to the directory in which we will build our application. I recommend using the same structure as your dev environment, from the GOPATH. For the Golang images, the GOPATH is /go. This will ensure that our go application behaves the same as it does in our dev environment.

WORKDIR /go/src/github.com/coding-latte/golang-docker-multistage-build-demo

Next, let’s copy our projects files, and install our projects dependencies and build our go application.

...
COPY . .
RUN go get .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
...

TIP: If you are using go modules, I recommend copying the project code in a directory outside the GOPATH (/go/src). This is because, once you put it inside the GOPATH, go modules will be disabled. This may cause your application to break.

...
WORKDIR /app/project-name
COPY . .
...

This is image title

Stage 2 – Deployment Image

In this stage, we are going to build the image which we will be deploying. So, let’s set our base image as  scratch – FROM scratch. Naming the stage is not necessary since it’s the last stage in our multi-stage build.

...
FROM scratch
...

Next, we are going to copy ca-certificates and compiled Golang static binaries from stage one – builder. First, let’s copy the ca-certificates, this is important if you will be using SSL/TLS to access your application. Otherwise you can skip this one if your app is not using SSL/TLS.

...
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
...

Then, change the working directory to bin (or any other name of your choice), and copy the compiled binaries to that directory:

...
WORKDIR /bin
COPY --from=builder /go/github.com/coding-latte/golang-docker-multistage-build-demo/app .
...

And finally, set our go app to be executed when the container starts and expose port 8080.

...
CMD ["./app"]
EXPOSE 8080
...

If your app is using another port, replace port 8080 with that port. And if it’s using multiple ports, separate them with a single whitespace.

NB: You can find the complete dockerfile here. And if you would rather use alpine linux docker image instead of scratch, you can find the corresponding dockerfile here.

Building the Docker Image

We can now go ahead and build our docker image:

docker build --rm -f "**scratch.dockerfile**" -t demo:latest .

NB: If you are not using scratch.dockerfile as the name of your dockerfile, replace that with correct name and path in the above command. You can learn more about building docker images here.

So, our final docker image size when using scratch docker image is 15.5MB, as compared to 844MB when using official Golang docker image. If you used alpine image instead of scratch, you can add just an extra 5MB on top of the scratch final size, where you will get about 20MB. This is still a huge size reduction from the official docker image for golang.

Source Code

You can find the source code for this post here.

Originally published by Maina Wycliffe at codinglatte.com

This is image title

docker go

Bootstrap 5 Complete Course with Examples

Bootstrap 5 Tutorial - Bootstrap 5 Crash Course for Beginners

Nest.JS Tutorial for Beginners

Hello Vue 3: A First Look at Vue 3 and the Composition API

Building a simple Applications with Vue 3

Deno Crash Course: Explore Deno and Create a full REST API with Deno

How to Build a Real-time Chat App with Deno and WebSockets

Convert HTML to Markdown Online

HTML entity encoder decoder Online

Docker Explained: Docker Architecture | Docker Registries

Following the second video about Docker basics, in this video, I explain Docker architecture and explain the different building blocks of the docker engine; docker client, API, Docker Daemon. I also explain what a docker registry is and I finish the video with a demo explaining and illustrating how to use Docker hub.

What's new in the go 1.15

Go announced Go 1.15 version on 11 Aug 2020. Highlighted updates and features include Substantial improvements to the Go linker, Improved allocation for small objects at high core counts, X.509 CommonName deprecation, GOPROXY supports skipping proxies that return errors, New embedded tzdata package, Several Core Library improvements and more.

Dockerization of Go

In this Goand Docker tutorial, Joan shares the alternatives when Dockerizing (containerizing with Docker) Go applications. Containers are one of the most popular paradigms to run software applications in all kind of environments (development, testing, production, etc)

Docker Tutorial for Beginners 8 - Build and Run C++ Applications in a Docker Container

Welcome to this on Docker Tutorial for Beginners. In this video provides an Introduction on C++ development with Docker containers. So we will see How to ship C++ Programs in Docker.

WordPress in Docker. Part 1: Dockerization

This entry-level guide will tell you why and how to Dockerize your WordPress projects.