Creating a Simple Go App

This tutorial will walk you through deploying a stateless Go application to Critical Stack.

Getting Started

Before starting this lab, you will need:

  1. Go installed
  2. Visual Studio Code (or your favorite IDE)
  3. curl or some equivalent way to do GET and POST   (The Postman browser plugin is a good alternative).
  4. If using curl, python to make JSON prettier (you can also use jq)
  5. Docker installed
  6. Access to a public container registry, e.g. Docker Hub
  7. A Critical Stack deployment with a master node and at least 1 worker node. Worker nodes are needed to expose your application externally with a Load Balancer.
  8. A user account provisioned for you. Make sure to take note of your namespace when you log in.

Overview

In this lab, you will deploy a simple Go “Hello World” application to Critical Stack and create Services to make it accessible externally. We will then illustrate how to upgrade the deployment by creating a new release of the application.

The Go code for this lab was lifted from Making a RESTful JSON API in Go and adapted to Critical Stack. This lab is not designed to illustrate Go programming practices or idioms.

Building your Hello World App

We need something to deploy, so let’s get started by creating the “Hello World” application.

Note: all source materials for this lab can be found in the Critical Stack Feature Lab repository.

  1. Create a working directory to build your “Hello World” application:

  2. Open a terminal window.

  3. In your current working directory (we will use the Development directory under the user’s home directory in this example), create a lab directory called go and a sub-directory of that called app

    cd ~/Development
    mkdir -p go/app
    cd go/app
  4. Using the editor of your choice, create a new file called main.go inside the app directory.

    vi hello-go.go
  5. Add the following content to your new file and save:

    package main
    
    import (
    		"fmt"
    		"html"
    		"log"
    		"net/http"
    )
    
    func main() {
    		http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
    			fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path))
    		})
    
    		log.Fatal(http.ListenAndServe(":8080", nil))
    }
  6. Compile your Go executable so it properly targets a linux OS and is built statically with minimal dependencies. In this example we have decided to name the executable hello-go:

    $ CGO_ENABLED=0 GOARCH=amd64 GOOS=linux go build -ldflags="-s -w" -a -o hello-go .
  7. A new executable binary called hello-go will be created in the same directory. This will be referenced when we build our docker container.

  8. Create a new file in the same directory and name it Dockerfile. This file could be named anything, but common convention is to use this filename. Copy the following code into this file:

    # STEP 1 build directory / file layout
    
    FROM ubuntu:latest as layout
    RUN groupadd -g 1000 appuser
    RUN useradd -r -u 1000 -g appuser appuser
    
    RUN mkdir -p /app/data && chmod -R 755 /app
    
    
    # STEP 2 debug if needed
    FROM busybox:latest as builder
    
    
    # STEP 3 build a small, non-root runtime image
    FROM scratch
    
    COPY --from=layout /etc/passwd /etc/passwd
    COPY --from=layout /etc/group /etc/group
    COPY --chown=appuser:appuser --from=layout /app/data /app/data
    
    # For debug
    COPY --from=builder /bin/busybox /bin/busybox
    COPY --from=builder /bin/sh /bin/sh
    
    WORKDIR /app
    USER appuser
    
    
    # STEP 4 add application
    ADD hello-go /app/hello-go
    
    EXPOSE 8080
    
    CMD ["/app/hello-go"]
  9. Login to your docker registry:

    $ docker login
    Login with your Docker ID to push and pull images from Docker Hub. If you dont have a Docker ID, head over to https://hub.docker.com to create one.
    Username: jabbottc1
    Password:
    Login Succeeded
  10. Build a Docker image using the Dockerfile. Make sure to include the . at the end of the following command. The name of the image we’re creating is hello-go:

    docker build -t hello-go -f Dockerfile .

Testing Your Application

Optional - if you want to test your docker image locally to see if it behaves as expected.

  1. Run the following command:

    docker run -e PORT=8080 -p 8080:8080 --rm -ti hello-go
  2. Verify the app works by using the following command in a new terminal window. Alternatively, you could open a browser to http://localhost:8080

    curl http://localhost:8080

To stop the running container run this command from the new terminal window (this uses –filter (-f) to find the container created from your hello-go image):

	docker stop $(docker ps -qf "ancestor=hello-go")

Another way to stop the hello-go container is to view all of the running docker containers and issue a command to stop it by the ID:

$ docker container ls
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              	PORTS                    NAMES
6009be3dfae3        hello-go            "/hello-go"         5 seconds ago       Up 4 seconds        	0.0.0.0:8080->8080/tcp   goofy_swanson

$ docker stop 6009be3dfae3
6009be3dfae3
$ docker container ls
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              	PORTS               NAMES

Tagging and Pushing your Image to a Registry

Now that we have an image we need to apply a tag and push it to a container registry.

  1. Docker tags allow you to convey useful information about a specific image version or variant. Rather than referring to your image by the IMAGE ID you can create aliases for your images. Lets look at the list of images locally (your list might be different):

    $ docker images
    REPOSITORY             TAG                 IMAGE ID            CREATED             
    hello-go               latest              56f2fefe6685        2 hours ago
    alpine                 latest              8cb3aa00f899        3 weeks ago         
    tomcat                 8.0                 ef6a7c98d192        6 months ago
  2. We will tag your local image with your namespace/repository using the following command: docker tag hello-go <your-registry-user-name>/hello-go:0.0.1

    $ docker tag hello-go jabbottc1/hello-go:0.0.1

    Optional Step list your images and tags

    $ docker images
    REPOSITORY               TAG                 IMAGE ID            CREATED             SIZE
    jabbottc1/hello-go       0.0.1               511503f3b991        16 minutes ago      7.06MB
    hello-go                 latest              511503f3b991        16 minutes ago      7.06MB
  3. Push the tagged image into your public container registry

    $ docker push jabbottc1/hello-go:0.0.1
    The push refers to repository [docker.io/jabbottc1/hello-go]
    1c82c58a49f6: Layer already exists
    d992cd40d944: Layer already exists
    e4ae77eef13a: Layer already exists
    8e3f00a45858: Layer already exists
    5483fef4153b: Layer already exists
    c1b4e3786f95: Layer already exists
    0.0.1: digest: sha256:385984109a3cf6b1a82dcebb1ee124a7172a5a81795bf8116f2cab6d7a09ca4e size: 1569

Your image digest and layer hashes will differ from the above terminal output.

Deploy your Container to Critical Stack

Deploying your container is as simple as following the steps in the Deploying Your Application tutorial, but change the docker image name and other parameters as appropriate.

  1. Navigate to the application Load Balancer URL to see your Hello World message or run curl to verify that you application is running and exposed externally. Note it may take a few minutes for the Load Balancer to be created.

    $ curl http://...
    Hello, "/"$

Conclusion

You have now successfully deployed a Go application in Critical Stack. But Hello World isn’t enough, right? Continue on for more feature-rich tutorials.