Kubernetes Complete Guide 2025 — From Zero to Production Clusters

Sanjeev SharmaSanjeev Sharma
7 min read

Advertisement

Introduction

Why This Matters

Docker answers "how do I run a container?" Kubernetes answers "how do I run hundreds of containers reliably across a fleet of servers?" It provides self-healing (restarts failed containers), horizontal scaling (adds pods under load), rolling updates (zero-downtime deploys), and service discovery (containers find each other without hardcoded IPs).

In 2025, Kubernetes runs the majority of production container workloads at companies of all sizes. AWS EKS, Google GKE, and Azure AKS are fully managed Kubernetes services used by millions of engineers. Kubernetes knowledge is one of the most consistently high-paying skills in DevOps, and the Certified Kubernetes Administrator (CKA) and Certified Kubernetes Application Developer (CKAD) certifications are widely recognized.

This guide covers Kubernetes architecture, core resources, and the operations knowledge needed to deploy and manage real applications.

Kubernetes Architecture

A Kubernetes cluster has two planes: the control plane (manages the cluster) and worker nodes (run workloads).

Control Plane components:

  • kube-apiserver — REST API gateway; all kubectl commands go here
  • etcd — distributed key-value store; all cluster state stored here
  • kube-scheduler — assigns Pods to worker nodes based on resources and constraints
  • kube-controller-manager — reconciliation loops that drive actual state toward desired state

Worker Node components:

  • kubelet — agent that runs on each node, receives Pod specs, manages containers
  • kube-proxy — handles network rules for Service IP routing
  • container runtime — containerd or CRI-O (actually runs containers)

Setting Up a Local Cluster

# Option 1: kind (Kubernetes in Docker) — fastest for CI and dev
brew install kind
kind create cluster --name dev
kubectl cluster-info --context kind-dev
 
# Option 2: minikube — full VM, good for learning
brew install minikube
minikube start --driver=docker --cpus=4 --memory=8g
minikube tunnel  # enables LoadBalancer services
 
# Option 3: k3d (k3s in Docker) — lightweight
brew install k3d
k3d cluster create dev --agents 2
 
# Verify cluster
kubectl get nodes

Core Resources: Pods

A Pod is the smallest deployable unit in Kubernetes. It wraps one or more containers that share a network namespace and storage.

# pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: api-pod
  namespace: production
  labels:
    app: api
    version: "1.2"
spec:
  containers:
  - name: api
    image: myrepo/api:1.2.0
    ports:
    - containerPort: 3000
    env:
    - name: NODE_ENV
      value: production
    resources:
      requests:
        cpu: 100m
        memory: 128Mi
      limits:
        cpu: 500m
        memory: 512Mi
    readinessProbe:
      httpGet:
        path: /health
        port: 3000
      initialDelaySeconds: 10
      periodSeconds: 5
    livenessProbe:
      httpGet:
        path: /health
        port: 3000
      initialDelaySeconds: 30
      periodSeconds: 10
kubectl apply -f pod.yaml
kubectl get pods -n production
kubectl describe pod api-pod -n production
kubectl logs api-pod -n production -f
kubectl exec -it api-pod -n production -- /bin/sh

Deployments — Managing Pod Replicas

Deployments manage a ReplicaSet which ensures the desired number of Pods are always running. Use Deployments, not bare Pods, in production.

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: api
  namespace: production
spec:
  replicas: 3
  selector:
    matchLabels:
      app: api
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0        # zero-downtime rolling update
  template:
    metadata:
      labels:
        app: api
    spec:
      containers:
      - name: api
        image: myrepo/api:1.2.0
        ports:
        - containerPort: 3000
        resources:
          requests:
            cpu: 100m
            memory: 128Mi
          limits:
            cpu: 500m
            memory: 512Mi
        readinessProbe:
          httpGet:
            path: /health
            port: 3000
          initialDelaySeconds: 10
          periodSeconds: 5
# Apply and verify
kubectl apply -f deployment.yaml
kubectl rollout status deployment/api -n production
 
# Scale
kubectl scale deployment api --replicas=5 -n production
 
# Rolling update (change image)
kubectl set image deployment/api api=myrepo/api:1.3.0 -n production
 
# Rollback to previous version
kubectl rollout undo deployment/api -n production
 
# View rollout history
kubectl rollout history deployment/api -n production

Services — Stable Networking for Pods

Pods get new IP addresses every time they are created. Services provide a stable IP and DNS name that load-balances across matching Pods.

# service.yaml
apiVersion: v1
kind: Service
metadata:
  name: api-service
  namespace: production
spec:
  selector:
    app: api           # routes to all pods with this label
  ports:
  - port: 80           # service port
    targetPort: 3000   # pod port
  type: ClusterIP      # internal only (default)

Service types:

  • ClusterIP — cluster-internal only (default, for pod-to-pod communication)
  • NodePort — exposes on each node's IP at a static port (for simple external access or testing)
  • LoadBalancer — provisions a cloud load balancer (AWS ELB, GCP LB, Azure LB)
  • ExternalName — maps to an external DNS name
kubectl apply -f service.yaml
kubectl get services -n production
 
# Access from within the cluster (pod-to-pod):
# http://api-service.production.svc.cluster.local:80

Namespaces — Cluster Organization

# Create namespace
kubectl create namespace staging
 
# Apply resources to a namespace
kubectl apply -f deployment.yaml -n staging
 
# Set default namespace for current context
kubectl config set-context --current --namespace=production
 
# List all resources across namespaces
kubectl get pods --all-namespaces
kubectl get pods -A   # shorthand

ConfigMaps and Secrets

# configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: api-config
  namespace: production
data:
  LOG_LEVEL: info
  PORT: "3000"
  FEATURE_FLAGS: '{"newUI": true, "betaAPI": false}'
 
---
# secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: api-secrets
  namespace: production
type: Opaque
stringData:
  DATABASE_URL: postgres://app:password@db:5432/mydb
  JWT_SECRET: my-super-secret-key
# Reference in Deployment
spec:
  containers:
  - name: api
    envFrom:
    - configMapRef:
        name: api-config
    - secretRef:
        name: api-secrets

Essential kubectl Commands

# Context management
kubectl config get-contexts
kubectl config use-context production
kubectl config current-context
 
# Resource inspection
kubectl get all -n production
kubectl describe deployment api -n production
kubectl explain deployment.spec.strategy
 
# Debugging
kubectl logs deployment/api -n production -f --tail=100
kubectl exec -it pod/api-xxx -n production -- /bin/sh
kubectl port-forward service/api-service 8080:80 -n production
 
# Resource management
kubectl delete pod api-xxx -n production  # pod is recreated by deployment
kubectl delete deployment api -n production
kubectl apply -f manifests/ -n production  # apply all YAML in directory
 
# Dry run (preview without applying)
kubectl apply -f deployment.yaml --dry-run=client

Common Mistakes

  • Not setting resource requests and limits — pods get evicted on node memory pressure without requests
  • Using bare Pods instead of Deployments — bare Pods are not rescheduled if the node fails
  • Storing sensitive data in ConfigMaps — use Secrets for passwords, tokens, and keys
  • Not configuring readiness probes — traffic is sent to pods that are not ready, causing errors
  • Ignoring namespace isolation — all teams dumping resources into default namespace causes confusion

Best Practices

  • Always set both requests (scheduling) and limits (runtime enforcement) for all containers
  • Use rolling update strategy with maxUnavailable: 0 for zero-downtime deployments
  • Apply resource quotas to namespaces to prevent one team from monopolizing cluster resources
  • Use liveness and readiness probes — liveness restarts stuck containers, readiness prevents premature traffic
  • Store manifests in Git and apply via CI/CD — never kubectl apply from a local workstation in production
  • Use kubectl diff -f manifest.yaml before applying to see what will change

Key Takeaways

  • Kubernetes runs containers across a cluster with self-healing, scaling, and rolling updates built in
  • The control plane (api-server, etcd, scheduler, controller-manager) manages cluster state; worker nodes run workloads
  • Deployments manage Pod replicas via ReplicaSets — always use Deployments, never bare Pods in production
  • Services provide stable network endpoints for Pods — ClusterIP for internal, LoadBalancer for external traffic
  • Resource requests and limits are mandatory in production — without them, pod scheduling and eviction are unpredictable
  • Readiness probes prevent traffic from reaching pods that are starting up or temporarily unhealthy
  • Namespaces provide logical isolation — use separate namespaces per team or per environment (staging, production)
  • All cluster changes should flow through Git and CI/CD pipelines — manual kubectl in production is a reliability risk

Advertisement

Sanjeev Sharma

Written by

Sanjeev Sharma

Full Stack Engineer · E-mopro

Related reading