Kubernetes Basics

COMPUTER

"We've got this project on kubernetes that you'll be helping maintain"

This started my journey with kubernetes. I found a great tutorial from freecodecamp and this blog is an attempt to set up some personal projects using it. It's best to read the original though as it's more in-depth.

I wanted to serve an index.html for a first project. A pod would be used. A pod is the smallest deployable unit in kubernetes, and it can contain one or more containers (e.g. docker containers). Its an isolated environment to run a docker image, providing storage and networking.

To create a pod, I can create one manually or use a workload resource, which provide extra features like replication, recreating pods when one stops working and more. The workload resources have a pod template, which provides a description of how to create the pods we want.

A deployment is a workload resource that creates replicas of the pod. I wanted 3 pods serving the index.html file, so I'd use a deployment.

To start off, I installed packages to help me play with kubernetes locally:

sudo pacman -S minikube kubectl docker
sudo systemctl start docker
minikube config set driver docker
minikube start

I also created a dockerfile that served the index.html file using nginx.

# tagged static:0.1.0
FROM nginx:1.21.1
COPY index.html /usr/share/nginx/html/index.html

I created a yml file that defined the deployment resource that would be created in minikube.

# static_deployment.yml
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: static-website-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: static-website-pod
  template:
    metadata:
      labels:
        app: static-website-pod
    # pod template section
    spec:
      containers:
      - name: static-website-container
        image: static:0.1.0
        ports:
        - containerPort: 80

This creates a deployment work resource with the name static-website-deployment. The deployment ensures that there are 3 pods running the docker image at any one time (defined in replicas). The selector.matchLabels.app is used to define what pods are being managed, and are the same as the metadata.labels.app found in the template section. This metadata is applied to each pod that is created. The pod template defines how the pod are created, so in each of the 3 pods there will be a running container named static-website-container, and the pod would expose port 80.

To run the above:

minikube start
eval $(minikube -p minikube docker-env) # ensure docker images are built in minikube context
docker build -t static:0.1.0 -f Dockerfile_static_content .
kubectl apply -f k8s/deployment.yml

To check that things are running as expected:

╰─$ kubectl get deployment
NAME                        READY   UP-TO-DATE   AVAILABLE   AGE
static-website-deployment   3/3     3            3           17s
╰─$ kubectl get pods
NAME                                         READY   STATUS    RESTARTS   AGE
static-website-deployment-57bdbf7d94-7ngwt   1/1     Running   0          4s
static-website-deployment-57bdbf7d94-9l5cv   1/1     Running   0          4s
static-website-deployment-57bdbf7d94-gj59k   1/1     Running   0          4s

I wanted to use curl to verify the pods are running correctly, but the kubernetes environment is isolated. To deal with this, kubernetes has services which provide a means of exposing a set of pods. I set up a LoadBalancer service, which provides an ip address and a port that can be used to access the pods.

# static_load_balancer.yml
apiVersion: v1
kind: Service
metadata:
  name: static-load-balancer
spec:
  selector:
    app: static-website-pod
  ports:
    - port: 80
      targetPort: 80
  type: LoadBalancer

and ran the following:

╰─$ kubectl apply -f static_load_balancer.yml
service/static-load-balancer unchanged
╰─$ kubectl get services
NAME                   TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
kubernetes             ClusterIP      10.96.0.1       <none>        443/TCP        6d5h
static-load-balancer   LoadBalancer   10.105.222.19   <pending>     80:30133/TCP   116s
╰─$ curl $(minikube ip):30133
<!DOCTYPE html>
<html lang="en">
    <head>

minikube ip provides the ip address of minikube, and the port is the second part of the PORTS section of the static-load-balancer service.

Since what we're exposing is http traffic, I could also use an ingress object, which is a type of controller that can expose http and https traffic. Other advantages include ssl termination and name-based virtual hosting. I first needed to enable ingress in minikube with:

minikube addons enable ingress

The ingress controller links up with a service, so we could use the LoadBalancer service previously created.

---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: static-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
    - http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: static-load-balancer
                port:
                  number: 80

However, it doesn't make much sense to have both an ingress object and a load balancer pointing to the same thing. Another service I could use is the ClusterIP which provides an ip internal to the cluster. This way we only have one entry point into minikube.

# static_ingress.yml
---
apiVersion: v1
kind: Service
metadata:
  name: static-clusterip
spec:
  selector:
    app: static-website-pod
  ports:
    - port: 80
      targetPort: 80
  type: ClusterIP

---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: static-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
    - http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: static-clusterip
                port:
                  number: 80

And now running:

╰─$ kubectl get services
NAME                   TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
kubernetes             ClusterIP      10.96.0.1       <none>        443/TCP        6d6h
static-clusterip       ClusterIP      10.109.227.9    <none>        80/TCP         108s
static-load-balancer   LoadBalancer   10.105.222.19   <pending>     80:30133/TCP   81m
╰─$ kubectl get ingress
NAME             CLASS    HOSTS   ADDRESS     PORTS   AGE
static-ingress   <none>   *       localhost   80      25m
╰─$ curl $(minikube ip)
<!DOCTYPE html>
<html lang="en">
    <head>
    .
    .

Having the basics of kubernetes i.e. controllers, services and ingress down, I tried to set up a django project. I'll add a link to this when it's ready.