Building a Golang Docker image

Building a Golang Docker image

 
PinClipart.com_shipping-container-clip-art_3317140.png
 

“It works on my laptop” is over.

Let’s build a Docker container with Go! It sounds complicated, but it really isn’t. All you need are a few tools, a couple of simple commands, and ideally some cake. (The cake isn’t strictly necessary, but coding is hungry work. It’s a good idea to have some tasty snacks on hand, to keep your strength up.)

Docker and Go make perfect partners, as we’ll see. Let’s start by getting you set up with the right tools.

Install Docker

Docker is a tool for building and running containers (you can read more about these in the Cloud Native DevOps with Kubernetes book). A container is a simple way of distributing software, using a single file called a container image.

The technical details don’t really matter for our purposes, but you can think of a container image as being like a ZIP file. It’s a single binary file that has a unique ID and holds everything needed to run the container. This means that if you upload your container to a public repository such as Docker Hub, you can run it anywhere using just Docker. We’ll see how to do that shortly.

If you don’t have Docker installed already, download it. Once you’ve installed Docker, run the application, and you’ll be all set for the next steps.

Install Golang

If you’re new to Go, then welcome! (If you’d like some help learning Go, see my Learn Go with Mentoring page.)

Go is a modern programming language (developed at Google since 2009) that prioritizes simplicity, safety, and readability, and is designed for building large-scale concurrent applications, especially cloud native web services. It’s also a lot of fun to program in. (We often refer to Go as ‘Golang’, because that’s easier to search for, but the name of the language is actually just ‘Go’.)

To get started programming with Go, you’ll need the ‘Go tools’ installed: that’s the Go compiler itself, libraries, supporting tools, and so on. Download the appropriate package for your system here:

Clone the Docker Golang example

If you already have a Go program you’d like to create a Docker container from, great! You can use that. If not, here’s one I prepared earlier:

This is a GitHub repo which accompanies the Cloud Native DevOps with Kubernetes book. It’s a very simple demonstration app: a little HTTP server which displays ‘Hello, world’ in your browser.

If you don’t have Git installed, see the Download Git page for instructions. Then run these commands to clone (download) the example repo:

git clone https://github.com/cloudnativedevops/demo.git
cd demo/hello

Here’s the program:

package main

import (
    "fmt"
    "log"
    "net/http"
)

func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "Hello, 世界")
}

func main() {
    http.HandleFunc("/", handler)
    log.Fatal(http.ListenAndServe(":8888", nil))
}

Don’t worry if this doesn’t make a lot of sense to you right now; a lot of it is essentially boilerplate. Go isn’t a difficult language to learn, so if you’re new to it, the only bit you need to look at is this line:

fmt.Fprintln(w, "Hello, 世界")

As the print suggests, this prints the message Hello, 世界 in the browser. (Why in Chinese? Well, why not?)

Build your Go program

Go is a compiled language, which means that we need to build (compile) our program in order to turn it into a binary file which you can run. We do that, straightforwardly enough, using the go build command:

go build
hello
./hello

If you now browse to:

you should see the friendly message we were expecting:

Hello, 世界

Nice!

Build your Golang Docker container image

So, now that you have a kick-ass Go program, how do you turn it into a Docker container? You use the docker image build command, which takes as input a special text file called a Dockerfile. The Dockerfile specifies exactly what needs to go into the container image.

Here’s the Dockerfile for our example Golang program:

FROM golang:1.20-alpine AS build

WORKDIR /src/
COPY main.go go.* /src/
RUN CGO_ENABLED=0 go build -o /bin/demo

FROM scratch
COPY --from=build /bin/demo /bin/demo
ENTRYPOINT ["/bin/demo"]

A Golang Dockerfile usually looks something like this, but the details may vary. Here the FROM golang... line specifies the base image to start with (this contains the Go tools and libraries, ready to build your program). The COPY command tells Docker to copy the Golang source code from the current directory into our container. Then the RUN ... command tells Docker how to build it.

golang:1.20-alpine specifies the Alpine version of the Go base image. Alpine is a tiny Linux distribution designed specifically for containers. So Docker, Golang, and Alpine are made for each other!

We have a second FROM SCRATCH line in this Dockerfile, which tells Docker to start again with a fresh, completely empty container image (called a scratch container), and copy our compiled demo program into it. This is the container image that we’ll then go on to run later.

Using a scratch image saves a lot of space, because we don’t actually need the Go tools, or anything else, in order to run our compiled program. Using one container for the build, and another for the final image, is called a multistage build. Docker multistage builds for Golang programs are common, because the compiled binary is so small compared to the Golang base image.

To build the image, run this command:

docker image build -t hello .

Sending build context to Docker daemon 11.78kB Step 1/7 : FROM golang:1.20-alpine AS build 1.20-alpine: Pulling from library/golang c9b1b535fdd9: Pull complete cbb0d8da1b30: Pull complete d909eff28200: Pull complete 8b9d9d6824f5: Pull complete a50ef8b76e53: Pull complete Digest: sha256:6578dc0c1bde86ccef90e23da3cdaa77fe9208d23c1bb31d942c8b663a519fa5 Status: Downloaded newer image for golang:1.20-alpine ---> 51e47ee4db58 Step 2/7 : WORKDIR /src/ ---> Running in b7ca62454711 Removing intermediate container b7ca62454711 ---> 7c8d8b173f7d Step 3/7 : COPY main.go go.* /src/ ---> 7eca1694d009 Step 4/7 : RUN CGO_ENABLED=0 go build -o /bin/demo ---> Running in 710dfbd8cfb8 Removing intermediate container 710dfbd8cfb8 ---> d3595bc9191c Step 5/7 : FROM scratch ---> Step 6/7 : COPY --from=build /bin/demo /bin/demo ---> 5d9c7c17f554 Step 7/7 : ENTRYPOINT ["/bin/demo"] ---> Running in a72f39f7aaff Removing intermediate container a72f39f7aaff ---> 43573a81f47d Successfully built 43573a81f47d Successfully tagged hello:latest

Run your container image

Run the command:

docker container run -p 8888:8888 hello

Now browse to http://localhost:8888, and you should see the message:

Hello, 世界

Next steps

Your Golang Docker image is available on your computer to run any time you want to. If you’d like to try modifying the program, feel free! Edit main.go and change the message to anything you like, for example. When you’re ready to rebuild your container, run the docker image build -t hello . command again.

To make your container more widely useful, you can create a Docker Hub account and upload your image to a public repository. Then anyone in the world can run your program just by using the docker container run command, or they can run it on any platform that supports Docker containers, such as Kubernetes.

(You can also use Docker Compose, docker stack, or docker deploy to run Golang containers, but we haven’t got space to talk about those here. See the Docker documentation for details.)

Docker Golang development is a good way for programmers to work, because you can build and test the container image on your own machine, before sending it to be deployed on whatever platform you use to run your applications. Because the container holds everything needed to run the program, there’s no chance of running into the ‘it works on my laptop!’ problem.

Have fun!

Read more

Go maps: declaring and initializing

Go maps: declaring and initializing

Learn Go with John

Learn Go with John

0