Kubernetes: A Beginner's Guide

Kubernetes is a container orchestration platform and has been described as "the OS of the cloud". It builds on container-based services by providing many features such as volumes for stateful services, resilience and scaling, monitoring, automated zero-downtime upgrades. It runs on a cluster of nodes (VMs) and allocates services to nodes based on either hard limits (eg. my service must run on linux) and prioritised preferences (eg. put these two services on the same VM). If you are running your services in containers, Kubernetes will likely make your life easier.

Containers

The first important step is to ensure your software is running in a container. A container is a way of packaging your service, along with any dependencies, removing the reliance of specific libraries, etc existing on the OS. I'm mostly assuming you know what a container is, what the benefits are and how to run one in this blog post.

First steps

Assuming you have a container already (and if you haven't you can just use one of mine to follow along), the first step is to create your cluster. Sorry, that part is up to you. Come back here when you're done. You can take a look at this page for assistance with Azure.

Pods

Pods are the basic building blocks of a Kubernetes system and it's probably fair to say everything else supports Pods in keeping their services running and available. A Pod runs one or more containers and defines ports, volumes, config settings, etc to expose a container service.

To create a pod, you will need a yaml file that defines its structure. If you're not sure what yaml is, it's like a json file with way fewer characters. It uses file layout to express relationships between properties, so take care of those spaces! Anyway, here's our first yaml file:

---
apiVersion: v1
kind: Pod
metadata:
  name: basiccore-pod
  labels:
    app: basiccore
spec:
  containers:
  - name: basiccore
    image: isonaj/basiccore
    ports:
    - containerPort: 80
  nodeSelector:
    beta.kubernetes.io/os: linux

Alright, that is not terribly clear because it's got 'basiccore' all over it. This yaml file has 4 parts to it: apiVersion, kind, metadata and spec. We are asking for a Pod kind of component, from v1 of the API. The metadata says that the name of the pod should be basiccore-pod and it should have one label called app with the value basiccore. Inside this pod, I want a single container running image isonaj/basiccore (with port 80 exposed) and the container should be called basiccore. The nodeSelector part is just to ensure that the linux container runs on a Linux node. (with AKS supporting windows nodepools, it helps to be sure)

Now, run that on the cluster using kubectl apply -f pod.yaml. You can check what is happening by running kubectl get pods. If it shows an error, use kubectl describe pod basiccore-pod to find out more.

Once you're finished, there's nothing more to see. We won't actually be using this to go further. Nobody expects the spanish inquision and nobody starts a pod on its own. Delete your pod with kubectl delete pod basiccore-pod.

Deployments

So let's start over. Nobody starts a pod by itself, because there's a much better construct for running pods and that is a Deployment. A deployment will provide zero downtime by managing all sorts of things for you. It can be used to automatically rollout new image versions or pod configs (basically anything that could put your uptime at risk). We'll take a closer look in a later post.

Let's jump into the yaml:

---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: basiccore-deployment
spec:
  replicas: 2
  template:
    metadata:
      name: basiccore-pod
      labels:
        app: basiccore
    spec:
      containers:
      - name: basiccore
        image: isonaj/basiccore
        ports:
        - containerPort: 80
      nodeSelector:
        beta.kubernetes.io/os: linux

It's a bit bigger than the Pod yaml. Let's work through from the top. Deployments weren't available in v1. It was added in extensions/v1beta1. Next, we're creating a Deployment, not a Pod and giving it a name of basiccore-deployment. The spec for a deployment consists of the number of replicas and the template. We are starting 2 Pods with this one and the template is the yaml we used to create the Pod earlier.

Let's take a look:

$ kubectl get deployments
NAME                   READY   UP-TO-DATE   AVAILABLE   AGE
basiccore-deployment   2/2     2            2           33s

$ kubectl get pods
NAME                                   READY   STATUS             RESTARTS   AGE
basiccore-deployment-c46759d6d-5qw96   1/1     Running            0          7s
basiccore-deployment-c46759d6d-f94lk   1/1     Running            0          7s

Services

We've just spent all of this time starting and stopping pods, but we haven't actually connected to it yet. We need a way to get in, and get directed to the pod. For this, we use a Service component.

A Kubernetes Service is an abstraction layer which defines a logical set of Pods and enables external traffic exposure, load balancing and service discovery for those Pods. - kubernetes.io

What's that mean? A service component is basically a load balancer that sits across some pods and directs the incoming requests where they are supposed to go.

---
apiVersion: v1
kind: Service
metadata:
  name: basiccore-service
spec:
  type: LoadBalancer
  selector:
    app: basiccore
  ports:
  - protocol: TCP
    port: 80
    targetPort: 80

This service will open up a port in our cluster and load balance any traffic across the pods in the selector. In this case, we're creating a service with a type of LoadBalancer targetting any pod with the metadata app of basiccore (which we applied in the deployment template).

Run kubectl apply -f basiccore-service.yaml to apply the service and then kubectl get services to see the current state.

$ kubectl get services
NAME                TYPE           CLUSTER-IP   EXTERNAL-IP   PORT(S)        AGE
basiccore-service   LoadBalancer   10.0.55.65   <pending>     80:30376/TCP   5m52s

$ kubectl get services
NAME                TYPE           CLUSTER-IP   EXTERNAL-IP    PORT(S)        AGE
basiccore-service   LoadBalancer   10.0.55.65   13.75.220.78   80:30376/TCP   8m12s

Once the External IP is displayed, put that into your browser, and there it is. A container, in a pod, accessible from the outside world.