How hard can it be – to create the smallest possible container image to run a Go application? It is not hard to create a container image that contains an binary executable file – and that is what a Go application turns into during the build process. However, that does not automatically produce a very small image or one that actually can run the application successfully. There are a few things to consider, as I found out during a few frustrating hours.
In the end it is all simple enough. When you know what to do, then it is all quite simple. Obviously.
What I had to learn:
- a Go application built just like that in my Ubuntu environment will not run just like that in an Alpine based container image; it should not have been a surprise, but still I had not considered this
- a Go application should be built with statically linked dependencies – to make runtime behavior not dependent on libraries being present (at all or in the expected location); you’ll enable a statically linked binary by setting CGO_ENABLED=0
- the binary executable can be created a little smaller by stripping off the debugging symbols; this done using compile flags -ldflags=”-s -w”
- in order to make HTTPS calls from the Alpine image it is required to have (recent) details for certificate authorities loaded into the container image
The Go build command I use to create the binary executable that will find its way into the container:
CGO_ENABLED=0 GOOS=linux go build -o person-producer -ldflags=”-s -w”
I run this command in the directory that contains the Go source file with the main package. person-producer is the name of the executable file that is the result of the build process; it is a standalone binary executable file of about 6MB.
The container image is built from a Dockerfile called `DockerfileAlpine`; it is located in the same directory as `person-producer.go`. It has the following content (to be processed to produce the small container image I am hoping for):
# add ca-certificates to allow signed communications
RUN apk –no-cache add ca-certificates
# copy the application’s binary executable
COPY person-producer ./
# run the executable when the container is started
The container image is created from the latest *alpine* image. A directory called */app* is created and used for copying various files to. The file`person-producer` (the binary executable produced using `go build`) is copied to the container, to the work directory /app. The `RUN apk` line adds the required certificates to the image. The last line `CMD [“./person-producer”]` makes the application start up when a container based on this image starts running
Build the container image locally using this command:
docker build -t person-producer:1.0.0 -f DockerfileAlpine .
Building the container will take some time the first time because then the base image needs to be loaded. It will probably last less than 15 seconds on any subsequent iteration.
You can check the success of the build process by inspecting the command line output, using `docker images | grep person-producer` and by running a container based on the image:
docker run –name person-messenger -e STREAM_DETAILS_SECRET_OCID=$STREAM_DETAILS_SECRET_OCID person-producer:1.0.0
The size of the container image is about 12.2 MB.
My favorite build options for Go by Gaurav Kamathe – https://opensource.com/article/22/4/go-build-options
Compiling Your Go Application for Containers – Powerful Command-Line Applications in Go — by Ricardo Gerardi – https://medium.com/pragmatic-programmers/compiling-your-go-application-for-co-ntainers-b513190471aa