In this example we are going to create two independent applications and both of them will run in their own namespaces. Namespaces are like virtual clusters within the main cluster. They are mainly used for isolating group of related components. You can use namespaces for each environment in a cluster such as develop, qa, staging, sandbox, production. Another way is using application names. In this example I am going to combine both just to make things visually obvious. If you delete a namespace, all the components within will be deleted as well. You can use kubectl config set-context --current --namespace={namespace} command to switch between namespaces.


TCP server


Structure


├── Makefile
├── deploy
│ └── k8s
│ ├── configmap.yaml
│ ├── deployment.yaml
│ ├── namespace.yaml
│ └── service.yaml
├── docker
│ └── dev
│ └── Dockerfile
├── .env
└── main.go

Files


.env

TCP_HOST=0.0.0.0
TCP_PORT=8882

main.go

package main

import (
"log"
"net"
"net/http"
"os"

"github.com/joho/godotenv"
)

func main() {
_ = godotenv.Load()

lis, err := net.Listen("tcp", os.Getenv("TCP_HOST") + ":" + os.Getenv("TCP_PORT"))
if err != nil {
log.Fatalln(err)
}

rtr := http.NewServeMux()
rtr.HandleFunc("/teams", team)

if err := http.Serve(lis, rtr); err != nil && err != http.ErrServerClosed {
log.Fatalln(err)
}
}

func team(w http.ResponseWriter, _ *http.Request) {
_, _ = w.Write([]byte("Arsenal | Liverpool | Manchester United"))
}

Dockerfile


# STAGE 1: build
#
FROM golang:1.15-alpine3.12 as build

WORKDIR /source
COPY . .

RUN CGO_ENABLED=0 go build -ldflags "-s -w" -o bin/main main.go

#
# STAGE 2: run
#
FROM alpine:3.12 as run

COPY --from=build /source/bin/main /main

ENTRYPOINT ["/main"]

Makefile

## Build application binary.
.PHONY: build
build:
go build -race -ldflags "-s -w" -o bin/main main.go

## Build application binary and run it.
.PHONY: run
run: build
bin/main

## Send a dummy request to the local server.
.PHONY: local-test
local-test:
curl --request GET http://localhost:8882/teams

## -------------------------------------------------------------

## Build, tag and push application image to registry then clean up.
.PHONY: push
push:
docker build -t you/football:latest -f ./docker/dev/Dockerfile .
docker push you/football:latest
docker rmi you/football:latest
docker system prune --volumes --force

## Deploy application to kubernetes cluster.
.PHONY: deploy
deploy:
kubectl apply -f deploy/k8s/namespace.yaml
kubectl apply -f deploy/k8s/configmap.yaml
kubectl apply -f deploy/k8s/deployment.yaml
kubectl apply -f deploy/k8s/service.yaml

## Send a dummy request to the exposed pod on kubernetes.
.PHONY: k8s-test
k8s-test:
curl --request GET $(shell minikube service football-service --url --namespace=football-develop-namespace)/teams

namespace.yaml

apiVersion: v1
kind: Namespace

metadata:
name: football-develop-namespace

configmap.yaml

apiVersion: v1
kind: ConfigMap

metadata:
name: football-configmap
namespace: football-develop-namespace

data:
TCP_HOST: 0.0.0.0
TCP_PORT: "8888"

deployment.yaml

apiVersion: apps/v1
kind: Deployment

metadata:
name: football-deployment
namespace: football-develop-namespace
labels:
app: football

spec:
replicas: 1
selector:
matchLabels:
app: football
template:
metadata:
labels:
app: football
spec:
containers:
- name: golang
image: you/football:latest
ports:
- containerPort: 8888
envFrom:
- configMapRef:
name: football-configmap

service.yaml

apiVersion: v1
kind: Service

metadata:
name: football-service
namespace: football-develop-namespace

spec:
type: NodePort
selector:
app: football
ports:
- protocol: TCP
port: 80
targetPort: 8888

HTTP server


Structure


├── Makefile
├── deploy
│ └── k8s
│ ├── configmap.yaml
│ ├── deployment.yaml
│ ├── namespace.yaml
│ └── service.yaml
├── docker
│ └── dev
│ └── Dockerfile
├── .env
└── main.go

Files


.env

HTTP_HOST=localhost
HTTP_PORT=8881

# You can use the K8S POD address here if you wish to test it against the actual K8S cluster.
# You can find out what it is by running `make k8s-test` command.
FOOTBALL_URL=http://localhost:8882

main.go

package main

import (
"io/ioutil"
"log"
"net/http"
"os"

"github.com/joho/godotenv"
)

func main() {
_ = godotenv.Load()

rtr := http.NewServeMux()
rtr.HandleFunc("/api/v1/teams", team)

if err := http.ListenAndServe(os.Getenv("HTTP_HOST") + ":" + os.Getenv("HTTP_PORT"), rtr); err != nil {
log.Fatalln(err)
}
}

func team(w http.ResponseWriter, _ *http.Request) {
req, err := http.NewRequest(http.MethodGet, os.Getenv("FOOTBALL_URL") + "/teams", nil)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
_, _ = w.Write([]byte("build request:" + err.Error()))
return
}

res, err := http.DefaultClient.Do(req)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
_, _ = w.Write([]byte("make request:" + err.Error()))
return
}
defer res.Body.Close()

bdy, err := ioutil.ReadAll(res.Body)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
_, _ = w.Write([]byte("read body:" + err.Error()))
return
}

_, _ = w.Write([]byte(string(bdy)))
}

Dockerfile


# STAGE 1: build
#
FROM golang:1.15-alpine3.12 as build

WORKDIR /source
COPY . .

RUN CGO_ENABLED=0 go build -ldflags "-s -w" -o bin/main main.go

#
# STAGE 2: run
#
FROM alpine:3.12 as run

COPY --from=build /source/bin/main /main

ENTRYPOINT ["/main"]

Makefile

## Build application binary.
.PHONY: build
build:
go build -race -ldflags "-s -w" -o bin/main main.go

## Build application binary and run it.
.PHONY: run
run: build
bin/main

## Send a dummy request to the local server.
.PHONY: local-test
local-test:
curl --request GET http://localhost:8881/api/v1/teams

## -------------------------------------------------------------

## Build, tag and push application image to registry then clean up.
.PHONY: push
push:
docker build -t you/api:latest -f ./docker/dev/Dockerfile .
docker push you/api:latest
docker rmi you/api:latest
docker system prune --volumes --force

## Deploy application to kubernetes cluster.
.PHONY: deploy
deploy:
kubectl apply -f deploy/k8s/namespace.yaml
kubectl apply -f deploy/k8s/configmap.yaml
kubectl apply -f deploy/k8s/deployment.yaml
kubectl apply -f deploy/k8s/service.yaml

## Send a dummy request to the exposed pod on kubernetes.
.PHONY: k8s-test
k8s-test:
curl --request GET $(shell minikube service api-service --url --namespace=api-develop-namespace)/api/v1/teams

namespace.yaml

apiVersion: v1
kind: Namespace

metadata:
name: api-develop-namespace

configmap.yaml

apiVersion: v1
kind: ConfigMap

metadata:
name: api-configmap
namespace: api-develop-namespace

data:
HTTP_HOST: 0.0.0.0
HTTP_PORT: "8888"
FOOTBALL_URL: http://football-service.football-develop-namespace

deployment.yaml

apiVersion: apps/v1
kind: Deployment

metadata:
name: api-deployment
namespace: api-develop-namespace
labels:
app: api

spec:
replicas: 1
selector:
matchLabels:
app: api
template:
metadata:
labels:
app: api
spec:
containers:
- name: golang
image: you/api:latest
ports:
- containerPort: 8888
envFrom:
- configMapRef:
name: api-configmap

service.yaml

apiVersion: v1
kind: Service

metadata:
name: api-service
namespace: api-develop-namespace

spec:
type: NodePort
selector:
app: api
ports:
- protocol: TCP
port: 80
targetPort: 8888

Test


First you need to push your service images then deploy with make publish and make deploy commands.


TCP server


$ make k8s-test
curl --request GET http://192.168.99.100:32630/teams
Arsenal | Liverpool | Manchester United

HTTP server


$ make k8s-test
curl --request GET http://192.168.99.100:31233/api/v1/teams
Arsenal | Liverpool | Manchester United