ruk·si

☸️ Kubernetes
Go Example

Updated at 2018-12-31 02:57

This note shows how to host a simple Go web server using Kubernetes.

brew update
brew install go

Create hello.go:

package main

import (
    "fmt"
    "log"
    "net/http"
)

func main() {
    http.HandleFunc("/", handler)
    log.Fatal(http.ListenAndServe(":3000", nil))
}

func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprint(w, "Hello from Go!")
}
# Test that it works:
go run hello.go
# Navigate to http://localhost.local:3000/

# Compile to Linux binary:
CGO_ENABLED=0 GOOS=linux go build -o hello -a -tags netgo -ldflags '-w' .

Define Dockerfile:

FROM scratch
ADD hello /
CMD ["/hello"]

Setup Docker your registry:

# Install Docker Toolbox
# https://www.docker.com/products/docker-toolbox
# Start Docker Quickstart Terminatel
docker --version
# => Docker version 1.10.2, build c3959b1

# Setup DockerHub repository
# https://hub.docker.com/ > Create Repository > gobernetes > ruksi/gobernetes
# https://hub.docker.com/r/ruksi/gobernetes/

docker build -t ruksi/gobernetes:v1 .
docker run --rm -p 3000:3000 ruksi/gobernetes:v1
# Navigate to http://dockerhost:3000/ (IP of the Docker VM)
# Resulting Docker iamge is something like 5MB, crazy!

docker login
docker push ruksi/gobernetes:v1

Setup Google Cloud SDK:

# Install Google Cloud SDK (gcloud)
# https://cloud.google.com/sdk/downloads#interactive
# Make sure you have Python 2.7 as `python --version`
curl https://sdk.cloud.google.com | bash
# Restart shell

gcloud init
gcloud components update kubectl

# Create new project here: https://console.cloud.google.com/project?pli=1
# Name: gobernetes, you get the ID
# Compute Engine > Get Started > Enable Billing
gcloud config set project gobernetes
gcloud compute zones list
gcloud config set compute/zone us-central1-a
gcloud config list # see current configuration

Managing a Kubernetes cluster:

# Creates VM instance group with 3 instances:
gcloud container clusters create gobernetes-app

# To inspect clusters:
gcloud container clusters list
gcloud container clusters describe gobernetes-app

# To delete the cluster:
gcloud container clusters delete gobernetes-app

Load Balancer

Load balancer service distributes your traffic to all underlying pods. At least one pod must be running at initialization.

Define balancer.yaml:

apiVersion: "v1"
kind: "Service"
metadata: name: "gobernetes-balancer"
labels: name: "gobernetes-balancer"
spec: type: "LoadBalancer"
ports: - name: "http"
port: 80
targetPort: 3000
selector: name: "gobernetes"
kubectl create -f balancer.yaml
kubectl get services
kubectl describe services gobernetes-balancer
# Rerun until you see "LoadBalancer Ingress" value in the summary.
# Use that to connect to the load balancer e.g. http://104.197.91.49/

# To delete it:
kubectl delete service gobernetes-balancer

Replication Controller

A replication controller ensures that a specified number of pod "replicas" are running at any one time.

Define rc.yaml to tell replication controller what a pod looks like. Note that all the -v1 definitions after controller name are important as each new release mush have different identifier.

apiVersion: "v1"
kind: "ReplicationController"
metadata:
  name: "gobernetes-rc-v1"
  labels:
    name: "gobernetes-rc-v1"
spec:
  replicas: 3
  selector:
    name: "gobernetes"
    version: "v1"
  template:
    metadata:
      labels:
        name: "gobernetes"
        version: "v1"
    spec:
      containers:
        - name: "gobernetes"
          image: "ruksi/gobernetes:v1"
          ports:
            - name: "http"
              containerPort: 3000
              protocol: "TCP"
kubectl get pods
kubectl delete pods gobernetes
kubectl get pods
kubectl create -f rc.yaml
kubectl get pods
kubectl get rc
kubectl describe rc gobernetes-rc-v1

# You can try out the replication by deleting a pod and quickly list pods:
kubectl delete pods gobernetes-rc-v1-875xg
kubectl get pods

# You can modify the scale:
kubectl scale --replicas=5 -f rc.yaml
kubectl get pods

# To delete:
kubectl delete rc gobernetes-rc-v1

Releasing New Versions

Create a new version of the application:

rm hello
# Edit the Go file...
CGO_ENABLED=0 GOOS=linux go build -o hello -a -tags netgo -ldflags '-w' .
docker build -t ruksi/gobernetes:v2 .
docker push ruksi/gobernetes:v2

The best approach is to update the YAML specification:

apiVersion: "v1"
kind: "ReplicationController"
metadata:
  name: "gobernetes-rc-v2"
  labels:
    name: "gobernetes-rc-v2"
spec:
  replicas: 3
  selector:
    name: "gobernetes"
    version: "v2"
  template:
    metadata:
      labels:
        name: "gobernetes"
        version: "v2"
    spec:
      containers:
        - name: "gobernetes"
          image: "ruksi/gobernetes:v2"
          ports:
            - name: "http"
              containerPort: 3000
              protocol: "TCP"
kubectl rolling-update gobernetes-rc-v1 -f rc.yaml
# Kubernetes starts switching 1 pod at a time to the new version
# so you are momentarily serving two versions.
# The new controller will be named `gobernetes-rc-v2` per rc.yaml
# If you navigate to the load balancer URL, you can see that it has updated.