Pods are the smallest deployable units in Kubernetes. But you rarely manage pods directly. Deployments manage stateless applications with rolling updates and rollbacks. StatefulSets manage stateful applications with stable network identities. DaemonSets run a pod on every node. In this lesson, you'll master all the controllers that manage your pods.

1. Learning Objectives

By the end of this lesson, you will be able to:

  • Understand Pod lifecycle and multi-container pods
  • Create and manage Deployments for stateless apps
  • Perform rolling updates and rollbacks
  • Use StatefulSets for stateful applications
  • Run node-wide services with DaemonSets
  • Understand ReplicaSets for pod replication

2. Why This Matters

Real-world scenario: Your web app needs 5 replicas for high availability. When you update the image, you want zero downtime. When something fails, you want automatic healing. Deployments give you all of this automatically.

3. Core Concepts

Pods (Deep Dive)

# pod.yaml - Single container pod
apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod
  labels:
    app: nginx
    tier: frontend
spec:
  containers:
  - name: nginx
    image: nginx:1.25
    ports:
    - containerPort: 80
    resources:
      requests:
        memory: "64Mi"
        cpu: "250m"
      limits:
        memory: "128Mi"
        cpu: "500m"

# multi-container-pod.yaml - Sidecar pattern
apiVersion: v1
kind: Pod
metadata:
  name: app-with-sidecar
spec:
  containers:
  # Main application container
  - name: app
    image: nginx:alpine
    volumeMounts:
    - name: shared-logs
      mountPath: /var/log/nginx
  
  # Sidecar container (logs collector)
  - name: log-collector
    image: fluent/fluentd:latest
    volumeMounts:
    - name: shared-logs
      mountPath: /logs
    env:
    - name: FLUENTD_CONF
      value: "tail"
  
  volumes:
  - name: shared-logs
    emptyDir: {}

# Create and manage pods
kubectl apply -f pod.yaml
kubectl get pods -w
kubectl describe pod nginx-pod
kubectl logs nginx-pod
kubectl exec -it nginx-pod -- /bin/bash
kubectl delete pod nginx-pod
Pod definitions and management

Pod Lifecycle and States

Pod States:
- Pending: Pod accepted, containers not running yet
- Running: Pod bound to node, all containers running
- Succeeded: All containers terminated successfully
- Failed: At least one container terminated with failure
- Unknown: Pod state unknown (usually node communication issue)
- CrashLoopBackOff: Container repeatedly crashing
- ImagePullBackOff: Image pull failed

Pod Conditions:
- PodScheduled: Pod assigned to node
- Initialized: Init containers completed
- ContainersReady: All containers ready
- Ready: Pod ready to serve traffic

# Check pod status
kubectl get pods
kubectl describe pod <pod-name>
kubectl get events --field-selector involvedObject.name=<pod-name>
Pod states and conditions

Deployments (Stateless Applications)

# deployment.yaml - Complete deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  name: webapp-deployment
  namespace: default
  labels:
    app: webapp
    version: v1
spec:
  replicas: 3
  selector:
    matchLabels:
      app: webapp
  template:
    metadata:
      labels:
        app: webapp
        version: v1
    spec:
      containers:
      - name: webapp
        image: nginx:1.25
        ports:
        - containerPort: 80
        env:
        - name: ENVIRONMENT
          value: "production"
        livenessProbe:
          httpGet:
            path: /
            port: 80
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /ready
            port: 80
          initialDelaySeconds: 5
          periodSeconds: 5
        resources:
          requests:
            memory: "128Mi"
            cpu: "100m"
          limits:
            memory: "256Mi"
            cpu: "200m"
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0

# Deployment commands
kubectl apply -f deployment.yaml
kubectl get deployments
kubectl get replicasets
kubectl get pods -l app=webapp

# Scale deployment
kubectl scale deployment webapp-deployment --replicas=5
kubectl autoscale deployment webapp-deployment --cpu-percent=50 --min=3 --max=10

# Rolling update
kubectl set image deployment/webapp-deployment webapp=nginx:1.26
kubectl rollout status deployment/webapp-deployment

# Rollback
kubectl rollout history deployment/webapp-deployment
kubectl rollout undo deployment/webapp-deployment
kubectl rollout undo deployment/webapp-deployment --to-revision=2

# Pause and resume (for canary)
kubectl rollout pause deployment/webapp-deployment
kubectl rollout resume deployment/webapp-deployment
Deployments for stateless applications

ReplicaSets (Under the Hood)

# replicaset.yaml (usually not created directly)
apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: webapp-replicaset
spec:
  replicas: 3
  selector:
    matchLabels:
      app: webapp
  template:
    metadata:
      labels:
        app: webapp
    spec:
      containers:
      - name: nginx
        image: nginx:1.25

# ReplicaSet is managed by Deployment
# View ReplicaSets
kubectl get replicasets
kubectl describe replicaset webapp-deployment-xxx

# ReplicaSet ensures specified number of pod replicas running
# If pods die, ReplicaSet creates new ones
# Deployment manages ReplicaSet, ReplicaSet manages pods
ReplicaSets - the pod controller

StatefulSets (Stateful Applications)

# statefulset.yaml - For databases and stateful apps
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: postgres-statefulset
spec:
  serviceName: postgres-hl
  replicas: 3
  selector:
    matchLabels:
      app: postgres
  template:
    metadata:
      labels:
        app: postgres
    spec:
      containers:
      - name: postgres
        image: postgres:15-alpine
        ports:
        - containerPort: 5432
        env:
        - name: POSTGRES_PASSWORD
          valueFrom:
            secretKeyRef:
              name: postgres-secret
              key: password
        volumeMounts:
        - name: data
          mountPath: /var/lib/postgresql/data
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes: ["ReadWriteOnce"]
      resources:
        requests:
          storage: 10Gi

# Headless service for StatefulSet
apiVersion: v1
kind: Service
metadata:
  name: postgres-hl
spec:
  clusterIP: None
  selector:
    app: postgres
  ports:
  - port: 5432

# StatefulSet features:
# - Stable, unique network identifiers (pod-0, pod-1, pod-2)
# - Stable, persistent storage per pod
# - Ordered, graceful deployment and scaling
# - Ordered, automated rolling updates

kubectl get statefulsets
kubectl get pods -w
# Pods created in order: postgres-statefulset-0, -1, -2
# Each pod gets its own persistent volume
StatefulSets for stateful applications

DaemonSets (Node-Wide Services)

# daemonset.yaml - Run on every node
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: fluentd
  namespace: kube-system
spec:
  selector:
    matchLabels:
      name: fluentd
  template:
    metadata:
      labels:
        name: fluentd
    spec:
      tolerations:
      - key: node-role.kubernetes.io/master
        effect: NoSchedule
      containers:
      - name: fluentd
        image: fluent/fluentd:latest
        volumeMounts:
        - name: varlog
          mountPath: /var/log
        - name: dockercontainers
          mountPath: /var/lib/docker/containers
          readOnly: true
      volumes:
      - name: varlog
        hostPath:
          path: /var/log
      - name: dockercontainers
        hostPath:
          path: /var/lib/docker/containers

# Common DaemonSet use cases:
# - Log collection (Fluentd, Logstash)
# - Monitoring (Prometheus Node Exporter, Datadog agent)
# - Network plugins (Calico, Flannel, Cilium)
# - Storage (Ceph, GlusterFS)
# - Security (Falco, Twistlock)

kubectl get daemonsets -n kube-system
kubectl describe daemonset fluentd -n kube-system
DaemonSets for node-wide services

4. Complete Project: Multi-Tier Application

# complete-app.yaml - Frontend + Backend + Database
---
# Backend Deployment (stateless)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: backend-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      app: backend
  template:
    metadata:
      labels:
        app: backend
    spec:
      containers:
      - name: backend
        image: myapp/backend:latest
        ports:
        - containerPort: 5000
        env:
        - name: DATABASE_URL
          value: postgresql://postgres:password@postgres-statefulset-0.postgres-hl:5432/app
        resources:
          requests:
            memory: "256Mi"
            cpu: "200m"
          limits:
            memory: "512Mi"
            cpu: "400m"
        livenessProbe:
          httpGet:
            path: /health
            port: 5000
          initialDelaySeconds: 30
        readinessProbe:
          httpGet:
            path: /ready
            port: 5000
          initialDelaySeconds: 5

---
# Frontend Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
  name: frontend-deployment
spec:
  replicas: 2
  selector:
    matchLabels:
      app: frontend
  template:
    metadata:
      labels:
        app: frontend
    spec:
      containers:
      - name: frontend
        image: myapp/frontend:latest
        ports:
        - containerPort: 3000
        env:
        - name: API_URL
          value: http://backend-service:5000

---
# Database StatefulSet
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: postgres-statefulset
spec:
  serviceName: postgres-hl
  replicas: 1
  selector:
    matchLabels:
      app: postgres
  template:
    metadata:
      labels:
        app: postgres
    spec:
      containers:
      - name: postgres
        image: postgres:15-alpine
        ports:
        - containerPort: 5432
        env:
        - name: POSTGRES_PASSWORD
          value: "password"
        - name: POSTGRES_DB
          value: "app"
        volumeMounts:
        - name: data
          mountPath: /var/lib/postgresql/data
  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes: ["ReadWriteOnce"]
      resources:
        requests:
          storage: 10Gi

---
# Headless Service for StatefulSet
apiVersion: v1
kind: Service
metadata:
  name: postgres-hl
spec:
  clusterIP: None
  selector:
    app: postgres
  ports:
  - port: 5432

---
# Backend Service
apiVersion: v1
kind: Service
metadata:
  name: backend-service
spec:
  selector:
    app: backend
  ports:
  - port: 5000
    targetPort: 5000
  type: ClusterIP

---
# Frontend Service
apiVersion: v1
kind: Service
metadata:
  name: frontend-service
spec:
  selector:
    app: frontend
  ports:
  - port: 80
    targetPort: 3000
  type: LoadBalancer

# Deploy
kubectl apply -f complete-app.yaml

# Monitor rollout
kubectl rollout status deployment/backend-deployment
kubectl rollout status deployment/frontend-deployment
kubectl rollout status statefulset/postgres-statefulset

# Test scaling
kubectl scale deployment backend-deployment --replicas=5
kubectl get pods -l app=backend

# Rolling update
kubectl set image deployment/backend-deployment backend=myapp/backend:v2
kubectl rollout status deployment/backend-deployment

# Rollback if needed
kubectl rollout undo deployment/backend-deployment
Complete multi-tier application with all controller types

5. Common Errors & Solutions

6. Summary Checklist

7. Next Steps

Next lesson: Kubernetes Lesson 6.3: Services & Networking

Gataya Med

DevOps Engineer & Backend Developer. Sharing insights on cloud, automation, and scalable systems.

Comments (0)

Sarah Chen February 8, 2025

This is exactly what I needed! The initContainer approach solved our migration issues completely. Thanks for the detailed guide!

Reply

Leave a Comment