How Kubernetes works? (I)

When you Google “why kubernetes is freaking hard to learn” you’ll discover thousands of results. It appears that learning how to work with Kubernetes is not easy, especially for people who don’t have backgrounds in software engineering or DevOps. It could be a nightmare for those who haven’t had experience deploying applications to VMs or physical machines. While there are numerous Kubernetes tutorials available, they often fall short of being helpful. Some of the complaints revolve around the amount of concepts and configurations, resulting in an overwhelming learning curve. I believe the primary reason these Kubernetes guides are not effective is that they focus on teaching how to work with Kubernetes, rather than explaining how Kubernetes works at its core. The ideal approach should be to educate people about the underlying workings of Kubernetes first, and then guide developers on how to work with it. When you attempt to learn how to work with Kubernetes, you will be presented a long list of abstractions to learn, which you can easily forget. However when you understand why these abstractions exist and what resides in the layer under you will gain more confidence while working with Kubernetes and you can figure out everything easier. This article is going to have two parts. In this part, I will explain why we use Kubernetes how Kubernetes works in detail, and in the next part, I will describe how to set up a Kubernetes cluster on your laptop and deploy an application to it.

Why Docker? Have you ever developed an app that works fine on your friend’s computer but not on yours? Have you cloned code from GitHub only to find it doesn’t work on your machine? This could be due to various reasons different compiler version, port conflict, missing dependencies, improper environment variables, and more. To address these issues, Virtual Environments were introduced, such as Python Virtual Environments. If you have two Python codes that rely on different versions of a dependency that can’t be swapped, you can create two isolated environments and run each code within its own environment. Virtual Environments help manage environment-related problems regarding dependencies and variables but do not completely isolate the code from the underlying operating system. Virtual Machines (VMs) were introduced to tackle this isolation problem. They allow you to run multiple operating systems on a single physical machine with the help of a hypervisor. VMs are good at isolation, but they tend to be resource-intensive and expensive. This is where containers come in. Containers are lightweight operating system environments that can isolate virtually everything they contain from the host machine. You can package your app into a container, making it portable and eliminating compatibility issues. Containers leverage the cgroups feature in the Linux kernel and can run an entire operating system. If you want to learn more about how containers are created, you can read this. There are multiple tools available to run containers, called container runtimes.

Why Kubernetes? You can break down a monolithic codebase into smaller, isolated apps that can communicate with each other but run independently; this is known as microservices architecture. There are many debates about the challenges of microservices, but I won’t delve into that here12. Instead, I want to talk about how we can implement a micro service app system using containers. If you have only one machine and need to run a single container, you can do it manually. If you have multiple containers on one machine, you can use tools like Docker Compose. However, when you have multiple containers that you want to run across multiple machines, several problems arise. How do you allocate containers to a machine without overloading it? How do you ensure that every container is up and running? How do you manage networking between the machines? This is where Kubernetes comes in to help solve these problems. If you think of the containers and machines running an application as an orchestra, Kubernetes acts as the conductor. There are less popular alternatives for Kubernetes like Swarm.

“Kubernetes (K8s) is an open-source system for automating deployment, scaling, and management of containerized applications. It groups containers that make up an application into logical units for easy management and discovery.”

— kubernetes.io

What’s the Kubernetes architecture like?3 Like many distributed applications, K8s assigns nodes to two roles: leader and workers. The leader determines which tasks need to be done, and the workers execute the tasks, but there’s a key difference in how tasks are delegated. Instead of the leader issuing instructions to the workers, the workers inquire with the leader about what they should do at any given moment. Consequently, the leader doesn’t need to worry about constantly instructing the nodes; it just needs to record the tasks for itself, and over time, the worker nodes will request their assignments. For example, the Kubernetes (K8s) approach to adding a worker to the cluster (a collection of all of the nodes) doesn’t involve adding specific configurations for the worker within the leader’s configurations. Instead, the worker registers in the cluster by informing the leader that it is available and currently not running any tasks. In future the leader might assign some task to the node. To tolerate leader failures, there are typically multiple leaders in a K8s cluster, and one is elected through a majority voting process to become the acting leader (Read more about leader replication).

Summarizing the K8s task delegation approach: When you wish to deploy an app with three replicas in your cluster, you make the request to the leader. The leader then determines which nodes should run these replicas and writes down this information. Worker nodes regularly check the notes maintained by the leader, and eventually, the relevant worker nodes notice what tasks they need to perform.

Let’s get into more details on how this is accomplished in worker nodes4. Kubernetes worker nodes are designed to be quite straightforward; they simply ask for their assigned tasks and execute them. The smallest unit of abstraction in Kubernetes for running a container (or more) is known as a Pod. Each Pod possesses its own IP address and can communicate with other Pods within the cluster via an internal network. A worker node’s primary responsibility is to run the Pods assigned to it, similar to how supervisord monitors processes. This task is performed in the worker nodes by Kubelet, which communicates with the leader and runs the Pods that the leader has assigned. Additionally, there is another module, kube-proxy, on worker nodes responsible for managing networking for the Pods. There aren’t any more major components; that’s essentially it! You can create a K8s worker node by simply running kubelet and kube-proxy.

Leader nodes have more components, as they are responsible for applying user requests and monitoring the cluster. A crucial module within a leader node is the API Server, which handles all incoming and outgoing communication for the leader. Both kubelet in worker nodes and users (via kubectl or curl) communicate with the leader through the API Server. The API Server stores the desired state of the cluster in etcd. etcd is a distributed, fault-tolerant key/value store and is the only component in Kubernetes that is not stateless. When a user asks the API Server to run a replica of a specific pod, API Server needs to understand and manage the request. This is achieved with the help of the controller manager, which includes components like the replica manager, responsible for managing pods’ replica sets (determining how many replicas of a pod should be running). Additionally, the API Server assigns tasks to appropriate nodes through the Scheduler. Furthermore, the Leader has a DNS Server, and for internal cluster networking, hostnames are used instead of IPs. That summarizes most of what you’ll find in a Leader node.

Image created with https://www.autodraw.com.

I will write about the basic K8s resources (config files for deployments) in the next part. You can find an intuitive definition for K8s architecture here.

  1. https://changelog.com/posts/monoliths-are-the-future ↩︎
  2. https://m.signalvnoise.com/the-majestic-monolith ↩︎
  3. This is a good introduction for a user oriented answer to this question. ↩︎
  4. The idea of introducing K8s parts from worker to leader is from https://kamalmarhubi.com/blog/2015/08/27/what-even-is-a-kubelet. ↩︎

One response to “How Kubernetes works? (I)”

  1. […] the previous part we talked about K8s architecture. In this post we talk about running a cluster and deploying an app […]