In this article we will develop a template micro service written in go using gin which can run on Kubernetes.
Pre-Requisites:
Please make sure you have the following setup before proceeding :
- helm
- A Kubernetes flavor is accessible, it can either be a local setup via Minikube, Docker Desktop, or even a cloud-based setup using GKE or EKS. This article will mostly focus on using Docker Desktop to demonstrate, but the configuration and the setup should stay the same across all Kubernetes flavors.
- IDE for ease of use, you can use any editor as long as golfing is supported. This tutorial uses GoLand IDE from Jet-brains
- kubectl CLI .
- Go Latest Stable Version installed.
Create a Template Application using Gin and Golang
Create a folder microservice-template and run the following command under it
go get -u github.com/gin-gonic/gin
This will install gin onto your local project including setting up imports within go.mod file, once done it should look like this:
module template
go 1.19
require github.com/gin-gonic/gin v1.8.1
require (
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-playground/locales v0.14.0 // indirect
github.com/go-playground/universal-translator v0.18.0 // indirect
github.com/go-playground/validator/v10 v10.11.1 // indirect
github.com/goccy/go-json v0.9.11 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/leodido/go-urn v1.2.1 // indirect
github.com/mattn/go-isatty v0.0.16 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.0.6 // indirect
github.com/ugorji/go/codec v1.2.7 // indirect
golang.org/x/crypto v0.3.0 // indirect
golang.org/x/net v0.2.0 // indirect
golang.org/x/sys v0.2.0 // indirect
golang.org/x/text v0.4.0 // indirect
google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
)
Next up, we can import it to our projects main.go file by importing gin for e.g. import "github.com/gin-gonic/gin"
Now create a file called main.go within the folder microservice-template with the following code:
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
r := gin.Default()
r.GET("/health", healthCheck)
r.GET("/test", sampleApi)
r.Run("0.0.0.0:8080")
}
func sampleApi(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"test": "ok"})
}
func healthCheck(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"status": "ok"})
}
The above code imports the installed gin framework to your project and then creates two apis /health and /test using gin framework with separate handlers for each. The intent here is to demonstrate how to package this go program to run in k8s. In future posts I will try to add more advanced used cases of routing than the one above.
Now build and run the above program to ensure its working as intended, run the following command inside micro service-template folder
$ go run main.go
[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.
[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production.
- using env: export GIN_MODE=release
- using code: gin.SetMode(gin.ReleaseMode)
[GIN-debug] GET /health --> main.healthCheck (3 handlers)
[GIN-debug] GET /test --> main.sampleApi (3 handlers)
[GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value.
Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details.
[GIN-debug] Listening and serving HTTP on 0.0.0.0:8080
Now to test our apis we can just hit the endpoints using curl:
$ curl http://localhost:8080/health
{"status":"ok"}
$ curl http://localhost:8080/test
{"test":"ok"}
Create DockerFile for the Go program
Next step is to package our go application to a container image. Create a Dockerfile within the root directory of the application to package this program into a OCI compliant container image. This image we will use later in the helm chart to deploy to kubenertes. Here is the Dockerfile:
FROM golang:1.19-alpine
#Define Work Dir for all image files to be built
WORKDIR /app
#Install Necessary Modules
COPY go.mod ./
COPY go.sum ./
# This installs all go modules needed.
RUN go mod download
# Copy Source Files to work dir
COPY *.go ./
# Creates an executable in the root folder
RUN go build -o /ms-template
EXPOSE 8080
CMD [ "/ms-template" ]
The above structure of dockerfile is explained in much more detail here.
Create Image and Push to Local Docker Registry
In this step we will create a docker image using the above Dockerfile and push to a Docker compliant registry. In this article we will be using Docker Desktop to demonstrate, but in practice it’s recommended to use something like docker hub, GCR etc.
Build the image using the following command within the directory where Dockerfile is, in this case its our root application directory.
$ docker build -t ms-template:0.1 .
[+] Building 47.1s (12/12) FINISHED
$ docker image ls ms-template
REPOSITORY TAG IMAGE ID CREATED SIZE
ms-template latest bf9d234a7885 41 seconds ago 530MB
The image is created successfully, next up we will create a helm chart which will use the image created above.
Create and Configure Helm Chart
We are just going to use the base generated helm chart for our application, as its pretty straightforward. Generate a base helm chart using the following command:
helm create ms-template
Next up we will make changes similar to this post, where we will configure this helm chart to add relevant details such as health check urls and image repository. The following files needs to be updated:
- deployment.yaml : Add readiness and liveness check urls and update port numbers
- values.yaml: modify Service type to LoadBalancer and add image name
- chart.yaml: update the tag to reflect the image tag.
Deploy on Local Kubernetes
Almost done, now just navigate to the directly where the root folder of helm chart is and install the chart on your local Kubernetes cluster
$ helm install ms-template ms-template
NAME: ms-template
LAST DEPLOYED: Sun Dec 11 13:31:10 2022
NAMESPACE: default
STATUS: deployed
REVISION: 1
NOTES:
1. Get the application URL by running these commands:
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
You can watch the status of by running 'kubectl get --namespace default svc -w ms-template'
export SERVICE_IP=$(kubectl get svc --namespace default ms-template --template "{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}")
echo http://$SERVICE_IP:8080
After a few seconds the pods will start up and will be ready to receive traffic
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
ms-template-5bbc7c448c-j9nnv 1/1 Running 0 5s
$ kubectl get service ms-template
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S)
ms-template LoadBalancer 10.104.150.21 localhost 8080:32048/TCP
$ curl http://localhost:8080/test
{"test":"ok"}
$ curl http://localhost:8080/health
{"status":"ok"}
Troubleshooting
Troubleshooting any failure or issues is similar to how you would do it for other Kubernetes workloads. Here are some tips on easy starting over
- If something goes wrong in helm and you want to start over just delete the chart by running
helm delete ms-template
- YAML is very sensitive to indentation, so please ensure that the indentation is proper.
- if all else fails you can still download the code from the GitHub repo and try comparing it with your setup
The entire source code for this post is available here.