Building-a-Golang-application-Docker-image
We'll find a way; we always have.
After we have written the application after several months of hard work, how to deploy it? Let’s use a simple example of Hello Worldto learn.
The project structure is as follows:
1 | . |
The code content of hello.gois as follows:
1 | package main |
In order to keep up with the trend, we choose to use Docker deployment here.
First attempt.
For convenience, we are going to put all the content into Docker for compilation, and after some research, we get the following Dockerfile
file:
1 | FROM golang:alpine |
Next start building.
1 | docker build -t hello:v1 . |
It runs successfully, and then we look at the size of the image.
1 | docker images | grep hello |
A friend told me that I can compile the code first, and then copy it in, so I don’t need that huge base image, but it’s easy to say, I still spent some time learning, and finally the Dockerfile
looks like this:
1 | FROM alpine |
Let’s rebuild the image:
1 | $ docker build -t hello:v2 . |
Oh hoo, wrong report. The prompt hello cannot be found, so I forgot to compile hello.go first and execute it again.
1 | go build -o hello hello.go |
Finally, the build is successful, let’s try it out.
1 | $ docker run -it --rm hello:v2 |
No problem, let’s take a look at the content and size.
1 | $ docker run -it --rm hello:v2 ls -l /build |
Wow, it’s only 6.61MB this time, which is OK!
Third attempt.
Although the above image can be successfully built, there are still some shortcomings. It is not a multi-stage build.
We need to be able to build a docker image from Go code, which is divided into three steps:
- Compile Go code natively, if it involves cgo the cross-platform compilation will be more troublesome.
- Build a docker image with the compiled executable.
- Write a shell script or makefile to get these steps in one command.
Multi-stage builds are all about putting it all into oneDockerfile
, no source code leaks, no scripting for cross-platform compilation, and a minimal image.
Loving to learn and striving for perfection, I ended up writing the following Dockerfile
.
1 | FROM golang:alpine AS builder |
The first FROM starts with building a builder image in which the executable hellois compiled.
The part starting with the second FROM is to copy the executable hello from the first image, and use the smallest possible base image alpine to ensure that the final image is as small as possible.
As for why you don’t use a smaller scratch, it’s because there’s really nothing in scratch, and there is no chance to take a look if there is a problem, and alpine is only 5MB, which is good for our service will not have much impact.
Let’s run it first to verify:
1 | docker run -it --rm hello:v3 |
No problem, as expected! See what the size looks like:
1 | docker images | grep hello |
The size of the image built by the second method is exactly the same. Take a look at the contents of the mirror:
1 | $ docker run -it --rm hello:v3 ls -l /build |
Also, only one executable hello file builds perfectly!
https://blog.devgenius.io/tutorial-building-a-golang-application-docker-image-78e36d437c70