We now have a Kubernetes master node setup with proper network configuration.

This is part 1 of the 3 parts article, links to part 2 and 3 below:

  1. Part 1 - Basic OS setup and master node.
  2. Part 2 - Worker node and creating a new service.
  3. Part 3 - Make your service stateful via persistent volume.

However, you cannot run anything on it without a worker node. if you try to "deploy a pod" (i.e. running some programs) in the cluster, there is no place to run it.

You will need a worker node.

Setting up a worker node

You will need to setup your machine (in my case, a Raspberry Pi) according to the setup guide in part 1. Follow the instructions there and stop just before the session about setting up the master node.

Basically you will have OS configured, all needed softwares installed and updated.

Remember when you initiated the master node, some output were generated that provides the instruction on how to setup a worker node:

kubeadm join <some IP>:6443 --token <some token> \
    --discovery-token-ca-cert-hash sha256:<some long sha256 hash> 

Just run it as root and you're done.

You can check the nodes status by:

kubectl get nodes

Running simple softwares

In Kubernetes, software is run on the worker nodes by either defining a pod, or a deployment. A deployment is defined by a manifest like below, which illustrates how to run the popular BitTorrent software, transmission, in a Kubernetes cluster.

Since it is a BitTorrent software, it needs to store information somewhere. Hence, this example below also contains a Persistent Volume and Persistent Volume Claim that will allow the software to store information.

Otherwise the deployment, when deleted (stopped), will lost all information in it, and thus you wasted a couple of hours downloading something and you will not be able to find it anywhere.

# transmission.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: transmission
spec:
  selector:
    matchLabels:
      app: jasworks
      department: transmission
  replicas: 1
  template:
    metadata:
      labels:
        app: jasworks
        department: transmission
    spec:
      volumes:
      - name: pv-transmission-config
        persistentVolumeClaim:
          claimName: pvc-transmission-config
      - name: pv-transmission-downloads
        persistentVolumeClaim:
          claimName: pvc-transmission-downloads
      hostNetwork: true
      containers:
      - name: transmission
        image: ghcr.io/linuxserver/transmission
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 9091
        - containerPort: 51413
        - containerPort: 51413
          protocol: UDP
        env:
        - name: name
          value: "transmission"
        - name: PUID
          value: "10997"
        - name: PGID
          value: "10997"
        - name: TRANSMISSION_WEB_HOME
          value: "/combustion-release/"
        - name: WHITELIST
          value: "192.168.0.0/24"
        volumeMounts:
        - mountPath: "/config"
          name: pv-transmission-config
        - mountPath: "/downloads"
          name: pv-transmission-downloads

How do we find the software?

The section below (extracted from above), tells the Kubernetes controller, which in turn tells the Docker, to download the container image for transmission from ghcr.io/linuxserver/transmission.

      containers:
      - name: transmission
        image: ghcr.io/linuxserver/transmission
        imagePullPolicy: IfNotPresent

How to store information?

The following sections defines 2 required paths for this container image to work. Normally in the docker repository (like the one above), they will provide documentation on how to configure the container image. In this case, they require at minimum, 2 storage folders (one for configuration, one for downloads) in order for the software to work.

This is the section that defines the volume.

volumes:
      - name: pv-transmission-config
        persistentVolumeClaim:
          claimName: pvc-transmission-config
      - name: pv-transmission-downloads
        persistentVolumeClaim:
          claimName: pvc-transmission-downloads

And this is how you tell the container to mount the folders according to the volumes definition.

   volumeMounts:
        - mountPath: "/config"
          name: pv-transmission-config
        - mountPath: "/downloads"
          name: pv-transmission-downloads

The definitions of pv-transmission-config and pv-transmission-downloads are covered in additional manifest files, which will be explained in part 3. You can also refer to the Persistent Volumes documentation (from Kubernetes website) to create one.

Running it

And once you have created the necessary manifest files (in the above case, 3), use the following command to apply the manifest into your cluster.

kubectl apply -f <manifest file name>
# There are 3 of them in this example.

You can check the running status by:

kubectl get deployments

Something like below will show up:

NAME           READY   UP-TO-DATE   AVAILABLE   AGE
ghost          1/1     1            1           22h
transmission   1/1     1            1           99s

In the above example, I have 2 applications running, one is ghost and the other one is transmission. You can get pods (pods are the places where the applications really run) by running:

kubectl get pods

Results will be similar to below:

NAME                            READY   STATUS    RESTARTS   AGE
ghost-548587c976-zlhmm          1/1     Running   0          3h47m
transmission-67bcdb6f6c-tq94m   1/1     Running   0          3m43s

And you can review the logs for the transmission pod by:

kubectl logs transmission-67bcdb6f6c-tq94m

That concludes part 2. I'll explain more details of Persistent Volumes and Persistent Volume Claims in part 3.