"Kubernetes, making a very basic start"

So New Job use K8s. I've managed to avoid it thus far, so I only have a vague understanding of how it works. What's the best way to learn a new tool? Come up with a project you can use it on of course, and the best candidate I have right now is the (never ending) energy monitor project, so here we go.

The plan for today is to deploy a tiny API on my home server using k8s, using locally built images (and no docker registry). The aim is to get the hang of how to deploy stuff, not to build anything proper. It could not get more basic than this!

Step 1 - Create the API

First thing's first, create a tiny API. We'll need a total of 3 files and 1 folder:

k8s_experiments/
    Dockerfile
    app/
        main.py
        requirements.txt
  • main.py contains the Hello World example API copied from the FastAPI docs
  • requirements.txt only has fastapi and uvicorn, whatever the latest versions are right now
  • Dockerfile is a very basic FastAPI runner, again copied directly from the FastAPI docs

Once these were created, we can build the docker image and test it all works as expected before we do the complicated stuff:

  • Build the image with this: sudo docker build . -t hello-web:local
    • The tag can be whatever you like, just don't use latest because images tagged with this are not cached so can't be used locally. I like descriptive names so local is good for me.
  • Run with this: sudo docker run --name hello-web -p 80:80 hello-web
  • Test in a browser: just http://0.0.0.0 in my example

    image

Step 2 - Get it into K8s

According to the microk8s docs: The image we created is known to Docker. However, Kubernetes is not aware of the newly built image. This is because your local Docker daemon is not part of the MicroK8s Kubernetes cluster. We can export the built image from the local Docker daemon and “inject” it into the MicroK8s image cache

To do that we... * Compress the docker image: sudo docker save hello-web > hello-web.tar * Inject the compressed image into the k8s cache: microk8s ctr image import hello-web.tar

OK I lied earlier, we do have to create one more file - a deployment definition file. I created it in the root folder which may or may not be a good place for it. I'm sure if it's wrong I'll figure it out soon enough.

  • Create a new file: hello-web-deployment.yaml with the following contents:

    :::yaml apiVersion: apps/v1 kind: Deployment metadata: name: hellow-deployment labels: app: hellow spec: selector: matchLabels: app: hellow template: metadata: labels: app: hellow spec: containers: - name: hello-web image: hello-web:local imagePullPolicy: Never ports: - containerPort: 80

The important bits here are image: hello-web:local which references my locally built image and imagePullPolicy: Never which will prevent k8s from even trying to get the image from a registry. Ports also tripped me up here, I found containerPort: 80 had to match the port used in the Dockerfile, but that could just be luck and me not understanding something more complicated about port mapping. Again, I'm sure I'll find out as soon as I try doing something more interesting!

Now we are ready to: * Create a deployment: microk8s kubectl apply -f hello-web-deployment.yaml * Check the pod status (should be 'Running'): microk8s.kubectl get pods * Expose the API via a Node Port (don't know what that is): microk8s.kubectl expose deployment hellow-deployment --type=NodePort --name=hello-service * List the running k8s services to find the port the API is running on: microk8s.kubectl get svc, which in my case gave me this:

NAME            TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
kubernetes      ClusterIP   10.152.183.1     <none>        443/TCP        3h5m
hello-service   NodePort    10.152.183.177   <none>        80:30108/TCP   69m
  • Check in a browser, in my case http://192.168.1.156:30108/ and wahay! Hello World!

    image

Bonus Extras

Useful commands

  • list all docker images docker ps -a
  • remove all docker images docker rm <image id>
  • list all k8s deployments sudo microk8s.kubectl get deployments
  • list all k8s pods and their status microk8s.kubectl get pods
  • list k8s services microk8s.kubectl get svc

Problems

  • If you get the status ImagePullBackOff when you list the pods it means k8s can't find your image, I solved it with that imagePullPolicy in the yaml file but there's probably loads of ways to cause it
  • Ports. I'm not sure where I was going wrong here, but by default I never run anything on 80 (or other commonly used ports), I like to pick some random number usually in the 5000s. But whatever I did with this it wasn't having it, I could only make it work by setting everything to 80. I guess it doesn't matter what FastAPI is working with inside the container because it's mapped outside but how does that work - does it map to something in Docker then again in k8s?

Credits

Most of these instructions were copied from this medium post with some tweaks from the k8s docs