Kubernetes : Deploying multi-replica Go App (Kube101 part 1)

Gilang Prambudi
5 min readJun 16, 2024

Learn Kubernetes by deploying multi-replica Golang HTTP Service in K0s and see how useful Kubernetes can manage your micro services to scale.

Objectives:

Create a simple Golang HTTP service that serves a “/check-host” endpoint, printing “Hello, from ${host_name}”. Deploy it on Kubernetes with multiple replicas and set up a load balancer. You’ll see a different host name each time you request the server.

Set Up

  1. Golang SDK environment, I assume you already have basic understanding of Go.
  2. Docker, ensure you can build the go service to Docker image and deploy it to Dockerhub or local registry
  3. K0s or other Kubernetes distro (K3s, Minikube, MicroK8s, etc) installed on the local machine along with Kubectl command
    (in this Post, I’ll use K0s along with the Kubectl built-in command)
  4. (Optional) Kubernetes GUI management tool like Lens

How To

Configure Load Balancer

As we will create 5 replicas for the Go service. In Kubernetes, running a service with five replicas means you have five instances of the same service running concurrently.

To ensure incoming requests are evenly distributed across these replicas, a Load Balancer is used. This Load Balancer exposes the service to a dedicated IP address within your local network subnet and intelligently distributes requests to each replica based on its algorithm. This setup ensures efficient load distribution and improves service reliability.

We’ll use MetalLB for the Load Balancer, this is the step:

⚙️ Install MetalLB by Manifest

kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.14.5/config/manifests/metallb-native.yaml

Ensure the installation by checking MetalLB Controller and Speaker pod

sudo k0s kubectl get pods -n metallb-system

📋 Check the the assignable IP Range for MetalLB
First, check our local network configuration to know what IP can be used as the IP Range for MetalLB

ifconfig
By looking at the netmask, we can see that we have usable local network from 192.168.18.1–192.168.18.254

Because I use my local home network, I know that some IP addresses are already occupied for other devices, therefore, I will use 20 IPs from range 192.168.18.120–192.168.18.140 for MetalLB config-map.

📍Configure IP Range for MetalLB
Create a file named metallb-configmap.yaml and paste this code:

apiVersion: v1
kind: ConfigMap
metadata:
namespace: metallb-system
name: config
data:
config: |
address-pools:
- name: default
protocol: layer2
addresses:
- 192.168.18.120-192.168.18.140

Apply config to Kubernetes

sudo k0s kubectl apply -f metallb-configmap.yaml

Create Go service

Create a simple HTTP service that will print Hostname when we navigate to (/check-host) path, make main.go file and put this:

package main

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

func handler(w http.ResponseWriter, r *http.Request) {
hostname, err := os.Hostname()
if err != nil {
fmt.Fprintf(w, "Error: %v", err)
return
}

fmt.Fprintf(w, "Hostname: %s", hostname)
}

func main() {
http.HandleFunc("/check-host", handler)
port := os.Getenv("SERVICE_PORT")
if port == "" {
port = "8001" // default port if SERVICE_PORT is not set
}
fmt.Printf("Starting server on port %s... version %s\n", port, "0.0.1")
if err := http.ListenAndServe(":"+port, nil); err != nil {
fmt.Printf("Error: %v\n", err)
}
}
I run it locally in my PC, see the host name is still prambudi-pc

🐋 Build it as Docker Image

Create Dockerfile

from golang:1.22.4-alpine

WORKDIR /app

copy . .

RUN go build -o main .

EXPOSE 8001

CMD ["./main"]

Build it, make sure your Docker already login to Dockerhub account, rename it with your own image

docker build -t {YOUR_DOCKERHUB_USERNAME}/go-simple-http-service:0.0.1 .

Push to Docker repository

docker push {YOUR_DOCKERHUB_USERNAME}/go-simple-http-service:0.0.1

Create New Namespace

We’ll create new namespace to organize this deployment, namely go-http-example

sudo k0s kubectl create namespace go-http-example

Create Deployment Config

We’ll create two configuration files:

  1. Go Service Deployment Config: Manages the deployment of Go service pods, including replica configuration
  2. Kubernetes Service Config: Acts as the load balancer, exposing the internal Go service pods to the assigned IP from MetalLB

go-http-service-deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
name: go-simple-http
namespace: go-http-example
spec:
selector:
matchLabels:
app: go-simple-http
replicas: 5
template:
metadata:
labels:
app: go-simple-http
spec:
containers:
- name: go-simple-http
image: gilangprambudi/go-simple-http-service:0.0.1
ports:
- containerPort: 8001

go-http-service-lb-deployment.yaml

apiVersion: v1
kind: Service
metadata:
name: golang-http-service-lb
namespace: go-http-example
spec:
type: LoadBalancer
selector:
app: go-simple-http
ports:
- protocol: TCP
port: 80
targetPort: 8001

Apply Config to Kubernetes

type this command

sudo k0s kubectl apply -f go-http-service-lb-deployment.yaml
sudo k0s kubectl apply -f go-http-service-deployment.yaml

Ensure if LB Service has been assigned with External IP from MetalLB

sudo k0s kubectl get service -n go-http-example
This Load Balancer has been assigned with External IP 192.168.18.121

Now, check if all the pods are running successfully

sudo k0s kubectl get pods -n go-http-example
There are 5 replica running all with different name of Pod

Test it

We can check if the Pod is really running and load balancer is working as expected by simply navigate to browser and go to the given <External-IP>/check-host

Try reloading for several times, ensure to hard reload it in browser (in chrome press CTRL + R) to prevent from browser caching, then you’ll see the hostname is changing according to the available pods

--

--

Gilang Prambudi

I prefer old-school song and technology, as they are obvious and more understandable.