We are able to run an application in seconds with Docker nowadays just by grabbing specific image within several official / custom docker images and that’s it. However, this easiness, using ready images for your application, may let us to have docker images with a size hundreds of megabytes ! In this article, I will show you how to create damn small images for Go application and run them. Let’s rock!
Sample Go REST API
I assume you have already have Golang and necessary tools installed on your system. This project has no dependent external library, so just create a file called main.go
and put following content inside
package main
import (
"net/http"
"fmt"
"log"
)
func main() {
http.HandleFunc("/", func(writer http.ResponseWriter, request *http.Request) {
fmt.Fprint(writer, "Hello, World")
})
log.Fatal(http.ListenAndServe(":3000", nil))
}
This is a sample application that listens requests on port 3000 and responds Hello, World. You can try it yourself by executing go run main.go
, and going to http://localhost:3000
Dockerize Go Project with Official Image
What is your first step for dockerizing a project? Just search official image you need on Docker Hub and just use inside Dockerfile right? Let’s use this approach first. Create a Dockerfile
inside project folder and use official golang image to run your project inside container.
FROM golang
ADD . ./
RUN go build -o main
ENV PORT 3000
EXPOSE 3000
CMD [“/main”]
Build your docker image with docker build -t my-go-app-golang .
Now check your image with docker images | grep my-go-app-golang
739 MB! If you are happy with this size, I think you don’t need to go further, thanks for reading so far 🙂 If you want to make this smaller, continue to read…
Dockerize Go Project with Official Alpine Image
Docker Alpine images are the images with minimal dependencies that means you will not be able to see lots of tools inside it when you exec and check. For a specific Golang application, we don’t need so much dependencies and this alpine images may fit well with our needs.
Use Alpine Image inside your Dockerfile
like below;
FROM golang:alpine
ADD . ./
RUN go build -o main
ENV PORT 3000
EXPOSE 3000
ENTRYPOINT [“/main”]
Build it wit docker build -t my-go-app-alpine .
Now check your image with docker images | grep my-go-app-alpine
275 MB, is it small enough for you ? Ok, keep reading for next surprize docker image size.
Dockerize Go Project with Docker Stratch Image
Think about a docker image and there is nothing inside it. Yes, it is Docker Scratch image. You cannot pull this image, but you can refer this in your Dockerfile. The very first next line after referral will be your 1st layer of filesystem. The main strategy here is, provide your binary as entrypoint this scratch image and that’s all.
We need a binary of our Golang application to provide as an entrypoint to our scratch image. In order to build a binary, go to your project folder and execute:
CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main .
Here with CGO_ENABLED=0
we are saying that disable cgo and build golang application statically that means you will have all the dependencies once you copy this binary to image. -a
is for re build entire packages to be sure you have all the dependencies. After this execution, you will have a binary inside your project folder.
We have a binary and now create Dockerfile
with following content.
FROM scratch
ADD main ./
ENV PORT 3000
EXPOSE 3000
ENTRYPOINT [“/main”]
You can create your image with docker build -t my-go-app-scratch .
And when you check your image size docker images | grep my-go-app-scratch
6.1 MB! If this is not small enough for you, keep reading …
Just joking 🙂 This is the minimal image I can provide you for this application. We have make image 100 times smaller than initial one. This minimal image will keep our motivation very high because, you will be able to deploy this image in a very short time on any kind of environment.
Conclusion
Using an official docker image for an application is very good choice at first, but if you have good devops mindset, I know you will try to make docker image smaller. This time, the main step will be more probably using an alpine image or other small images like busybox, etc… If you are able to convert your project to a binary, do use docker scratch image to have smallest docker image.
You can find application on Github here