From my previous post, we flashed a RaspberryPi 4 with RaspbianOS and Installed K3S Distribution of Kubernetes.

In this post we will develop, build and deploy a Basic Golang Web Application and Deploy it to K3S and access our application via Traefik's Reverse Proxy capabilities.


We will require a container runtime as we will be building a image from our application application code, so in this case we will be installing Docker.

Docker gave some issues installing from the raspbian repositories, so I will be using the source:

$ curl -fsSL -o && sh

To verify that docker has been installed:

$ docker version
Client: Docker Engine - Community
 Version:           19.03.1
 API version:       1.40
 Go version:        go1.12.5
 Git commit:        74b1e89
 Built:             Thu Jul 25 21:33:17 2019
 OS/Arch:           linux/arm
 Experimental:      false

Server: Docker Engine - Community
  Version:          19.03.1
  API version:      1.40 (minimum version 1.12)
  Go version:       go1.12.5
  Git commit:       74b1e89
  Built:            Thu Jul 25 21:27:09 2019
  OS/Arch:          linux/arm
  Experimental:     false
  Version:          1.2.6
  GitCommit:        894b81a4b802e4eb2a91d1ce216b8817763c29fb
  Version:          1.0.0-rc8
  GitCommit:        425e105d5a03fabd737a126ad93d62a9eeede87f
  Version:          0.18.0
  GitCommit:        fec3683

Our Golang Application

We will develop a basic web application that returns the hostname from the container where its running from when making a http request.

Our code, app.go :

package main

import (

func hostnameHandler(w http.ResponseWriter, r *http.Request) {
    myhostname, _ := os.Hostname()
    fmt.Fprintln(w, "Hostname:", myhostname)

func main() {
    const port string = "8000"
    fmt.Println("Server listening on port", port)
    http.HandleFunc("/", hostnameHandler)
    http.ListenAndServe(":" + port, nil)

For our Dockerfile, we will be using a golang alpine image from arm32v7 for our build to compile our binary.

Note that I am compiling it for ARM architecture, if you want to run this on anything else than a RaspberryPi or ARM, then specify the arch of your choice.

Once the binary is built, we will then copy the compiled golang binary to a scratch image.

Our Dockerfile:

FROM arm32v7/golang:alpine AS builder
ADD app.go /go/src/hello/app.go
WORKDIR /go/src/hello
RUN apk add --no-cache gcc libc-dev
RUN GOOS=linux GOARCH=arm GOARM=5 go build app.go

FROM hypriot/rpi-alpine-scratch
COPY --from=builder /go/src/hello/app /app
CMD ["/app"]

Build the image by using the image name and tag of your own account:

$ docker build -t ruanbekker/rpi-hostname:latest .

Now the image has been built, let's test this locally:

$ docker run -it -p 80:8000 ruanbekker/rpi-hostname:latest
Server listening on port 8000

Make a HTTP Request:

$ curl -i
HTTP/1.1 200 OK
Date: Sat, 10 Aug 2019 13:14:20 GMT
Content-Length: 23
Content-Type: text/plain; charset=utf-8

Hostname: 7c5f0d0bc212

And we can see it works as expected. Push the image to your image repository of choice, I will be using docker hub (make sure to use your own account):

$ docker push ruanbekker/rpi-hostname

Deploying to Kubernetes

Since we have a working application packaged up into a image, its time to deploy our application to Kubernetes.

You can use my image if you did not push to your own repository. Our deployment spec, k3s-demo.yml:

apiVersion: extensions/v1beta1
kind: Deployment
  name: k3s-demo
  namespace: default
  replicas: 1
      app: k3s-demo
        app: k3s-demo
      - name: k3s-demo
        image: ruanbekker/rpi-hostname:latest
apiVersion: v1
kind: Service
  name: k3s-demo
  namespace: default
  - name: http
    targetPort: 8000
    port: 80
    app: k3s-demo
apiVersion: extensions/v1beta1
kind: Ingress
  name: k3s-demo
  annotations: "traefik"

  - host:
      - path: /
          serviceName: k3s-demo
          servicePort: http

From the configuration above we are using Traefik for our ingress and mapping port 80 to the k3s-demo service via the dns name

As I do not own, I will be setting that in my /etc/hosts file:

$ cat /etc/hosts | grep k3s

Deploy our application:

$ kubectl apply -f k3s-demo.yml
deployment.extensions/k3s-demo created
service/k3s-demo created
ingress.extensions/k3s-demo created

After a minute or so, have a look at the services:

$ kubectl get service
NAMESPACE     NAME         TYPE           CLUSTER-IP     EXTERNAL-IP     PORT(S)                      AGE
default       k3s-demo     ClusterIP    <none>          80/TCP                       31s

The deployments:

$ kubectl get deployments
default       k3s-demo   1/1     1            1           42s

And our pods:

$ kubectl get pods
NAME                        READY   STATUS    RESTARTS   AGE
k3s-demo-777b9b7799-68kxg   1/1     Running   0          49s

From the above output we can see that our application is deployed and running in Kubernetes.

Testing our Application

Now that our application is deployed and our hosts file is set, let's test the application:

$ curl
Hostname: k3s-demo-777b9b7799-68kxg

And as you can see the Traefik accepted the connection and routed to the correct container.

Scaling our Application

As the application returns the hostname, it will return the hostname of the container where the application is running.

So if we scale our application to more replicas, every time a request comes in, it will return the hostname of that container and we should see the served hostname from every request that has been made.

Let's scale our application to 3 replicas:

$ kubectl scale --replicas=3 deployments/k3s-demo
deployment.extensions/k3s-demo scaled

Ensure that the deployment has been scaled and the desired number of pods has been checked in:

$ kubectl get deployments
default       k3s-demo   3/3     3            3           4m3s

Which is true, then we can look a bit further at the pods, where we should see 3 pods:

$ kubectl get pods
NAMESPACE     NAME                         READY   STATUS      RESTARTS   AGE
default       k3s-demo-777b9b7799-68kxg    1/1     Running     0          4m8s
default       k3s-demo-777b9b7799-hm7hm    1/1     Running     0          39s
default       k3s-demo-777b9b7799-j6p7m    1/1     Running     0          39s

Making 3 HTTP Requests, should show the hostnames that we are seeing from the above output:

$ curl
Hostname: k3s-demo-777b9b7799-68kxg

$ curl
Hostname: k3s-demo-777b9b7799-hm7hm

$ curl
Hostname: k3s-demo-777b9b7799-j6p7m

If you are interested in having more nodes in your cluster you can have a look at this gist:

That's it for now

Thank You

Thanks for reading. If you would like to check out more of my content, check out my website at or follow me on Twitter @ruanbekker