30/11/2020 - KUBERNETES
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.
├── Makefile
├── deploy
│ └── k8s
│ ├── configmap.yaml
│ ├── deployment.yaml
│ ├── namespace.yaml
│ └── service.yaml
├── docker
│ └── dev
│ └── Dockerfile
├── .env
└── main.go
TCP_HOST=0.0.0.0
TCP_PORT=8882
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"))
}
# 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"]
## 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
apiVersion: v1
kind: Namespace
metadata:
name: football-develop-namespace
apiVersion: v1
kind: ConfigMap
metadata:
name: football-configmap
namespace: football-develop-namespace
data:
TCP_HOST: 0.0.0.0
TCP_PORT: "8888"
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
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
├── Makefile
├── deploy
│ └── k8s
│ ├── configmap.yaml
│ ├── deployment.yaml
│ ├── namespace.yaml
│ └── service.yaml
├── docker
│ └── dev
│ └── Dockerfile
├── .env
└── main.go
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
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)))
}
# 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"]
## 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
apiVersion: v1
kind: Namespace
metadata:
name: api-develop-namespace
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
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
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
First you need to push your service images then deploy with make publish
and make deploy
commands.
$ make k8s-test
curl --request GET http://192.168.99.100:32630/teams
Arsenal | Liverpool | Manchester United
$ make k8s-test
curl --request GET http://192.168.99.100:31233/api/v1/teams
Arsenal | Liverpool | Manchester United