It manages and automates running many containers across clusters of machines.
It builds on Docker concepts like images, containers, and networking, but adds scalability, orchestration, and automation.
Automatic Binpacking: Schedules containers efficiently across nodes.
Self-Healing: Restarts failed containers, replaces nodes, kills unhealthy pods.
Horizontal Scaling: Scale Pods automatically based on CPU/memory or custom metrics.
Service Discovery & Load Balancing: Pods can discover each other; traffic can be balanced automatically.
Automated Rollouts & Rollbacks: Deploy new versions with zero downtime.
Secret & Config Management: Securely manage sensitive data and configs.
Storage Orchestration: Mount persistent storage from cloud or local storage automatically.
We can have multiple clusters (e.g., dev, staging, prod).
It is the "brain" of Kubernetes — it makes global decisions about the cluster, such as scheduling, scaling, and maintaining desired states.
API Server:
The main entry point for all Kubernetes commands.
Exposes the Kubernetes API.
Used by both the UI and kubectl (CLI).
Validates and processes REST requests.
etcd:
A distributed key-value store that stores all cluster data (state, configurations, metadata).
Think of it as the cluster's "database."
Scheduler:
Decides which worker node should run a new Pod.
It checks resource availability and constraints.
Controller Manager:
Watches the cluster's state and makes sure the actual state matches the desired state.
For example, if a Pod fails, it creates a new one automatically.
Worker nodes are the machines (VMs or physical servers) that actually run the applications inside containers.
kubelet:
The "node agent" that communicates with the control plane.
Ensures containers are running as instructed.
Reports the node's status.
kube-proxy:
Handles networking and load balancing for Pods.
Routes traffic inside and outside the cluster.
Container Runtime (Docker here):
Responsible for running containers.
Kubernetes can use Docker, containerd, or others.
The smallest deployable unit in Kubernetes.
Each Pod can have one or more containers that share storage and network resources.
Containers inside a Pod can easily communicate with each other.
Users interact with Kubernetes through:
UI (Dashboard): Web-based graphical interface.
CLI (kubectl): Command-line tool to interact with the API server.
A user (via kubectl or UI) submits a request (e.g., "deploy this app").
The API server receives it and records it in etcd.
The scheduler picks a node for the Pod to run on.
The controller-manager ensures the desired number of Pods are running.
On the chosen worker node, the kubelet starts the containers using Docker.
The kube-proxy ensures network communication for the Pods.
For example: One image/container for each microservices (e.g., frontend, backend, database).
Build the images and push them to the registry.
docker build -t myapp/frontend:v1 .
docker push myrepo/myapp-frontend:v1
Kubernetes nodes can pull these images from the registry
Kubernetes is declarative: Describe what's needed, and Kubernetes makes it happen.
Define the application, Pods, networking, scaling, etc., in YAML files.
For example:
Deployment YAML: defines Pods, container images, replicas, etc.
Service YAML: exposes Pods for networking.
ConfigMap/Secret YAML: store app configuration or credentials.
Ingress YAML: manages external access (like HTTP routes).
Example: deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-deployment
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp-container
image: myrepo/myapp-frontend:v1
ports:
- containerPort: 80
kubectl apply -f deployment.yaml
kubectl connects to the API server (in the control plane).
The API server validates and stores your configuration in etcd (the cluster database).
The controller-manager notices that new Pods need to be created.
The scheduler decides which worker node(s) will host these Pods.
Once assigned:
The kubelet on the chosen node receives the Pod spec.
It checks which images are required — and pulls those container images from the registry.
Then, it runs the containers using the container runtime (Docker, containerd, etc.).
Each Pod gets its own internal IP.
To let users or other services reach them, define a Service object (in YAML).
For example:
apiVersion: v1
kind: Service
metadata:
name: myapp-service
spec:
selector:
app: myapp
ports:
- port: 80
targetPort: 80
type: ClusterIP
This lets other Pods in the cluster communicate with the app consistently, even if Pods are restarted or rescheduled.
If public access is needed (from outside the cluster), use:
NodePort
LoadBalancer
Ingress
Kubernetes continuously monitors system state.
Controller Manager: Ensures the number of Pods = desired replicas.
kubelet: Ensures containers on each node are running properly.
Metrics Server / Prometheus: Collects CPU, memory, etc.
Horizontal Pod Autoscaler (HPA): Scales Pods up or down automatically based on load.
Example:
kubectl get pods
kubectl logs <pod-name>
kubectl top pods
Visualize everything via the Kubernetes Dashboard (UI) or third-party tools like:
Lens
Prometheus + Grafana
K9s (CLI tool)
To update the app:
Build and push a new image version.
Update the image tag in the YAML file.
Apply the change again: kubectl apply -f deployment.yaml
Kubernetes performs a rolling update — gradually replacing old Pods with new ones without downtime.
Here’s a common structure:
Deployment: deployment.yaml — Defines Pods, container images, replicas, labels
Service: service.yaml — Exposes Pods inside or outside the cluster
ConfigMap: configmap.yaml — Stores non-sensitive configuration (like environment variables, config files)
Secret: secret.yaml — Stores sensitive data (like passwords, tokens, keys)
Ingress: ingress.yaml — Defines external access via domain names, load balancers, HTTPS, etc.
Namespace: namespace.yaml — Organizes resources logically (like folders in a project)
PersistentVolume: persistent-volume.yaml — Defines storage space (e.g., disks)
PersistentVolumeClaim: persistent-volume-claim.yaml — Requests storage from a PersistentVolume
HorizontalPodAutoscaler: autoscaler.yaml — Automatically scales Pods up/down
RBAC resources: role.yaml, rolebinding.yaml, serviceaccount.yaml — For access control and security
Number of YAML files depends on what resources the application needs.
Typical small app: 3–6 YAML files (Deployment, Service, ConfigMap, Secret, Ingress)
Larger systems (microservices): Each microservice might have its own set of YAMLs — maybe 5–10 per service.
Organizing the application files in folders:
myapp/
├─ k8s/
│ ├─ namespace.yaml
│ ├─ deployment.yaml
│ ├─ service.yaml
│ ├─ configmap.yaml
│ └─ ingress.yaml
├─ Dockerfile
└─ README.md
Each YAML file can be applied individually one at a time:
kubectl apply -f deployment.yaml
kubectl apply -f service.yaml
Or group them and apply all at once:
kubectl apply -f k8s/ # Apply all YAML files in a directory
It helps to install, upgrade, and manage complex Kubernetes applications that might otherwise require many YAML files which is messy, repetitive, and error-prone.
Helm packages all those YAMLs into a single versioned chart, like an installable “app.”
Example: helm install my-wordpress bitnami/wordpress
Note: Helm uses templated YAML files.
Chart: A Helm package — a collection of YAML templates + metadata.
Release: A running instance of a Chart in the cluster. (e.g., myapp-v1)
Values.yaml: A configuration file that customizes the Chart (like variables).
Repository: A place where Charts are stored and shared (like Docker Hub).
Helm has public repos of prebuilt apps (charts):
Examples:
Bitnami → https://charts.bitnami.com/bitnami
Kubernetes official → https://kubernetes.github.io/charts
helm repo add bitnami https://charts.bitnami.com/bitnami
helm search repo wordpress
When we create our own Helm Chart, it looks like this:
myapp/
├─ Chart.yaml # Metadata (name, version, dependencies)
├─ values.yaml # Default configuration values
├─ templates/ # Directory of templated YAML files
│ ├─ deployment.yaml
│ ├─ service.yaml
│ ├─ ingress.yaml
│ └─ configmap.yaml
└─ charts/ # (optional) sub-charts for dependencies
Helm lets us to parameterize the YAML files with variables.
Instead of hardcoding values like image names or replicas, we use templates:
spec:
replicas: {{ .Values.replicaCount }}
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
Then in values.yaml, define:
replicaCount: 3
image:
repository: myrepo/myapp
tag: v1.0.0
Install or upgrade:
helm install myapp ./myapp
# or
helm upgrade myapp ./myapp --set image.tag=v2.0.0
# Deploys chart as a new release
helm install myapp ./mychart
# Shows deployed releases
helm list
# Updates configuration or image
helm upgrade myapp ./mychart
# Revert to a previous version
helm rollback myapp 1
# Removes all related Kubernetes objects
helm uninstall myapp