Running applications typically require servers. In the good old days, it was not possible to define and enforce boundaries for running applications on a server and to ensure fairness in resource usage. As a result, we were constrained to run a single application on a server, and obviously, this resulted in poor resource utilization.
This led to the introduction of virtualization, which allows us to create multiple virtual instances of physical resources on a single physical machine.
A virtual machine (VM) is a virtualized instance of a computer system being managed by software, termed a hypervisor. Each VM operates as a self-contained and isolated entity with its virtual resources. Multiple VMs can coexist on the same physical server. Virtualization resulted in better resource utilization. It is important to highlight here that each VM is completely isolated and has its own operating system. This approach has several limitations, including limiting the number of VMs that can share a physical system.
Containers provide a lightweight virtualization solution when compared to VMs as multiple containers running on a host physical system share OS. Like VMs, each container has its own set of resources, including CPU share, but it shares the OS with other containers. Docker is a widely used container runtime for managing containers.
Containers offer several benefits compared to VMs and are widely used to bundle applications. However, managing containers in a production environment and providing services such as fault tolerance and load balancing is a challenging task.
This is where Kubernetes comes to the rescue. It is an open-source and extensible container orchestration platform. The project was open-sourced by Google in 2014. It automates the deployment, scaling, and management of containerized applications. Kubernetes allows the management and coordination of clusters of containers across multiple hosts, providing services such as fault tolerance and scalability.
Note: Kubernetes is often called and written as K8s, as there are eight letters between “K” and “s.”
A Kubernetes deployment is called a Kubernetes cluster with two types of resources; control plane and nodes. Each cluster has a pool of worker nodes and they run the containerized applications on Pods, which represent one or more co-located containers. These nodes are managed by the control plane, as shown in the illustration below. In a production environment, the cluster would contain multiple worker nodes, and the control plane would run across multiple machines ensuring high availability and fault tolerance.
The main components of the control plane are discussed below:
etcd: This is the key-value storage for storing the Kubernetes cluster’s data, service discovery details, and API objects.
kube-scheduler: It schedules newly created Pods on a worker node.
kube-controller-manager: It runs controller processes such as the node controller for handling node failures and the job controller. There is a separate controller component for Cloud integration.
kube-apiserver: The Kubernetes API server is the primary management entity for the cluster, receiving all REST requests.
Every worker node in the Kubernetes cluster also runs some components, as shown in the illustration above. We have specified Docker as the container runtime; however, Kubernetes supports many other runtimes. A high-level overview of them is as follows:
Kubelet: It manages the containers in the Pod and ensures that they are running and healthy.
Kube-proxy: It allows network communication to the Pods from the internet or inside the cluster.
Let’s first familiarize ourselves with some key concepts related to Kubernetes:
Pods: The basic building blocks of Kubernetes. A Pod is the smallest deployable unit in Kubernetes and represents one or more co-located containers.
ReplicaSets: Ensure that a specified number of Pod replicas are always running. Generally, we do not manage ReplicaSets directly and use a high-level concept, Deployments.
Deployments: A higher-level abstraction that manages ReplicaSets. Deployments enable us to define and update your application’s desired state declaratively.
Services: The Pods on a host can communicate with other Pods on the host. However, we can use the Service API, if we want to expose the application running on the Pods to the outside world (or within the cluster). Services allow us to abstract away the underlying Pod IPs and provide services such as load balancing.
Namespaces: Provide a way to logically divide cluster resources and thus, resource names need to be unique within a namespace.
In this section, we’ll deploy a sample application on minikube
, a local Kubernetes cluster. You are required to follow the steps mentioned on the minikube website to install minikube
on your local system. Then, use the commands below to start the cluster:
minikube start
To interact with the Kubernetes cluster, you can use the kubectl
command-line utility to perform various operations on the cluster using the Kubernetes API. Follow the instructions available on the Kubernetes website to install the kubectl
CLI. Alternatively, minikube
also comes with kubectl
which can be accessed using minikube kubectl -- [commands]
. For this blog, we’ll assume that we have kubectl
installed.
The general structure for kubectl
commands is providing an <action>
to be performed on a <resource>
. To get a list of nodes, we can use the command below. We have also provided some other common examples to get you started. Note that by appending --help
at the end of a command, you can get more information about its usage.
kubectl get nodeskubectl get nodes --helpkubectl get podskubectl describe pods nginx-pod
Let’s start by creating our first Pod. In practice, we do not create Pods directly; they are created using workload resources, such as Deployments. However, to get us started, here is the YAML template for creating a Pod:
apiVersion: v1kind: Podmetadata:name: nginx-podspec:containers:- name: nginximage: nginx:1.25.1ports:- containerPort: 80
The YAML file shown above is easier to understand. We name our Pod as nginx-pod
and specify it to contain a single container running nginx
. It is important to reiterate that Pods are the basic building blocks of Kubernetes. A Pod is the smallest deployable unit in Kubernetes, and the most common use case is the one-container-per-Pod model, where each Pod runs a single container.
kubectl
can be used in two different ways: imperative or declarative. When used declaratively, we provide a manifest, such as the YAML file shown above, that describes our desired state, and kubectl
submits it to the cluster, determining how to achieve it. On the other hand, when used imperatively, we provide cluster-specific commands to instruct kubectl
on what actions to take.
To create the Pod shown in the file above, save the contents in a file named nginx-pod
and then use kubectl apply
as follows:
kubectl apply -f nginx-pod.yamlkubectl get pods
It may take a few seconds for the status of the Pod to change from
ContainerCreating
toRunning
. You should be able able to see1/1
in theREADY
column.
The second command gets a list of Pods, and if everything goes well, we’ll be able to find our Pod listed there.
Congratulations! You have created your first Pod on Kubernetes!
Our happiness may be short-lived as we can see that the Pod is running a container with nginx
listening at port 80
. However, we won’t be able to access it by using http://127.0.0.1:80
. This is understandable; the Pod is running inside a cluster and, by default, is not directly accessible.
Generally, we do not directly access Pods, but again, to get us started, we can use port-forward
with kubectl
, which establishes a tunnel to direct traffic from our host machine to the specified port on a Pod.
kubectl port-forward nginx-pod 8080:80
After running the command above, browse to http://127.0.0.1:8080
in the browser and you should be able to see the welcome page of the nginx
server. Press “Ctrl + C” to end the port-forwarding session. We can now delete this Pod as we’ll manage them by creating Deployments.
kubectl delete pod nginx-podkubectl get pods
In the previous section, we created our first Pod, but we also learned that, in practice, we do not create Pods directly and use the workload resources, such as Deployments. In this section, we will create our first Deployment using the manifest below:
apiVersion: apps/v1kind: Deploymentmetadata:name: nginx-deploymentlabels:app: nginxspec:replicas: 2selector:matchLabels:app: nginx-podtemplate:metadata:labels:app: nginx-podspec:containers:- name: nginximage: nginx:1.25.1ports:- containerPort: 80
There are three important parts of this manifest. We name the Deployment as nginx-deployment
and then create a ReplicaSet by specifying the number of replicas to be 2
. We learned earlier that ReplicaSets ensures that a specified number of Pod replicas are running at all times. The Deployment name would guide the name of the replicas, as we’ll see later. Finally, we specify the Pods template in lines 12–21. This serves as an example to reiterate that we do not normally create Pods directly and manage them using higher-level concepts such as Deployments.
Let’s save the manifest in a file named nginx-deployment.yaml
and then create a Deployment using the following command:
kubectl apply -f nginx-deployment.yamlkubectl get deployments
If all goes well, we should be able to see our Deployment in the list. We can notice 2/2
in the READY
column that this matches our ReplicaSet specification. We can get a list of Pods to confirm this as well.
We can now test the availability of Deployment by deleting Pods and see how it automatically achieves the desired state by starting new Pods. We need to use the commands below:
kubectl get podskubectl delete pod nginx-deployment-7d6955794c-s8c2hkubectl get pods
The name of the Pod being deleted would be different for you but you’ll notice that as soon as a Pod is deleted, another one is instantiated with a different name. We can observe the AGE
column to confirm the behavior.
Congratulations on creating your first Deployment!
We have learned that we can use the Service API to expose the application running on the Pods to the outside world. Services allow us to abstract away the underlying Pod IPs and provide services such as load balancing. Generally, multiple types of Services can be created, and many related use cases exist. We can create a Service for our Deployment using the command below:
kubectl expose deployment nginx-deployment --type=LoadBalancer --name=nginx-service --port=80
The kubectl expose
allows to expose the Kubernetes objects, in our case, a Deployment, as a new Kubernetes Service. We can see the newly created Service in the list of services and get more details about the Service using the describe
command as follows:
kubectl get serviceskubectl describe service nginx-service
There is a lot of information to cover in this blog, but the value of interest for us is in the NodePort
field; it specifies a random port, which can be used to access the Service. Since we are using minikube
for this blog, we can access the Service using the command below:
minikube service nginx-service
If everything goes fine, this will open up the welcome page of nginx
. However, we are not directly accessing the Pods, and we can confirm this behavior by deleting the existing Pods and reaccessing the Service. We leave this as an exercise for you!
Kubernetes is an open-source and extensible container orchestration platform. Kubernetes allows the management and coordination of clusters of containers across multiple hosts, providing services such as fault tolerance and scalability.
In this blog, we thoroughly covered the architecture and components of Kubernetes, coupled with an introduction to various key concepts. We provided a hands-on guide on deploying a sample application on minikube
, a local Kubernetes cluster. We intentionally kept the presentation simple and focused on creating Pods, Deployment, and a Kubernetes Service. We encourage you to enrich your knowledge and understanding of Kubernetes by following the courses below.
Kubernetes is a powerful container management tool that's taking the world by storm. This detailed course will help you master it. In this course, you'll start with the fundamentals of Kubernetes and learn what the main components of a cluster look like. You'll then learn how to use those components to build, test, deploy, and upgrade applications and, as well as how to achieve state persistence once your application is deployed. Moreover, you'll also understand how to secure your deployments and manage resources, which are crucial DevOps skills. By the time you're done, you'll have a firm grasp of Kubernetes and the skills to deploy your own clusters and applications with confidence.
Businesses are modernizing and adopting multi-cloud approaches to deliver services. This shift in application deployment has given rise to containerization and Kubernetes for the deployment, management, and scaling of containerized applications. This course introduces you to serverless computing and shows you how to build serverless applications using Knative. It teaches CI/CD using Tekton and shows you how to build pipelines triggered by GitHub events. You will create a pipeline that builds container images using Build and, later, Cloud Native Buildpacks. In the last part of the course, you will build a web application that integrates with GitHub using a GitHub App, and triggers application build and deployment in response to GitHub events.
Free Resources