Kubernetes Revision
Kubernetes is designed to orchestrate containers.
Why is Container Orchestration Needed?
Imagine you have:
An application split into multiple services (e.g., frontend, backend, database), each running in its own container.
Multiple instances of these containers to handle high traffic.
A need to ensure these containers stay healthy and restart if they fail.
Managing this manually for dozens or hundreds of containers would be overwhelming. Container orchestration automates these tasks.
Example: Running a Web App with Kubernetes
Let’s say you have a web application with three components:
Frontend (React or Angular)
Backend API (Node.js, Python)
Database (MySQL)
Without Orchestration:
You manually start containers for each service.
You restart failed containers yourself.
You update each container manually during a new release.
With Kubernetes:
Define Your Application in a YAML File
Deploy to Kubernetes:
Kubernetes Handles the Rest
- A cluster is a collection of machines (physical or virtual) working together to run applications using Kubernetes. It consists of one master node and multiple worker nodes. Think of it as a team: the master node is the manager, and the worker nodes are the employees doing the tasks.
Master Node Components
API Server:
Significance: Acts as the central communication hub for the cluster.
Function:
Receives commands from users (
kubectl
or other tools).Passes those instructions to the other components.
Example: When you type
kubectl apply -f deployment.yaml
, the API server processes this request and instructs the cluster to deploy your application.
Scheduler:
Significance: Decides which worker node will run a specific task or application.
Function:
Analyzes the available resources on worker nodes (CPU, memory).
Places tasks on the most suitable node.
Example: If worker node 1 is busy, it assigns the job to worker node 2.
etcd:
Significance: The brain's memory—a distributed key-value store.
Function:
Stores all the cluster’s state and configuration data.
Ensures consistency across the cluster.
Example: If a node fails, Kubernetes consults
etcd
to determine what was running there and reschedules it on another node.
Controller Manager:
Significance: Handles the cluster's overall health and operations.
Function:
Manages controllers.
Examples of controllers:
Node Controller: Monitors worker nodes and replaces failed ones.
Replica Controller: Ensures the correct number of app instances are running.
Example: If 3 replicas of your app are required, but 1 crashes, the Controller Manager restarts it automatically.
Worker Node Components
Kubelet:
Significance: The worker node's agent that follows orders from the master node.
Function:
Ensures the containers on the worker node are running as instructed by the master.
Reports the node's status back to the master.
Example: The Kubelet ensures the frontend container is running and will restart it if it crashes.
Kube-proxy:
Significance: Manages networking for apps in the cluster.
Function:
Routes traffic to the right container or pod.
Allows communication between containers, worker nodes, and the outside world.
Example: If a user accesses your app,
kube-proxy
directs their request to the appropriate container.
Container Runtime:
Significance: The software that runs your containers.
Function:
Responsible for starting, stopping, and managing the lifecycle of containers.
Examples: Docker, containerd.
Example: If your app's container is built with Docker, the runtime runs it on the worker node.
How They Work Together
You issue a command:
kubectl apply -f app-deployment.yaml
.The API Server receives the request and updates the desired state in etcd.
The Scheduler assigns the task to a suitable worker node.
The Controller Manager ensures that the required number of app instances are running.
On the worker node:
Kubelet ensures the assigned containers are running.
Kube-proxy routes network traffic to the correct containers.
The Container Runtime (e.g., Docker) runs the containers.
What is kubectl
?
kubectl
(pronounced cube-control) is a command-line tool used to interact with a Kubernetes cluster. It acts as the interface between the user and the Kubernetes API Server, allowing you to manage, monitor, and troubleshoot applications and resources in your cluster.
What is a Pod in Kubernetes?
A Pod is the smallest and most basic unit of deployment in Kubernetes. It represents a single instance of a running process in your cluster. It can run one or more containers and share the same resources.
How Kubernetes Helps with Scalability, Load Balancing, High Availability, Rollouts, and Rollbacks?
Scalability:
Adds or removes instances when needed. Example:
Imagine you run an online store, and during a sale, many users visit your site.
You start with 2 instances of your app. As traffic increases, Kubernetes automatically scales up to 10 instances.
When traffic drops, it scales back down to save resources.
Load Balancing:
Spreads traffic evenly across instances. Example:
Your app has 3 pods running.
A user visits your site; Kubernetes sends their request to Pod 1.
Another user visits; their request is routed to Pod 2, and so on.
High Availability:
Keeps your app running, even during failures. Example:
You have 5 pods running your app.
If one pod crashes, Kubernetes automatically creates a new pod to replace it.
If a worker node fails, Kubernetes reschedules the pods to other nodes.
Rollouts:
Updates your app without downtime. Example:
You release a new version of your app (v2).
Kubernetes starts replacing the old pods (v1) with new ones (v2), one at a time.
Users don’t experience downtime because the old pods keep running until the new ones are ready.
Rollbacks:
Reverts to a stable version if things go wrong. Example:
Your new version (v2) has a bug.
Kubernetes quickly rolls back to the previous version (v1).
Users won’t notice because the rollback happens smoothly.
What is minikube?
A tool to run a Kubernetes cluster locally on your computer.
It's like a mini Kubernetes lab for testing and learning.
It sets up a small, single-node Kubernetes cluster on your machine.
It’s ideal for experimenting and development without needing a full-fledged cloud setup.
Deployment of nginx for practicing Kubernetes :
At first , we create deployment ; then, we view the deployments and also the pods.
By default, Nginx listens on port 80 inside the container, but this port is inside the Pod's network namespace, not accessible directly from your local machine.
To make the Nginx service available outside the Pod, we can run the command :
kubectl expose deployment my-nginx --port=80 --type=LoadBalancer
This creates a Service in Kubernetes. A Service provides a stable network endpoint to access your Pods.
--port=80
: Exposes the Pod's internal port 80 to the Service.--type=LoadBalancer
: Requests a cloud load balancer (or Minikube simulates one) to expose the Service externally.
Justification: A Service is necessary because Pods are ephemeral (they can restart or move to another node). A Service ensures a consistent way to access your Nginx app, regardless of where the Pod runs.
To check if the Service was created successfully, we can use the command : kubectl get services
This displays the list of Services, including:
ClusterIP
: The internal IP assigned to the Service (accessible within the cluster).External-IP
: The external endpoint (if applicable).Port
: The port exposed by the Service.
Then we run the command minikube service my-nginx
. Minikube translates the Kubernetes Service's external endpoint into a URL that you can access on your local machine. We can use minikube service my-nginx --url
to obtain the accessible URL
On visiting the URL , through our local machine, we get :-
What is Minikube Dashboard?
Minikube Dashboard is a web-based user interface (UI) that allows you to visually interact with the resources running in your Minikube Kubernetes cluster. It provides a graphical view of various Kubernetes objects like Pods, Deployments, Services, ReplicaSets, and other components within your Minikube environment.
Demo WebApp Project :
At first, we will dockerize a simple react app by following the steps mentioned in my previous blog : https://iamkishaloy.hashnode.dev/docker-revision . Then we will build its image by a command like docker build -t kishaloy01/webapp-demo:02 .
(since my Dockerhub username is kishaloy01). Then we will use docker login
and enter our credentials properly ; then finally we will push the image like docker push kishaloy01/webapp-demo:02
Then we can use the command : kubectl create deployment webapp --image=IMAGEID
(In my case, it was kubectl create deployment webapp --image=kishaloy01/webapp-demo:02 )
We can then check with get deployments and get pods commands :
We can then expose the PORT using service :
NOTE: We can also delete the undesirable deployments using the “delete” command.
For example :
NOTE: 1) The kubectl logs
command is used to view the logs of a pod's container. It is helpful for debugging issues such as crashes, errors, or unexpected behavior within containers.
2) The kubectl describe
command provides a detailed overview of a resource (such as a pod, deployment, or service) in the cluster. It includes metadata, status, events, and other useful information that helps to debug problems and understand resource configurations.
Concept of Rollout in Kubernetes:
In Kubernetes, "rollout" refers to the process of updating a deployment to use a new version of an application or configuration. Kubernetes manages the rollout automatically to ensure that your application remains available during the update.
Let use see an example :
1. Create a Deployment
Let’s create a deployment with version 1 of the application.
kubectl create deployment my-app --image=nginx:1.20
Check the pods:
kubectl get pods
2. Update the Deployment
Now, update the application to a new version (nginx:1.21
).
kubectl set image deployment/my-app nginx=nginx:1.21
3. Rollout Status
You can track the progress of the rollout with:
kubectl rollout status deployment/my-app
This shows whether the update was successful or if there are any issues.
After the update, new pods will run nginx:1.21
, while the old ones are terminated.
4. Rollback if Needed
If the new version has issues, you can rollback to the previous version.
kubectl rollout undo deployment/my-app
This restores the previous version (nginx:1.20
).
Rollout Benefits
Zero Downtime: Old pods are gradually replaced with new ones to ensure continuous availability.
Rollback: Quickly revert to a stable version if something goes wrong.
Control: Manage updates at your own pace using commands like
pause
andresume
.
Rollback in Kubernetes
A rollback in Kubernetes reverts a deployment to a previous version if a newer version has issues like bugs, misconfiguration, or instability. Kubernetes maintains a history of revisions for each deployment, making it easy to switch back to an earlier version. Example: Point 4 above .
Self-Healing in Kubernetes
Self-healing in Kubernetes means the system automatically detects and fixes problems with your application to ensure it remains healthy and available. Kubernetes uses built-in mechanisms to monitor your application's state and restore it if something goes wrong.
Kubernetes monitors your application's desired state (defined in your deployment) and compares it to the actual state:
Desired State: The state you want.
Actual State: The state Kubernetes observes in the cluster.
If the actual state doesn't match the desired state (e.g., a pod crashes), Kubernetes automatically:
Restarts failed pods.
Reschedules pods on different nodes if a node fails.
Recreates pods if they are deleted accidentally.
Example : Self-Healing in Action :-
Step 1: Create a Deployment
Deploy an application with 3 replicas:
Check the pods:
Step 2: Simulate a Failure
Manually delete one of the pods:
Check the pods again:
- What Happened? Kubernetes noticed one pod was missing (actual state ≠ desired state) and created a new pod to maintain 3 replicas. (Note: In the above example , we can see the AGEs of the pods to understand the phenomenon).
YAML Configuration for Deployment and Service in Kubernetes
In Kubernetes, YAML files are used to define configurations for deploying applications and exposing them as services. These configurations describe what Kubernetes should do and the desired state of your application. Using YAML configurations for Kubernetes deployments and services streamlines application management by making it simpler, consistent, and scalable. It allows developers and DevOps teams to focus on innovation rather than managing infrastructure manually.
Step 1: Deployment YAML
This file creates a deployment with 2 replicas of an Nginx application:
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx-deployment
spec:
replicas: 2
selector:
matchLabels:
app: my-nginx
template:
metadata:
labels:
app: my-nginx
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
Key Points:
replicas: 2
: Runs 2 instances of the app.selector
: Matches pods with theapp: my-nginx
label.containers
:image: nginx:latest
: Pulls the Nginx image from Docker Hub.containerPort: 80
: Exposes port 80 inside the pod.
Step 2: Service YAML
This file exposes the pods created by the deployment:
apiVersion: v1
kind: Service
metadata:
name: my-nginx-service
spec:
selector:
app: my-nginx
ports:
- protocol: TCP
port: 80
targetPort: 80
type: LoadBalancer
Key Points:
selector
: Matches pods with theapp: my-nginx
label.ports
:port: 80
: The port the service listens on.targetPort: 80
: The port the pods listen on.
type: LoadBalancer
: Exposes the service to the internet.
How to Apply These Files
Save the deployment YAML as
deployment.yaml
and the service YAML asservice.yaml
.Apply them using
kubectl
:kubectl get deployments
andkubectl get services
.
Note: We can see above : my-nginx-deployment and my-nginx-service
What is a Multi-Container App?
A multi-container app is an application where multiple containers work together to achieve a specific functionality. These containers are usually part of the same Pod in Kubernetes and share resources. Example: A Simple Web App with a Logger . Imagine a scenario where:
Container 1: Runs an Nginx web server to serve a website.
Container 2: Runs a logging service that collects and logs all the HTTP requests.
Step 1: YAML Configuration for Multi-Container Pod
Here’s the YAML file (multi-container.yml) to deploy the above example:
apiVersion: v1
kind: Pod
metadata:
name: multi-container-pod
labels:
app: multi-container-app
spec:
containers:
- name: web-server
image: nginx:latest
ports:
- containerPort: 80
- name: logger
image: busybox
command: ["sh", "-c", "while true; do echo $(date) 'Access Log'; sleep 5; done"]
Explanation of YAML File
Kind: Pod
- We are creating a Pod that runs multiple containers.
Containers Section:
web-server
: Runs the Nginx image and listens on port 80.logger
: Runs thebusybox
image with a command to simulate logging by printing logs every 5 seconds.
Deploy and check the POD.
Check the logs of the logger
container:
Step 2: Expose the Nginx Web Server:
Step 3: Verify Logs
Check the logs of the logger container again. You should see the simulated access logs being generated continuously.
NOTE : 1) What the Logs Show:
The logger
container is running a simple command that outputs the current date and time, followed by the text "Access Log," every 5 seconds.
2) Purpose:
The
logger
container is acting as a logging utility within the pod.It's periodically logging a timestamp along with the string "Access Log" to simulate logging activity.
Two Ways to Run Multi-Container Applications in Kubernetes
1. Multiple Containers in the Same Pod :
The one which we did above.
2. Multiple Containers in Separate Pods
Description:
Each container is deployed in its own Pod.
Communication happens via Kubernetes Services (e.g., ClusterIP, NodePort).
Used when containers are loosely coupled but need to interact (e.g., a front-end container and a back-end container).
Example Use Case:
A React front-end container running in one Pod and a Node.js API back-end container running in another Pod.
Deployment Example: Separate Pods with Services
- Front-End Pod (React): front-end.yaml
apiVersion: v1
kind: Pod
metadata:
name: front-end
labels:
app: front-end
spec:
containers:
- name: react-app
image: node:16
command: ["sh", "-c", "npx create-react-app my-app && cd my-app && npm start"]
ports:
- containerPort: 3000
- Back-End Pod (Node.js): back-end.yaml
apiVersion: v1
kind: Pod
metadata:
name: back-end
labels:
app: back-end
spec:
containers:
- name: node-api
image: node:16
command: ["sh", "-c", "while true; do echo 'API Running'; sleep 5; done"]
ports:
- containerPort: 5000
- Front-End Service: front-end-service.yaml
apiVersion: v1
kind: Service
metadata:
name: front-end-service
spec:
selector:
app: front-end
ports:
- protocol: TCP
port: 80
targetPort: 3000
type: NodePort
- Back-End Service: back-end-service.yaml
apiVersion: v1
kind: Service
metadata:
name: back-end-service
spec:
selector:
app: back-end
ports:
- protocol: TCP
port: 80
targetPort: 5000
type: ClusterIP
Upon deployment , we get:
Access the front-end using the NodePort: minikube service front-end-service
IMPORTANT NOTE: command ["sh", "-c", "npx create-react-app my-app && cd my-app && npm start"]
in front-end.yaml:
This is trying to create a new React app every time the pod starts, which might not be what you want in production. It will also take a long time each time the pod restarts, since create-react-app
installs dependencies and sets up the app. For a more efficient deployment, you should consider using a pre-built React app or mount an already built app into the container.
Steps to Build and Use a Pre-Built React App:
Build the React App Locally: First, you need to build the React app on your local machine.
Navigate to your React app directory on your local machine.
Run the following commands:
npx create-react-app my-app # only the first time cd my-app npm run build
This will create a build/
directory inside your project with all the static files needed for production.
Use a Pre-Built Docker Image for React: You can create a custom Dockerfile that copies the build folder into the container and serves it using a web server like
nginx
.Example Dockerfile:
# Use nginx as the base image FROM nginx:alpine # Copy the built React app into the nginx container COPY ./my-app/build /usr/share/nginx/html # Expose the default nginx port EXPOSE 80
Steps:
Build the Docker image locally:
docker build -t my-react-app .
Push it to a container registry like Docker Hub or your private registry:
docker push my-react-app
Update the
front-end.yaml
to Use the Custom Image:Now, modify your
front-end.yaml
to use the pre-built image instead of runningnpx create-react-app
.Updated
front-end.yaml
:yamlCopy codeapiVersion: v1 kind: Pod metadata: name: front-end labels: app: front-end spec: containers: - name: react-app image: my-react-app:latest # Use the pre-built image from your registry ports: - containerPort: 80 # Serve the React app on port 80
Apply the updated pod configuration:
bashCopy codekubectl apply -f front-end.yaml
ConfigMaps
What are ConfigMaps?
ConfigMaps in Kubernetes are used to store configuration data that your application can consume as environment variables, command-line arguments, or configuration files.
You can think of them like a key-value pair storage, where the key is a configuration name, and the value is the setting or configuration for that key.
Simple Example: Let's say you have a web application that connects to a database. You want to store the database configuration (like the host, username, and password) outside your application code. You can store these in a ConfigMap.
Example:
apiVersion: v1
kind: ConfigMap
metadata:
name: db-config
data:
DB_HOST: "localhost"
DB_USER: "admin"
DB_PASS: "password123"
You can then use the above ConfigMap in your pod by referencing the values inside the container.
apiVersion: v1
kind: Pod
metadata:
name: web-app
spec:
containers:
- name: web-container
image: my-web-app
envFrom:
- configMapRef:
name: db-config
In this case, the environment variables DB_HOST
, DB_USER
, and DB_PASS
will be automatically populated inside the container from the ConfigMap.
Volume and Data
What are Volumes?
A volume in Kubernetes is a storage resource that can be used by containers to store data.
Volumes are useful when you want to persist data beyond the lifecycle of a container. By default, when a container is deleted, its data is lost. A volume allows data to persist even if the container restarts.
Simple Example: Imagine you're running a container that generates logs. You don't want to lose those logs if the container restarts, so you store them in a volume.
Example:
apiVersion: v1
kind: Pod
metadata:
name: log-generator
spec:
containers:
- name: log-container
image: log-generator-image
volumeMounts:
- mountPath: "/var/log"
name: log-storage
volumes:
- name: log-storage
emptyDir: {}
In this example:
The
emptyDir
volume is created when the pod starts and is used to store logs.The logs will persist in the volume even if the container is restarted, but the volume will be deleted if the pod is deleted.
Persistent Volume (PV)
What is a Persistent Volume (PV)?
A Persistent Volume (PV) is a piece of storage in the cluster that has a lifecycle independent of any pod. It's like a long-term storage that can be used by multiple pods.
PVs are useful when you want to ensure data persists beyond the pod or node lifecycle, such as for a database that needs permanent storage.
-
Simple Example: Think of a database where you need to store data permanently. A Persistent Volume allows the database to keep its data even if the pod is recreated or moved to another node.
Example:
apiVersion: v1 kind: PersistentVolume metadata: name: my-pv spec: capacity: storage: 5Gi accessModes: - ReadWriteOnce hostPath: path: "/data/pv"
This Persistent Volume is created on the node where Kubernetes is running, and it stores data in the
/data/pv
path on the host machine.You can use this PV in a pod by creating a PersistentVolumeClaim (PVC).
PersistentVolumeClaim (PVC):
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: my-pvc spec: accessModes: - ReadWriteOnce resources: requests: storage: 5Gi
Now, the PVC can be used in a pod:
apiVersion: v1 kind: Pod metadata: name: database-pod spec: containers: - name: database image: my-database-image volumeMounts: - mountPath: "/data" name: database-storage volumes: - name: database-storage persistentVolumeClaim: claimName: my-pvc
Here, the data in the
/data
folder will be stored in the Persistent Volume, ensuring that it persists even if the pod is deleted. The above YAML configuration defines a Kubernetes pod with:
A container named "database" that uses the "my-database-image" Docker image.
A volume named "database-storage" that is mounted at the
/data
directory inside the container.The volume is backed by a Persistent Volume Claim (PVC) named "my-pvc", ensuring that data in the
/data
directory persists even if the pod is restarted.
NOTE : Types of access modes for PV :
ReadWriteOnce (RWO): The volume can be mounted as read-write by a single node.
ReadOnlyMany (ROX): The volume can be mounted as read-only by multiple nodes.
ReadWriteMany (RWX): The volume can be mounted as read-write by multiple nodes.
What is Scaling in Kubernetes?
Scaling in Kubernetes means increasing or decreasing the number of replicas of your application to handle more traffic or save resources. This is done automatically or manually, depending on your configuration.
Types of Scaling in Kubernetes
Horizontal Scaling:
Increases or decreases the number of pods running your application.
Example: If one pod can't handle traffic, Kubernetes can add more pods.
Vertical Scaling:
Adjusts the CPU or memory resources allocated to a pod.
Example: If your application is slow, you can increase the pod's memory.
Example of Horizontal Scaling (Manual)
Scenario: You have a web app running with 2 replicas, but you expect a lot of traffic, so you scale it to 5 replicas.
YAML File Example:
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-app
spec:
replicas: 2 # Start with 2 replicas
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: web-container
image: nginx
Scaling Command:
kubectl scale deployment web-app --replicas=5
Now, Kubernetes creates 3 more pods, making it 5 pods in total.
Example of Horizontal Scaling (Automatic)
Scenario: Your web app experiences fluctuating traffic. You want Kubernetes to automatically scale the number of replicas based on CPU usage.
YAML File for Autoscaler:
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
name: web-app-autoscaler
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: web-app
minReplicas: 2
maxReplicas: 10
targetCPUUtilizationPercentage: 70 # Scale if CPU usage exceeds 70%
Deploy the autoscaler:
kubectl apply -f hpa.yaml
Now, Kubernetes will monitor CPU usage and adjust the replicas between 2 and 10 based on traffic.
Example of Vertical Scaling
Scenario: Your database pod needs more memory to process large queries.
Command to Edit Resources:
kubectl edit deployment database
Update Resources:
resources:
requests:
memory: "1Gi" # Minimum memory
cpu: "500m"
limits:
memory: "2Gi" # Maximum memory
cpu: "1"
Now, Kubernetes adjusts the resources for the database pod.
Summary
Horizontal Scaling: Adding/removing pods for your app.
Vertical Scaling: Changing resource limits (CPU/memory) for existing pods.
Autoscaling: Automatically adjusting resources based on demand.