Building your first container application in GO

Start to Learn go

If you been working in the Kubernetes Management world then you probably already know that you need a good scripting language to manage your infrastructure.
Bash in that sense will only get you so far (with oc/kubectl )and Ansible is a very powerful automation tool but if you want to be able to write complex Self service operators (with operator-sdk) or a very reach multi micro services application then Go is you why to go …
In this tutorial we are going to use podman/buildah to build our image.

What’s going on ?

I think the best place to start is with a very simple API application with returns the URL path we are calling it with.
(in this example we will call it monkey-app)

Setting the environment

Before we write a bit of code , the environment needs to be set up correctly in order for us to build in go.

Let’s start by creating a directory (under our $HOME/project directory) with the needed sub directories and then set up the GOPATH variable :

$ mkdir -p $HOME/project/monkey-app
$ export GOPATH=$HOME/project/monkey-app

Now we need to create a few sub directories :

$ mkdir $GOPATH/src $GOPATH/pkg $GOPATH/bin

each directory has a different utility while we write our code :

  • src — the source directory where all the *.go are going to resides.
  • pkg — in case our code needs to download a few packages then this is where they are going to be downloaded to.
  • bin — unless we define other ways , this is where our binaries are going to be created once we ran the compilation successfully.

now that we have our base directories in order we would like to create another directory for our project under the “src” directory.

$ mkdir -p $GOPATH/src/monkey

and we are going to switch to it because it will be our go files directory.

$ cd $GOPATH/src/monkey

Once we have everything (so far) in order then we can start and write out code.

Writing some code

Let’s create a file named main.go and set it up with the package name , the import of the libraries we need and the main function itself.

$ cat > main.go << EOF
package main
import (
"fmt"
"io/ioutil"
"net/http"
"os"
"log"
"github.com/golang/glog"
)
type Page struct {
Title string
Body []byte
}
func loadPage(filename string) (*Page, error) {
body, err := ioutil.ReadFile(filename)
if err != nil {
return nil, err
}
return &Page{Title: filename, Body: body}, nil
}
// HelloServer responds to requests with the given URL path.
func HelloServer(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, you requested: %s\n", r.URL.Path)
glog.Info("Received request for path: %s", r.URL.Path)
}
func ReadFile(w http.ResponseWriter, r *http.Request) {
h_workdir, found := os.LookupEnv("HELLO_WORKDIR")
if !found {
h_workdir = "/opt/app-root/"
}
file_name, f_found := os.LookupEnv("HELLO_FILENAME")
if !f_found {
file_name = "index.html"
}
h_workdir += "/" + file_namep, err := loadPage(h_workdir)if err != nil {
p = &Page{Title: file_name}
}
h_workdir += "index.html"
fmt.Fprintf(w, "<h1>%s</h1><div>%s</div>", p.Title, p.Body)
glog.Info("parsing the HTML file...")
}
func main() {
port, found := os.LookupEnv("GO_PORT")
if !found {
port = "8080"
}
http.HandleFunc("/api/", HelloServer)
http.HandleFunc("/readfile/", ReadFile)
log.Printf("Starting to listen on port %s", port)
log.Fatal(http.ListenAndServe(":"+port, nil))
}
EOF

As you may notice in the code I am looking for a file named index.html under /opt/app-root/ which is a very small and simple HTML file so let’s go a head and create it :

$ cat > $GOPATH/index.html << EOF
<html>
<head>
<title> this is a simple example file </title>
<body>
<p1> Simple Example </p1>
</body>
</html>
EOF

Now what we need is to create a Dockerfile for our build :

cat > $GOPATH/Dockerfile << EOF
FROM golang:alpine as build
WORKDIR /opt/app-root
ENV GOPATH=/opt/app-root/
COPY src src
WORKDIR /opt/app-root/src/monkey/
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o monkey
FROM scratchWORKDIR /opt/app-root
COPY --from=build /opt/app-root/src/monkey/monkey /opt/app-root/monkey
COPY index.html /opt/app-root/
EXPOSE 8080
ENTRYPOINT ["./monkey"]
EOF

Now that we have our files we start using so “go magic” and build a “vendor” directory.
in case you are unfamilier with it a vendor is a module in GO which allow us to build a import library locally so in case we need to use the libraries in the code it will look for them under “vendor” first and then on the internet (not exactly accurate but for the sake of our tutorial it is true ).

First we need to initiate the modules in go :

$ go mod init

And then we will enable the vendor module :

$ go mod vendor
go: finding module for package github.com/golang/glog
go: downloading github.com/golang/glog v0.0.0-20210429001901-424d2337a529
go: found github.com/golang/glog in github.com/golang/glog v0.0.0-20210429001901-424d2337a529

if you are getting the same output as I did then you are on the right track …

Building the container image

if you are lazy as I am then you would like a script to run the build for you :

$ cd $GOPATH$ cat > build.sh << EOF
buildah bud -f Dockerfile -t monkey-app
EOF

And change it’s permissions :

$ chmod a+x build.sh

Run the build :

$ ./build.sh
STEP 1: FROM golang:alpine AS build
STEP 2: WORKDIR /opt/app-root
STEP 3: ENV GOPATH=/opt/app-root/
STEP 4: COPY src src
STEP 5: WORKDIR /opt/app-root/src/monkey/
STEP 6: RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o monkey
STEP 7: FROM scratch
STEP 8: WORKDIR /opt/app-root
STEP 9: COPY --from=build /opt/app-root/src/monkey/monkey /opt/app-root/monkey
STEP 10: COPY index.html /opt/app-root/
STEP 11: EXPOSE 8080
STEP 12: ENTRYPOINT ["./monkey"]
STEP 13: COMMIT monkey-app
Getting image source signatures
Copying blob c483d4b36514 done
Copying config 24eca1d906 done
Writing manifest to image destination
Storing signatures
--> 24eca1d906f
24eca1d906ff0775e60eb9a0149eba67929eeb899a29140ab1d578aa18388f36

In case everything is in order you should see the logs I received.

For our final action lest see is the image is created.

$ podman image list | grep monkey
localhost/monkey-app latest 24eca1d906ff 11 minutes ago 6.31 MB

Congrads ,
We have just created our first container application in GO.

If you have any question feel free to responed/ leave a comment.
You can find on linkedin at : https://www.linkedin.com/in/orenoichman
Or twitter at : https://twitter.com/ooichman

Open Source contributer for the past 15 years