Lets containerize a sample golang microservice, create a docker image, upload to dockerhub, deploy the microservice in kubernetes cluster.
Lets create a simple golang microservice, no fancy, just a basic one, when hit with curl, simply returns string “hello world” and a health endpoint. cool?
mkdir go-helloworld
cd go-helloworld
No explanation needed for the above two lines, is n’t it?
Inside the above directory, create a main.go file just to host our simple hello world application as given below:
Create a golang microservice
package main
import (
"fmt"
"log"
"net/http"
"os"
)
func main() {
http.HandleFunc("/", helloHandler)
http.HandleFunc("/health", healthHandler)
port := os.Getenv("PORT")
if port == "" {
port = "8080"
}
log.Printf("Server starting on port %s", port)
log.Fatal(http.ListenAndServe(":"+port, nil))
}
func helloHandler(w http.ResponseWriter, r *http.Request) {
name := r.URL.Query().Get("name")
if name == "" {
name = "World"
}
fmt.Fprintf(w, "Hello, %s!\n", name)
fmt.Fprintf(w, "Welcome to our Go microservice!\n")
}
func healthHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
fmt.Fprint(w, `{"status": "healthy", "service": "helloworld"}`)
}
The above code does n’t use any fancy library, just native http library is used to expose a REST endpoint:
GET / — returns hello world
GET /health - returns health of the service
Create a go.mod file as below:
module github.com/alagesann/go-helloworld
go 1.23.3
require ()
Create Dockerfile
# Build stage
FROM golang:1.23.3-alpine AS builder
WORKDIR /app
COPY . .
RUN go mod download
RUN go build -o helloworld .
# Final stage
FROM alpine:3.18
WORKDIR /app
COPY --from=builder /app/helloworld .
EXPOSE 8080
USER nobody:nobody
CMD ["./helloworld"]
Build and test the service locally:
# Build the Go application
go build -o helloworld .
# Test locally
./helloworld
curl http://localhost:8080/
curl http://localhost:8080/?name=Raj
curl http://localhost:8080/health
Observe app works fine and returns result from the microservice.
Build and Push Docker Image
# Build the Docker image -- here instead of my name, use your docker hub username.
docker build -t alagesann/go-helloworld:1.0.0 .
# Test the Docker image
docker run -d -p 8080:8080 --name helloworld alagesann/go-helloworld:1.0.0
curl http://localhost:8080/
docker stop helloworld
docker rm helloworld
# Login to Docker Hub
docker login
# Push to Docker Hub
docker push alagesann/go-helloworld:1.0.0
Create Kubernetes Deployment
Create a kubernetes deployment yaml file to deploy the microservice on the local kubernetes cluster. dep.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: go-helloworld
labels:
app: go-helloworld
spec:
replicas: 2
selector:
matchLabels:
app: go-helloworld
template:
metadata:
labels:
app: go-helloworld
spec:
containers:
- name: go-helloworld
image: alagesann/go-helloworld:1.0.0
ports:
- containerPort: 8080
Create ClusterIP Service, svc.yaml
apiVersion: v1
kind: Service
metadata:
name: go-helloworld-service
labels:
app: go-helloworld
spec:
selector:
app: go-helloworld
ports:
- name: http
port: 80
targetPort: 8080
protocol: TCP
type: ClusterIP
Deploy to Kubernetes
# Apply the deployment and service
kubectl apply -f dep.yaml
kubectl apply -f svc.yaml
# Verify deployment
kubectl get deployments
kubectl get pods
kubectl get svc
# Check pod logs
kubectl logs -l app=go-helloworld --tail=10
Test the microservice
➜ go-helloworld k get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
go-helloworld-service ClusterIP 10.43.85.59 <none> 80/TCP 47m
Now test the service using a temporary pod.
➜ kubectl run -it --image=curlimages/curl curly -- /bin/sh
~ $ curl http://go-helloworld-service/
Hello, World!
Welcome to our Go microservice!
~ $ curl http://go-helloworld-service?name=Raj
Hello, Raj!
Welcome to our Go microservice!
~ $
Congratulations you have done the following:
✅ Created a simple Go microservice with HTTP endpoints
✅ Containerized it using a multi-stage Docker build
✅ Published the image to Docker Hub
✅ Deployed it to Kubernetes using a Deployment
✅ Exposed it internally using a ClusterIP service
✅ Verified the service is working correctly