Containerization your Go application with Docker
Tags: go, docker
Published: 2023-01-01 19:08:05

## Introduction

Docker is a container technology for packaging and shipping applications. a container will run the same way regardless of the host OS. It provides a lot of the advantages of a virtual machine, but in a more lightweight container. It's possible to limit the resource consumption of individual containers and sandbox your environment.This recipe will set up a Docker container for a basic Go application, store some version information about the container, and demonstrate hitting a handler from a Docker endpoint.

## Requirements 

You must have Docker and Dockercompose installed on your system. You can install Docker from:
 https:/​/​docs.​docker.​com/​install
*This will also include Docker Compose.

## Let's begin

1. create a new directory for your project and navigate to it.

2. Run the following command:

```bash
$ go mod init myapp
```
*You should see a file called go.mod appear in your folder. 

3. Create a file called dockerfile with the following content:
```yaml
FROM alpine

ADD ./example/example /example
EXPOSE 8000
ENTRYPOINT /example
```

4. Create a file called setup.sh with the following content:

```shell
#!/usr/bin/env bash
pushd example
env GOOS=linux go build -ldflags "-X main.version=1.0 -X
main.builddate=$(date +%s)"
popd
docker build . -t example
docker run -d -p 8000:8000 example
```

5. Create a file called version.go with the following content:
```go
package docker
import (
"encoding/json"
"net/http"
"time"
)
// VersionInfo holds artifacts passed in
// at build time
type VersionInfo struct {
Version string
BuildDate time.Time
Uptime time.Duration
}
// VersionHandler writes the latest version info
func VersionHandler(v *VersionInfo) http.HandlerFunc {
t := time.Now()
return func(w http.ResponseWriter, r *http.Request) {
v.Uptime = time.Since(t)
vers, err := json.Marshal(v)
if err != nil {
w.WriteHeader
(http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
w.Write(vers)
}
}

```

6.Create a new directory named example and navigate to it.
7. Create a main.go file with the following content:
```go
package main
import (
"fmt"
"net/http"
"strconv"
"time"
"github.com/PacktPublishing/
Go-Programming-Cookbook-Second-Edition/
chapter11/docker"
)
// these are set at build time
var (
version string
builddate string
)
var versioninfo docker.VersionInfo
func init() {
// parse buildtime variables
versioninfo.Version = version
i, err := strconv.ParseInt(builddate, 10, 64)
if err != nil {
panic(err)
}
tm := time.Unix(i, 0)
versioninfo.BuildDate = tm
}
func main() {
http.HandleFunc("/version",
docker.VersionHandler(&versioninfo))
fmt.Printf("version %s listening on :8000\n",
versioninfo.Version)
panic(http.ListenAndServe(":8000", nil))
}
```

8. Navigate back to the starting directory.
9. Run the following command:
```shell
$ bash setup.sh
```
10. Run the following commands:
```shell
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
b390ef429fbd example "/bin/sh -c /example" 22 seconds ago Up 23 seconds 0.0.0.0:8000->8000/tcp optimistic_wescoff

$ curl localhost:8000/version
{"Version":"1.0","BuildDate":"2017-04-
30T21:55:56Z","Uptime":48132111264}
$docker kill optimistic_wescoff # grab from first output
optimistic_wescoff

```
11. The go.mod file may be updated and the go.sum file should now be present in the top-level recipe directory.

## Conclusion:

we created a script that compiles the Go binary for the Linux architecture and sets private variables in main.go. These variables are used to return version information on a version endpoint. Once the binary is compiled, a Docker container is created that contains the binary. This allows us to use very small container images since the Go runtime is self-contained in the binary. We then run the container while exposing the port on which the container is listening for HTTP traffic. Lastly, we curl the port on localhost and see our version information returned.