Skip to content

Kubernetes Intro

Kubernetes is the orchestrator that ate the world — it manages containers at scale , handles deployments , scaling , networking , and self-healing. It's also the most over-engineered piece of infrastructure you'll encounter in your career K8s solves problems you don't have until you have them. If Docker Compose works for your team , stick with it. But if you're running 20 microservices across 10 servers and deploying three times a day , K8s changes the game

What K8s Actually Is

A distributed system for running containers across a cluster of machines It handles: * Scheduling — place containers on machines with available resources * Self-healing — restart failed containers , replace unhealthy ones * Scaling — add or remove replicas based on load * Rolling updates — deploy new versions without downtime * Service discovery — containers find each other by name * Storage orchestration — mount storage of your choice * Secret management — store and expose secrets safely

Architecture — The Control Plane

flowchart TB
    subgraph Control_Plane[Control Plane]
        API[API Server]
        Scheduler[Scheduler]
        CM[Controller Manager]
        etcd[etcd - cluster brain]
        API --> etcd
    end

    Control_Plane --> Worker1
    Control_Plane --> Worker2
    Control_Plane --> Worker3

    subgraph Worker1[Worker 1]
        P1a[Pod]
        P1b[Pod]
    end
    subgraph Worker2[Worker 2]
        P2a[Pod]
        P2b[Pod]
    end
    subgraph Worker3[Worker 3]
        P3a[Pod]
        P3b[Pod]
    end

Pods — The Atomic Unit

A pod is the smallest deployable unit — one or more containers that share network and storage Usually one container per pod (sidecar pattern is the exception)

apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
  labels:
    app: myapp
    environment: production
spec:
  containers:
    - name: myapp
      image: myapp:latest
      ports:
        - containerPort: 3000
      env:
        - name: NODE_ENV
          value: "production"
      resources:
        requests:
          memory: "256Mi"
          cpu: "250m"
        limits:
          memory: "512Mi"
          cpu: "500m"
      livenessProbe:
        httpGet:
          path: /healthz
          port: 3000
        initialDelaySeconds: 5
        periodSeconds: 10
      readinessProbe:
        httpGet:
          path: /readyz
          port: 3000
        initialDelaySeconds: 5
        periodSeconds: 10

Deployments — Declarative Updates

A Deployment manages ReplicaSets — it handles rolling updates , rollbacks , and desired state

apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp-deployment
  labels:
    app: myapp
spec:
  replicas: 3
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1           # Can spin up 1 extra pod during update
      maxUnavailable: 0     # Zero downtime — always keep all pods serving

  selector:
    matchLabels:
      app: myapp

  template:
    metadata:
      labels:
        app: myapp
    spec:
      containers:
        - name: myapp
          image: myapp:latest
          ports:
            - containerPort: 3000
          env:
            - name: DATABASE_URL
              valueFrom:
                secretKeyRef:
                  name: myapp-secrets
                  key: database_url
          resources:
            limits:
              memory: "512Mi"
              cpu: "500m"

Rolling update behavior:

flowchart LR
    subgraph Before
        P1[Pod1] --- P2[Pod2] --- P3[Pod3]
    end
    subgraph Step1
        P1n[Pod1-new] --- P2a[Pod2] --- P3a[Pod3]
    end
    subgraph Step2
        P1n2[Pod1-new] --- P2n[Pod2-new] --- P3b[Pod3]
    end
    subgraph Step3
        P1n3[Pod1-new] --- P2n2[Pod2-new] --- P3n[Pod3-new]
    end
    Before --> Step1 --> Step2 --> Step3

Zero downtime throughout the process

Services — Stable Network Endpoints

Pods come and go — they get IPs assigned dynamically. Services provide a stable DNS name that load balances across pods

apiVersion: v1
kind: Service
metadata:
  name: myapp-service
spec:
  selector:
    app: myapp              # Routes traffic to pods with this label
  ports:
    - protocol: TCP
      port: 80              # Service port
      targetPort: 3000       # Container port
  type: ClusterIP            # Internal — only accessible within cluster

Service types: * ClusterIP — internal virtual IP , only accessible inside the cluster * NodePort — exposes on each node's IP at a static port (30000-32767) * LoadBalancer — provisions a cloud load balancer (AWS ALB , GCP LB) * Ingress — not a service type , but handles HTTP routing (hostnames , paths)

# Ingress — HTTP routing
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: myapp-ingress
spec:
  rules:
    - host: api.myapp.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: myapp-service
                port:
                  number: 80

ConfigMaps and Secrets — Configuration Objects

ConfigMaps — non-sensitive config:

apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  NODE_ENV: "production"
  LOG_LEVEL: "info"
  API_URL: "https://api.example.com"

Secrets — sensitive data (base64 encoded — not encrypted):

apiVersion: v1
kind: Secret
metadata:
  name: app-secrets
type: Opaque
data:
  database_url: cG9zdGdyZXM6Ly91c2VyOnBhc3NAZGI6NTQzMi9teWFwcA==
  jwt_secret: c3VwZXJzZWNyZXRrZXkxMjM=

Using them in a pod:

spec:
  containers:
    - name: myapp
      env:
        - name: NODE_ENV
          valueFrom:
            configMapKeyRef:
              name: app-config
              key: NODE_ENV
        - name: DATABASE_URL
          valueFrom:
            secretKeyRef:
              name: app-secrets
              key: database_url
      envFrom:
        - configMapRef:
            name: app-config       # Inject ALL configmap values

Namespaces — Logical Clusters

Namespaces partition a K8s cluster into isolated environments for different teams or projects

# List namespaces
kubectl get namespaces

# Deploy to specific namespace
kubectl apply -f deployment.yaml --namespace=production

# Or set in the YAML
metadata:
  namespace: production

Common namespace structure:

default       — default for resources without a namespace
kube-system   — K8s system components (don't touch)
kube-public   — publicly readable config
production    — your production workloads
staging       — staging workloads
development   — dev workloads
monitoring    — Prometheus, Grafana, etc.

Kubectl Basics — The Only Commands You Need

# Cluster info
kubectl cluster-info
kubectl get nodes

# Pod operations
kubectl get pods
kubectl get pods -o wide                  # More detail (node IP , pod IP)
kubectl get pods --all-namespaces
kubectl describe pod myapp-pod            # Detailed status and events
kubectl logs myapp-pod
kubectl logs -f myapp-pod                 # Follow logs
kubectl logs --tail=50 myapp-pod          # Last 50 lines
kubectl exec -it myapp-pod -- bash        # Shell into container
kubectl exec myapp-pod -- env             # Run command in container

# Workload operations
kubectl get deployments
kubectl get services
kubectl get configmaps
kubectl get secrets

# Apply/delete resources
kubectl apply -f deployment.yaml
kubectl delete -f deployment.yaml
kubectl delete pod myapp-pod              # Deployment recreates it

# Scaling
kubectl scale deployment myapp --replicas=5

# Rolling updates
kubectl set image deployment/myapp myapp=myapp:v2
kubectl rollout status deployment/myapp
kubectl rollout undo deployment/myapp     # Rollback

# Port forwarding (tunnel to a pod for debugging)
kubectl port-forward pod/myapp-pod 3000:3000

# Resource usage
kubectl top pods
kubectl top nodes

Minikube — Local K8s Cluster

For local development when you can't afford cloud clusters

# Install minikube
curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
sudo install minikube-linux-amd64 /usr/local/bin/minikube

# Start cluster
minikube start --cpus=4 --memory=8192

# Check status
minikube status

# Open dashboard
minikube dashboard

# Get cluster IP
minikube ip

# Tunnel for LoadBalancer services
minikube tunnel

# Stop cluster
minikube stop

The Reality Check

K8s isn't for everyone. Here's when to use it: * Multiple microservices that need to scale independently * You're deploying multiple times per day * You need rolling updates and rollbacks * You have a team that can manage the complexity

When you don't need it: * One or two backend services — Docker Compose is fine * Small team with limited ops experience — you'll drown in YAML * Static workload that never changes — just rent a VPS * Your monthly cloud bill is under $1000 — spend the savings on dev time


next → back to DevOps home