In my previous blog, I talked about containers a lot and got started with Kubernetes where I realized that Kubernetes is in itself a broad concept and deserves a dedicated article. So, let’s try to unbox it and conceptually see what's inside, but before that, if you wanna have a look at my previous blog on containers go here, and if you already have a background with it then this can be a stand-alone article.
We have already talked about the problems we face while dealing with containers. In the life before Kubernetes, we struggled quite a lot with container orchestration, we had to write long code every time we had a multi-container application and sometimes even that didn’t work because of the variations in the deployment environment. So, like always some wise people in this field started thinking of a better and more generalized way of doing this. And this time the solution was given by someone who is always by your side to help you… “Google”. I know you didn’t get it right, no one does, because apparently, I am the only one who personifies “Google” :[. Yeah, so they came up with this open-source container orchestration tool called KUBERNETES. Sure it does its job but the best part is, it does that in all kinds of deployment environments whether it's a physical machine or a virtual machine, or even a cloud.
Now, this is where someone would stop me and ask “Why would I use multiple containers for one application?” “And If I am not using multiple containers why would I use your orchestration tool”. So, perhaps you don’t need any such tool for a single container application but we don’t have those sorts of applications anymore, and this is where we should discuss the transition of applications from a monolith to a microservice architecture. Initially, we used to have all the components of an application as one single unit and this was called a monolith. But then two of the biggest problems with it were scaling and entire system failure. To elaborate, say your application has several components out of which two are being used excessively so in monolith architecture you don’t have an option to just scale those components and you have to scale the entire application. Also, if something goes wrong with a component of your system, your entire application will face downtime. So, a single solution to all such problems was the adoption of microservice architecture. Here we break our application into several stand-alone components and call each of them a microservice. For example, in an application such as Uber, we can break it into microservices like the user, ride, driver, payments, Cars, eats, and so on (all these being an individual microservice). Now, we know not everyone is going to use an online payment method and may go with cash not using the payments microservice whereas almost everyone will use the ride microservice, you will need to scale the ride service much more than the payment service. Also, you wouldn’t want your entire application to be down just because there is something wrong with the payment methods, so go with a microservice architecture and let the rest of the components run unaffected.
Now we are all good to start with our discussion on various resources of Kubernetes. Though there are a lot of resources in Kubernetes, we will discuss only the ones which we’ll be using the most.
- Node: A node is a worker machine in Kubernetes and may be either a virtual or a physical machine, depending on the cluster.
- Pod: It is the smallest unit of a Kubernetes cluster. It is an abstraction over a container. We need this abstraction so that the container run time which is used does not affect the way you interact with your container. So, now you are free to use Docker or Podman or any other container runtime you want. Usually, we have one container inside a pod (or maybe some sidecar service containers also). Each pod gets its own IP address (internal and not public) and this is how pods communicate with each other.
- Service: A pod can crash anytime and when it is brought up again it won’t have the same IP address. This can disrupt the communication between pods. We use services as a solution. A service provides a pod with a permanent IP address. So, the communication will happen between services, and now if a pod dies its corresponding service will take care of it. A service can be internal which can be contacted internally (like for a database) or can be external which be reached by external clients(like for UI). Also, multiple pods doing the same job can hide behind a single service which will balance the load between them automatically.
- Ingress: It is an external service that acts as an external endpoint to the application.
- ConfigMap: It contains the external configuration required by various components of the application. So, if a component X wants to connect to another component Y, you just put a mapping of Y and its name in the configMap and give it to X for reference. Yes, you can give that mapping directly within X but then if the name changes then you’ll have to rebuild your components but if you use configMap, just change the name there.
- Secrets: Now in some cases, the configuration can contain confidential details like a pair of username and password (Usually if you are trying to connect to a database). Such information can not be and should not be kept as plain text. So, we use secrets to store such data in a base64 encoded format.
- Volume: Containers do not have persistent data storage. So, if we want to store some data we use this resource called Volume which when created goes and reserves a part of the hard drive or remote storage.
- Persistent Volume Claim(PVC): Once you have a volume resource created you would want your application to be able to go and ask the volume manager for a volume. That’s what PVC does. It tells about the configuration of volume needs of your application and then you can claim it if such a volume is available.
- Deployment: It is a blueprint of the application and contains details like which image is to be used, how many containers are there, how many replicas are to be created, etc. So, we don’t directly deal with pods but we do with deployment.
- StatefulSet: It is a deployment for the stateful components of your application such as a database. Since a single database can be tapped in by several pods, there is a need to synchronize reads and writes in such cases and statefilSet does that job for us.
So these were the major resources Kubernetes provides us with and uses them to do what it does. We can also define our own resources in Kubernetes which are called CRD (Custom Resource Definition). That's what most organizations do, they take the generic Kubernetes cluster, write some CRDs and then use that system to their benefit. Also, a lot of other products which are based on Kubernetes are nothing but its extension using CRDs, one such product is Openshift which again can be a good topic to write on. Let’s see maybe I’ll write a blog on it :).
Now, let's see what's inside of each node of a Kubernetes cluster i.e its architecture. Here we should know that there are two types of nodes, master and slave. A slave does all the actual work our application is supposed to do whereas the master manages the entire cluster, it takes decisions like which node should a pod goes to, what should be done if a pod fails, when to restart a pod, when to bring in a new node, etc.
So, there are three processes that must be present on every node in a Kubernetes cluster:-
- A container runtime: For the management of containers on each node. Can be Podman or Docker or any other that suits your needs.
- Kubelet: It is a Kubernetes process that schedules all the pods in that node. It acts as an interface between the machine and container runtime. It is responsible for starting containers by assigning desired resources, which are there on the node, to the container inside a pod using the given configuration.
- Kube-proxy: It contains the forwarding logic. Consider an example where you have several nodes running and each node contains your application and database pods. As discussed earlier multiple pods doing the same job can hide behind a single service which will balance the load between them automatically like shown below:
So, Kube-proxy will manage the forwarding of requests from App to DB through the service. It will intelligently decide which DB the request should go to.
I know it’s getting lengthy so let’s jump right into the last part of this article which the structure of a Kubernetes Master Node. A slave node will have all that your application should have, but what is that makes a master node so much more capable that it controls the entire cluster. Let's see. (Also, a cluster can have multiple master nodes depending on the requirement)
So, there are mainly four processes that are responsible for all that happens in a master node:
- API Server: This is what listens to a client when a request comes. It gets the initial request and asks slave nodes to act upon it. Also, this is where all the authentication process happens before something can be forwarded to the main application. So, this the only entry point to your cluster.
- Scheduler: Once the client’s request is validated it is passed to the scheduler to act upon. Say you want to start a new pod, the scheduler will go and check the status of each node and see where that pod can go. These decisions are based on the demand for resources by your request. So, the scheduler makes the decision and passes on the order to start a new pod to the Kubelet of the chosen node.
- Controller Manager: It keeps an eye on each node and sees if something has gone wrong with any of the pods. If some pod has crashed it will try to bring up a new pod as soon as possible by requesting the scheduler to create a new pod with the same configuration.
- etcd: It stores the cluster state at various stages which helps the above three components to make better decisions based on the data provided by etcd. So, every change will automatically get registered on etcd. It only stores the cluster data and not the application data.
So that was all about master nodes. One important thing to note is that slaves are the ones doing the actual job so they need to be on a node with better resources whereas jobs done by the master nodes are almost fixed and won’t need many resources from the node.
This image summarizes all of it quite well:
We have covered most of it. Now if you have made it here then you would want to see some practical working of a Kubernetes cluster as well, you can go and take this free four hours course on YouTube. I personally found it excellent for someone who is getting started with Kubernetes and this blog is kind of a summary of the conceptual part of Kubernetes explained in this video.
I would love your feedback, suggestions, and corrections. Feel free to get in touch or start a conversation in the comments.