Overview
Kubernetes (K8s) is the de facto standard for orchestrating containers at scale. It handles scheduling, scaling, self-healing, and service discovery. This tutorial explains the core objects you need to understand before touching a production cluster.
Why Kubernetes Exists
Running a single Docker container is easy. Running hundreds across multiple machines with rolling updates, automatic restarts, and load balancing is not. Kubernetes solves this by declaring the desired state of your application and continuously reconciling reality to match it.
| Problem | Kubernetes solution |
|---|---|
| Container crashes | Automatic restart via ReplicaSet |
| Traffic spikes | Horizontal Pod Autoscaler |
| Rolling updates | Deployment strategy with zero downtime |
| Service discovery | ClusterIP and DNS-based service names |
| Configuration drift | Declarative manifests reconciled continuously |
Cluster Architecture
| Component | Role |
|---|---|
| Control plane | API server, scheduler, controller manager, etcd |
| Worker node | Runs kubelet and container runtime; hosts pods |
| kubelet | Agent that ensures containers are running on its node |
| kube-proxy | Network rules for service routing |
Core Objects
Pod
A Pod is the smallest deployable unit. It wraps one or more containers that share a network namespace and storage volumes. In practice, most pods run a single container.
Deployment
A Deployment manages ReplicaSets, which in turn manage Pods. It handles rolling updates, rollbacks, and replica count.
Service
A Service gives a stable IP and DNS name to a group of pods. It decouples clients from the ephemeral nature of pod IPs.
| Service type | Use case |
|---|---|
ClusterIP |
Internal-only access within the cluster (default) |
NodePort |
Exposes the service on each node's IP at a static port |
LoadBalancer |
Provisions a cloud load balancer |
ExternalName |
Maps to an external DNS name |
Your First Deployment
Save this as deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
replicas: 3
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: web
image: nginx:1.27
ports:
- containerPort: 80
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 256Mi
Apply it:
kubectl apply -f deployment.yaml
kubectl get deployments
kubectl get pods -l app=web
Exposing the Deployment
apiVersion: v1
kind: Service
metadata:
name: web
spec:
selector:
app: web
ports:
- port: 80
targetPort: 80
type: ClusterIP
kubectl apply -f service.yaml
kubectl get svc web
Other pods can now reach this service at http://web.default.svc.cluster.local.
Scaling and Rolling Updates
kubectl scale deployment web --replicas=5
kubectl set image deployment/web web=nginx:1.28
kubectl rollout status deployment/web
kubectl rollout undo deployment/web
By default, Deployments use a rolling update strategy that keeps a configurable number of pods available throughout the change.
Configuration with ConfigMaps and Secrets
kubectl create configmap app-config --from-literal=LOG_LEVEL=info
kubectl create secret generic db-creds --from-literal=password=s3cr3t
Mount them into a pod:
envFrom:
- configMapRef:
name: app-config
- secretRef:
name: db-creds
Health Checks
| Probe | Purpose |
|---|---|
livenessProbe |
Restart the container if it is unhealthy |
readinessProbe |
Stop sending traffic if the pod is not ready |
startupProbe |
Delay liveness checks until the app has booted |
livenessProbe:
httpGet:
path: /healthz
port: 80
initialDelaySeconds: 10
periodSeconds: 15
Local Clusters for Practice
- minikube — minikube install guide
- kind — kind official site
- Docker Desktop — built-in Kubernetes option
Common Beginner Mistakes
- Forgetting to set resource requests and limits, causing noisy-neighbor problems.
- Using
latestimage tags; pin a specific version for reproducible rollouts. - Baking configuration into images instead of using ConfigMaps and Secrets.
- Ignoring the
namespacefield and deploying everything intodefault.
