Last modified: April 27, 2026
This article is written in: πΊπΈ
Kubernetes (K8s) is an open-source container orchestration platform that automates the deployment, scaling, and management of containerised applications across a cluster of machines.
+----------------------- Kubernetes Cluster -----------------------+
| |
| +------------------+ +------------------+ |
| | Control Plane | | Worker Node 1 | |
| | | | | |
| | API Server | | kubelet | |
| | etcd | | kube-proxy | |
| | Scheduler | | Container Runtime| |
| | Controller Mgr | | Pods | |
| +------------------+ +------------------+ |
| +------------------+ |
| | Worker Node 2 | |
| | kubelet / Pods | |
| +------------------+ |
+-------------------------------------------------------------------+
kubelet agent, a kube-proxy, and a container runtime.apiVersion: v1
kind: Pod
metadata:
name: myapp-pod
labels:
app: myapp
spec:
containers:
- name: myapp
image: ghcr.io/myorg/myapp:1.0.0
ports:
- containerPort: 8000
resources:
requests:
cpu: "100m"
memory: "128Mi"
limits:
cpu: "500m"
memory: "256Mi"
Pods are ephemeral. Use higher-level controllers (Deployment, StatefulSet) for resilience.
A Deployment manages a ReplicaSet to keep the desired number of Pod replicas running and handles rolling updates.
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: ghcr.io/myorg/myapp:1.0.0
ports:
- containerPort: 8000
readinessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 15
periodSeconds: 20
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
A Service gives Pods a stable DNS name and IP address, load-balancing across healthy replicas.
apiVersion: v1
kind: Service
metadata:
name: myapp-svc
spec:
selector:
app: myapp
ports:
- port: 80
targetPort: 8000
type: ClusterIP # ClusterIP (internal), NodePort, or LoadBalancer
An Ingress exposes HTTP/HTTPS routes from outside the cluster to Services.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myapp-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: myapp-svc
port:
number: 80
apiVersion: v1
kind: ConfigMap
metadata:
name: myapp-config
data:
LOG_LEVEL: "info"
MAX_CONNECTIONS: "100"
---
apiVersion: v1
kind: Secret
metadata:
name: myapp-secrets
type: Opaque
data:
DATABASE_URL: <base64-encoded-value>
Reference them in a Pod spec:
envFrom:
- configMapRef:
name: myapp-config
- secretRef:
name: myapp-secrets
Namespaces partition cluster resources between teams or environments:
kubectl create namespace staging
kubectl -n staging apply -f deployment.yaml
# Apply manifests
kubectl apply -f deployment.yaml
kubectl apply -f ./k8s/ # apply all files in a directory
# Inspect resources
kubectl get pods -n staging
kubectl describe pod myapp-abc123 -n staging
kubectl logs myapp-abc123 -n staging --follow
# Execute a command inside a pod
kubectl exec -it myapp-abc123 -n staging -- /bin/sh
# Scale a deployment
kubectl scale deployment myapp --replicas=5 -n staging
# Roll out a new image
kubectl set image deployment/myapp myapp=ghcr.io/myorg/myapp:2.0.0 -n staging
# Watch rollout status
kubectl rollout status deployment/myapp -n staging
# Roll back to the previous revision
kubectl rollout undo deployment/myapp -n staging
# Delete resources
kubectl delete -f deployment.yaml
HPA automatically scales the number of Pods based on observed metrics.
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: myapp-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: myapp
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: db-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
storageClassName: standard
Mount it in a Pod:
volumes:
- name: db-storage
persistentVolumeClaim:
claimName: db-pvc
containers:
- name: db
volumeMounts:
- mountPath: /var/lib/postgresql/data
name: db-storage
Use a StatefulSet for stateful workloads (databases, message brokers) that need stable network identities and ordered deployment:
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgres
spec:
serviceName: postgres
replicas: 3
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
containers:
- name: postgres
image: postgres:16-alpine
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 10Gi
| QoS Class | Condition |
| Guaranteed | requests == limits for all containers |
| Burstable | requests < limits (or only one is set) |
| BestEffort | No requests or limits set |
Always set requests and limits to prevent one Pod from starving others and to help the scheduler place Pods on appropriate nodes.
securityContext.runAsNonRoot: true.securityContext.readOnlyRootFilesystem: true.