We all are aware of the huge cultural and mindset change that swept over the software industry. There were conflicts, broken lines of communication, and disconnection between the development, operations, and testing teams, who used to work in silos before, and led to raised concerns. As a result, long hours, unhappy customers, and delayed or unsuccessful releases were frequent outcomes. Surely there had to be a way to overcome these hurdles. A mindset, a cultural or philosophical change in how the products were built, tested, and released, was the need of the hour and thus the “DevOps” movement started.
Table of Content
- Kubernetes and its Components
- How Kubernetes Helps in Testing
- Environment Management
- Log Access and Analysis
- Quick Replication of a Failed State
- Reduced Communication Gap with Developers
With the gradual adoption of DevOps, it became necessary to innovate and embrace technologies that would make the DevOps implementation easier and smoother. One of those technologies is “Containerization” (popularized by the open-source Docker platform).
Containerization simplifies the encapsulation of an application and its required environment, by packaging it and its dependent libraries, frameworks, and configuration files together in a portable image file. These efficient, lightweight “containers” allow applications to be built, tested, deployed, managed, and scaled (or rolled back) quickly and consistently. The containers can run in different environments (e.g., multiple OS, cloud distributions) efficiently and each container has a separate file system, CPU share, processing space, and memory allocations.
As the number of containers increases, managing them efficiently without human intervention is challenging. What if a container goes down? The system should be able to automatically restart another backup container to prevent downtime. That is how Kubernetes (also known as “kube” or K8s – “K – eight characters – s”) came into the picture.
Kubernetes is an open-source, extensible, and portable platform that is used to manage the containerized applications we talked about above. It helps in automating the deployment, scaling, and container management. It is widely regarded as one of the most powerful container “Orchestration” platforms since it has the capabilities to orchestrate and automate the operational effort required to run the containerized applications.
When Kubernetes is deployed, we get a “Cluster”. Each working cluster consists of a set of machines (called “Nodes” or “Worker Nodes”). Each worker node hosts something known as “Pod”, which is a layer of abstraction on top of the containers holding the applications. The pods are the smallest unit of a Kubernetes cluster and can contain one or more related containers. The “Control Plane” manages the worker nodes and pods.
Each node has “Node Components” which maintain the running pods and provides a Kubernetes runtime environment. The node components are:
kube-proxy: A network proxy that runs on each worker node after installation and maintains its network rules which allow communication with pods from inside or outside the Kubernetes cluster. It uses the operating system’s packet filtering layer. The worker nodes communicate via services and kubeproxies help in forwarding the requests from services to pods.
kubelet: An agent that runs on each worker node and ensures that the containers are running in the pods. The kubelet services have interfaces to interact with both the containers and the nodes. It starts the pods with containers inside them by assigning resources (e.g., CPU, RAM) from the node to the container.
container runtime: The software responsible for running the containers (e.g., Docker).
The “Control Plane Components” are responsible for handling the Kubernetes cluster as a whole. They are capable of taking decisions like detecting and responding to cluster events, scheduling pods, monitoring pods, rescheduling and restarting pods, or joining a new worker node. Any machine in the cluster can run the Control Plane Components which are as follows:
API server: The component which exposes the Kubernetes API and is the main entry point to the Kubernetes cluster. It acts as the client-cluster gateway, handles internal and external requests after validating whether those are valid requests or not (authentication), and then calls other processes. You can access this API server using REST calls through kubectl (the command-line interface written in “Go” language), or other user interfaces and APIs.
Controller manager: This component consists of several controller functions combined and takes care of the controller processes performed by the node controller, endpoints controller, replication controller, and service account & API access token creation controller. It also can detect the cluster state changes (e.g., pod crashes).
Scheduler: This component keeps an eye on newly created pods and assigns them to available nodes. The decision and scheduling are based on the resource needs of the pods (e.g., CPU/RAM) and the cluster health. Once the “scheduler” schedules the pod, the “kubelet” starts the pods with the containers on the assigned worker nodes.
etcd: A critical component (usually a key-value data store DB) that stores cluster configuration and cluster state information. It acts as the Kubernetes cluster “brain” and holds the current status data for all the cluster components 🧠
Once a developer writes code, builds the application, and deploys it to the container, the application should be tested. The tests should reveal any problems (supported by relevant and useful information) related to how the application is working or performing. A core component of the DevOps culture is faster iteration and automated feedback. It helps the team release high-quality software products quickly and more frequently.
The ability of Kubernetes to manage multiple containers across multiple worker nodes, without manual intervention, can help in managing the Test Environments and Test Infrastructure comfortably. Continuous integration pipelines can use Kubernetes to set up on-demand test environments with automated processes.
Before deploying the application to production, it has to be deployed to the Test Environment, where adequate checks will take place to ensure that the features of the application are working fine and are shippable 🚢
Without Kubernetes, creating and managing these environments can add a lot of overhead. This is especially true with a microservices architecture, where one service component (consumer) depends on the outputs of other service components (providers), and needs to be tested independently in its environment against the specific outputs of its providers. Since Kubernetes follows Infrastructure-as-code principles, it possesses the ability to provision and manage the Testing Infrastructure using declarative configuration definitions. This reduces human errors in the configuration process and improves repeatability, consistency, and recovery.
Log Access and Analysis
For testers, logs are essential – whether they are application logs or automation execution logs. They help observe how the applications, the containers holding the applications, or the frameworks are behaving. To be more precise, they tell how the complete application or its components and services behave in different states and environments.
Kubernetes provides different layers of logging mechanisms to generate logs from the containers. The “kubelet” node component, we talked about earlier, can collect all the application or framework logs from the containers present inside the cluster pods and write them to a local file which we can access at any point in time to perform log analysis.
Quick Replication of a Failed State
From the very start, Google designed Kubernetes for automation. It has jobs and software extensions (known as operators) that act as controllers to extend the cluster’s behavior. They can take and restore the backup of the application state and simulate failures in every part of the cluster.
From the testing point of view, if we are running an extremely long test case and the test is failing repeatedly, Kubernetes’ jobs and operators can help us to get to the failure state quickly. This is not only for testers, but rather this state replication knowledge also helps developers to replicate the issue quickly in their local environment. From there, they can provide a fix, build the code and deploy it to the Test Environment for testing once again.
This may not seem that great for small systems but has tremendous value in large ones. With hundreds of applications and thousands of services with many bugs, Kubernetes state replication can clearly provide value and save time and effort.
Reduced Communication Gap with Developers
Kubernetes enhances collaboration between testers and the developers. Since related nodes and pods can reside in the same Kubernetes cluster, if an automated check fails, developers can access the tester’s Kubernetes instance with the defined environment configurations and replicate the issue.
After reproducing and fixing the issue, developers can instantly deploy the fix to the tester’s environment. Long gone are the days of raising a bug and waiting for hours (if not days) to get the fixed build, perform testing and provide feedback on the quality.
Kubernetes is indeed a complex orchestration platform with a lot of abstractions underneath. Being testers, we will probably not require most of the abstractions and components.
Kubernetes has a lot of good documentation and, and you can overcome the learning curve by following good tutorials, blogs, and documentation. Below are a few of Kubernetes topics that testers should know:
- Starting Kubernetes and creating clusters/nodes/pods/containers
- Extracting application and test logs, and using a log aggregation tool (e.g., ELK) to analyze them
- Reading and creating configuration files
- Sharing clusters
As reference, I am adding some useful kubectl commands to help you interact with a Kubernetes cluster:
kubectl version kubectl get nodes kubectl get services kubectl create deployment <deployment_name> --image=<image_name> [--dry-run] [options] kubectl get deployment kubectl get pod kubectl get replicaset kubectl edit deployment <deployment_name> kubectl logs <pod_name> kubectl describe pod <pod_name> kubectl exec -it <pod_name> --bin/bash kubectl delete deployment <deployment_name> kubectl create -f <configfile_name.yaml> kubectl apply -f <configfile_name.yaml> kubectl delete -f <configfile_name.yaml> kubectl describe service <service_name> kubectl get pod -o wide kubectl get deployment <deployment_name> -o yaml > <fileName.yaml> kubectl get all kubectl create namespace <namespace_object> kubectl get namespace