How to deploy your Golang project on Kubernetes

Kasthedeveloper
8 min readMay 2, 2023

--

In this article we are going to create a very simple webserver using Golang and Gin-Gonic as its framework. Dockerize our project then push it on docker registery. After all we are going to deploy our program on 2 replicas using Minikube which is a local Kubernetes.

I am currently using Ubuntu as my OS so I will implement the required installations on Linux but don’t worry I’m going to add the references for MacOS and Windows at the end of this article.

1 Golang isntallation

Go is a statically typed, compiled high-level programming language designed at Google by Robert Griesemer, Rob Pike, and Ken Thompson. It is syntactically similar to C, but with memory safety, garbage collection, structural typing, and CSP-style concurrency.

rm -rf /usr/local/go && tar -C /usr/local -xzf go1.20.3.linux-amd64.tar.gz

Remove any previous Golang installations

export PATH=$PATH:/usr/local/go/bin

Add /usr/local/go/bin to your $PATH environment variable.

go version
go version go1.20.3 linux/amd64

Great job, now you have installed Go on your system successfully.

2 Docker installation

Docker is an open source platform that enables developers to build, deploy, run, update and manage containers — standardized, executable components that combine application source code with the operating system (OS) libraries and dependencies required to run that code in any environment.

sudo apt-get update
sudo apt-get install \
ca-certificates \
curl \
gnupg

Update the apt package index and install packages to allow apt to use a repository over HTTPS

sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg

Add Docker’s official GPG key

echo \
"deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
"$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

Setting up the repository

sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Install Docker Engine, containerd, and Docker Compose, the commands above will install the latest version of Docker

sudo docker run hello-world

Run your very first docker image

3 Kubernetes & Minikube installation

Kubernetes is an open source container orchestration platform that automates many of the manual processes involved in deploying, managing, and scaling containerized applications.

curl -fsSL https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo tee /usr/share/keyrings/kubernetes.gpg

Download the key, then store it in a safe place default is /usr/share/keyrings

echo "deb [arch=amd64 signed-by=/usr/share/keyrings/kubernetes.gpg] http://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee -a /etc/apt/sources.list

Add software repositories

sudo apt install kubeadm kubelet kubectl
sudo apt-mark hold kubeadm kubelet kubectl
kubectl version
curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
sudo install minikube-linux-amd64 /usr/local/bin/minikube

Install kubernetes tool, and there you have it. Now you must have installed Golang, Docker, Kubernetes and its tools.

Now let’s write our golang program in our main.go file using Gin-Gonic:

First we directory to put our files inside it and create our go.mod:

mkdir app/
cd app/
go mod init github.com/$GITHUB_USERNAME/go-ping
touch main.go
touch Dockerfile
# the commands above will create your go.mod and project requirements

That’s how our main.go should look like:

package main

import (
"net/http"
"github.com/gin-gonic/gin"
)

func main() {
r := gin.Default()
r.GET("/ping", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "pong",
})
})
r.Run()
}

As you can see now we’ve got a program listening on localhost:8080/ping

now you have to get gin-gonic package and run your program.

go get -u github.com/gin-gonic/gin
# you can either run your program
go run main.go
# or compile it and run the binary output
go build main.go
./main
# the second one is suggested because in this article we're going to need
# the binary output in our Dockerfile
there you have it, your program is now listening and serving HTTP on port 8080.

Now let’s write a Dockerfile to make your project Dockerized:

From golang:1.20.3

WORKDIR /ping

COPY go.mod go.sum ./

RUN go mod download

COPY *.go ./

RUN go build main.go

CMD ["./main"]

Remember the Golang version specified at your first line of Dockerfile must be the same as your $ go version command output (your Golang version on your computer).

docker build --tag $YOUR_DOCKER_REGISTRY_ACCOUNT/go-ping:1.0.0 .
docker login
docker push YOUR_DOCKER_REGISTRY_ACCOUNT/go-ping:1.0.0
# commands above will build your image with your docker username and push it
# on registry so you can pull it in next parts on your kubernetes pods.

With the command above you can build your docker image. But remember you must use your docker registry account name instead of $YOUR_DOCKER_REGISTRY_ACCOUNT.

If the build process was successful you can now run the image like this:

docker run -p 8080:8080 $YOUR_DOCKER_REGISTRY_ACCOUNT/go-ping:1.0.0
# the -p 8080:8080 connect the 8080 port inside docker container
# to the 8080 port outside of your container
# so you can call it on localhost:8080
Congrats! Your program is also running on docker.

Now let’s start the important part of this article

We’re going to use Minikube as a local kubernetes, it provides a great dashboard which we’re going to see. If you installed Minikube as I implemented it in installation section you can now run this command to start your Minikube agent.

minikube start

You have to wait a little bit so Minikube can run its services.

So lets create some directories when Minikube is getting up.

mkdir k8s/
cd k8s/
touch deploy.yaml ingress.yaml service.yaml

Now your project directory must look like this as we followed up:

.
├── Dockerfile
├── go.mod
├── go.sum
├── k8s
│ ├── deploy.yaml
│ ├── ingress.yaml
│ └── service.yaml
├── main
└── main.go

as you probably know .yaml files are the best way to give configs to Kubernetes.

This is how our deploy.yaml must look like:

---
apiVersion: apps/v1
kind: Deployment
metadata:
name: go-ping
spec:
replicas: 3 # Number of pods to run at any given time
selector:
matchLabels:
app: go-ping # This deployment applies to any Pods matching the specified label
template: # This deployment will create a set of pods using the configurations in this template
metadata:
labels:
app: go-ping
spec:
containers:
- name: go-ping
image: $YOUR_DOCKER_REGISTRY_ACCOUNT/go-ping:1.0.0
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080

• The kind of every pod configuration must be Deployment

• Metadata is not necessary as its obvious from its name

• Spec is the most important tag of each yaml configurations

• Replicas are like instances of a image container but its actually the number of pods we want to create

• Match labels app means deployment applies to any pods matching the same specified label (all of our replicas first name starts with go-ping*)

• The spec under the template attribute will specify the name of containers and also the most important part which is containers image and their ports. The imagePullPolicy: IfNotPresent means the container will pull the image if its not already on container.

you can create deployment using kubectl command from the root directory of your project:

# the kubectl create command will create the pods you need 
kubectl create k8s/deploy.yaml
# kubectl apply will update your pods as you change your config which is
# deploy.yaml file
# you can also use kubectl apply from the first step
# (kubectl apply) will update your pod if it exists in other scenarios if
# your pods doesn't exist it will first create them.
kubectl apply -f k8s/deploy.yaml

Now lets get ensured about deployments we applied:

kubectl get pods

Will give you the list of pods, so you must see 3 pods with go-ping name at the first part of your pod_name. This means you have 3 replicas of your program, which means there are 3 Golang programs that are using the same Docker image, so you can implement load-balancing or … to handle connection between your pods in a HUGE amount of requests.

Our service.yaml must look like this :

kind: Service
apiVersion: v1
metadata:
name: go-ping
spec:
# Expose the service on a static port on each node
# so that we can access the service from outside the cluster
type: NodePort

# When the node receives a request on the static port (30163)
# "select pods with the label 'app' set to 'echo-hostname'"
# and forward the request to one of them
selector:
app: go-ping

ports:
# Three types of ports for a service
# nodePort - a static port assigned on each the node
# port - port exposed internally in the cluster
# targetPort - the container port to send requests to
- port: 80
targetPort: 8080

• As its obvious every service kind must be specified as Service

• Under the spec which is the important part we have type that could be LoadBalancer, NodePort, ClusterIP, etc.

• The service will come up with the name specified in selector/app which is go-ping in our case. And also ports are given in port attribute which tell the services to communicate with its sub pods from their 8080 port

Now we must apply the service

kubectl apply -f k8s/service.yaml
# you can use either get services or get svc to get list of services
kubectl get services

Our ingress.yaml must look like this :

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: go-ping
spec:
rules:
- host: "localhost"
http:
paths:
- pathType: Prefix
path: /
backend:
service:
name: go-ping
port:
number: 8080

• As we can conclude from previous configurations, Kind of Ingress must be specified as Ingress. The metadata attribute is still not necessary to be defined.

• The rules specify the ingress to expose itself, get the requests from localhost, and then pass it through our services, so that they can pass the requests to our pods.

• The ingress will find out our services with backend/service/name attribute

# this command will apply our ingress
kubectl apply -f ingress.yml
# if you have been following up right, you must have minikube installed.
minikube ip
# the command above will give you an IP Adderss which is the ip you must send your requests to
minikube dashboard --url
# will give you access to the dashboard so you can see your pods and nodes visually

So now if you type minikube dashboard — url you should get an url which if you click on it should look like this:

Everything you need is marked up at the picture above. Number of replicas, services, their datas and IPs will be appeard if you click on each of them. My project is the same as yours and the one I implemented in this article but unfortunately its name is not the same. I configured it to be “go-timeserver” but except that, everything I have in this dashboard must be same like yours.

minikube ip # will give you the ip of where you have to send requests to
kubectl get svc # find out which one is your service and copy its port

Now you can send your requests to IP:PORT (Mine was like 192.168.49.2:32097)

CONGRATS!!!!

There you have it ;D

--

--

Kasthedeveloper

Just a happy Golang developer who wants to become DevOps engineer. That’s it.🧑🏾‍🚀👋🏾