[3/24] P is for Pods: The Basic Building Blocks of Kubernetes


πŸ“š This is Post #3 in the Kubernetes A-to-Z Series

Reading Order: ← Previous: Docker Fundamentals β†’ Next: Deployments β†’

Series Progress: 3/24 complete | Difficulty: Beginner | Time: 15 min | Part 1/6: Foundation

Welcome to the third post in our Kubernetes A-to-Z Series! Today we’ll explore Pods, the fundamental building blocks of Kubernetes. Understanding pods is crucial because everything in Kubernetes ultimately runs inside pods, whether it’s a single container or a complex multi-container application.

What is a Pod?

A Pod is the smallest deployable unit in Kubernetes. It represents a single instance of a running process in your cluster and can contain one or more containers that share storage, network, and specifications for how to run.

Pod vs Container

Container World (Docker):
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   Container 1       β”‚
β”‚   (nginx)           β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   Container 2       β”‚
β”‚   (node-app)        β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Kubernetes World (Pods):
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚           Pod 1                 β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”‚
β”‚  β”‚Container β”‚  β”‚Container β”‚    β”‚
β”‚  β”‚(nginx)   β”‚  β”‚(sidecar) β”‚    β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Key Pod Characteristics

  • Single IP Address: All containers in a pod share the same IP address
  • Shared Storage: Containers can share volumes and data
  • Same Network Namespace: Containers can communicate via localhost
  • Co-scheduled: All containers are scheduled together on the same node
  • Shared Fate: If the pod dies, all containers die together

Pod Architecture and Components

Pod Structure

apiVersion: v1
kind: Pod
metadata:
  name: my-app-pod
  labels:
    app: myapp
    tier: frontend
spec:
  containers:
  - name: app
    image: myapp:latest
    ports:
    - containerPort: 8080
    env:
    - name: DATABASE_URL
      value: "postgres://localhost:5432/mydb"
  - name: sidecar
    image: logging-agent:latest
    volumeMounts:
    - name: shared-logs
      mountPath: /var/log
  volumes:
  - name: shared-logs
    emptyDir: {}

Pod Lifecycle Phases

# Check pod status
kubectl get pods
# NAME          READY   STATUS    RESTARTS   AGE
# my-app-pod    2/2     Running   0          5m

Pod Phases:

  • Pending: Pod is created but not yet scheduled
  • Running: Pod is scheduled and containers are running
  • Succeeded: All containers completed successfully
  • Failed: All containers terminated, at least one failed
  • Unknown: Pod state cannot be determined

Creating and Managing Pods

Basic Pod Creation

# Create pod imperatively
kubectl run nginx --image=nginx:latest

# Create pod from YAML file
kubectl apply -f pod.yaml

# Get pod details
kubectl get pods
kubectl describe pod nginx

# Access pod logs
kubectl logs nginx
kubectl logs -f nginx  # Follow logs

# Execute commands in pod
kubectl exec -it nginx -- /bin/bash

Pod YAML Configuration

# simple-pod.yaml
apiVersion: v1
kind: Pod
metadata:
  name: nginx-pod
  labels:
    app: nginx
    environment: production
spec:
  containers:
  - name: nginx
    image: nginx:1.21
    ports:
    - containerPort: 80
      name: http
    resources:
      requests:
        memory: "64Mi"
        cpu: "250m"
      limits:
        memory: "128Mi"
        cpu: "500m"
    env:
    - name: NGINX_HOST
      value: "example.com"
    - name: NGINX_PORT
      value: "80"

Multi-Container Pod Patterns

1. Sidecar Pattern

A helper container that extends the main container’s functionality:

# sidecar-pattern.yaml
apiVersion: v1
kind: Pod
metadata:
  name: webapp-with-logging
spec:
  containers:
  - name: webapp
    image: myapp:latest
    ports:
    - containerPort: 8080
    volumeMounts:
    - name: logs
      mountPath: /var/log/app
  
  - name: log-collector
    image: fluentd:latest
    volumeMounts:
    - name: logs
      mountPath: /var/log/app
    - name: fluentd-config
      mountPath: /etc/fluentd
  
  volumes:
  - name: logs
    emptyDir: {}
  - name: fluentd-config
    configMap:
      name: fluentd-config

2. Adapter Pattern

Transforms the main container’s interface:

# adapter-pattern.yaml
apiVersion: v1
kind: Pod
metadata:
  name: metrics-adapter
spec:
  containers:
  - name: application
    image: legacy-app:latest
    ports:
    - containerPort: 8080
  
  - name: metrics-exporter
    image: prometheus-exporter:latest
    ports:
    - containerPort: 9090
    env:
    - name: TARGET_APP
      value: "localhost:8080"

3. Ambassador Pattern

A proxy that handles external communication:

# ambassador-pattern.yaml
apiVersion: v1
kind: Pod
metadata:
  name: database-client
spec:
  containers:
  - name: app
    image: myapp:latest
    env:
    - name: DATABASE_HOST
      value: "localhost:5432"
  
  - name: cloudsql-proxy
    image: gcr.io/cloudsql-docker/gce-proxy:latest
    command: ["/cloud_sql_proxy",
              "-instances=<project>:<region>:<instance>=tcp:5432",
              "-credential_file=/secrets/cloudsql/credentials.json"]
    volumeMounts:
    - name: cloudsql-instance-credentials
      mountPath: /secrets/cloudsql
      readOnly: true
  
  volumes:
  - name: cloudsql-instance-credentials
    secret:
      secretName: cloudsql-instance-credentials

4. Init Container Pattern

Containers that run before the main application:

# init-container-pattern.yaml
apiVersion: v1
kind: Pod
metadata:
  name: app-with-init
spec:
  initContainers:
  - name: database-migration
    image: migrate/migrate:latest
    command: ['migrate', '-path', '/migrations', '-database', 'postgres://db/myapp', 'up']
    volumeMounts:
    - name: migrations
      mountPath: /migrations
  
  - name: wait-for-services
    image: busybox:latest
    command: ['sh', '-c', 'until nc -z database-service 5432; do sleep 1; done']
  
  containers:
  - name: app
    image: myapp:latest
    ports:
    - containerPort: 8080
  
  volumes:
  - name: migrations
    configMap:
      name: database-migrations

Pod Networking

Network Model

# Get pod IP address
kubectl get pod nginx-pod -o wide
# NAME         READY   STATUS    RESTARTS   AGE   IP           NODE
# nginx-pod    1/1     Running   0          5m    10.244.1.5   node-1

Key Networking Concepts:

  • Single IP per Pod: All containers share the same IP
  • localhost Communication: Containers in same pod communicate via localhost
  • Port Binding: Containers must bind to different ports within pod
  • Network Policies: Control traffic between pods

Container Port Configuration

# networking-example.yaml
apiVersion: v1
kind: Pod
metadata:
  name: multi-port-app
spec:
  containers:
  - name: web
    image: nginx:latest
    ports:
    - containerPort: 80
      name: http
    - containerPort: 443
      name: https
  
  - name: metrics
    image: prometheus/nginx-exporter:latest
    ports:
    - containerPort: 9113
      name: metrics
    env:
    - name: SCRAPE_URI
      value: "http://localhost/nginx_status"

Pod Storage and Volumes

Volume Types

# volumes-example.yaml
apiVersion: v1
kind: Pod
metadata:
  name: storage-demo
spec:
  containers:
  - name: app
    image: busybox:latest
    command: ['sh', '-c', 'echo "Hello $(date)" >> /data/log.txt && sleep 3600']
    volumeMounts:
    - name: data
      mountPath: /data
  
  - name: reader
    image: busybox:latest
    command: ['sh', '-c', 'tail -f /data/log.txt']
    volumeMounts:
    - name: data
      mountPath: /data
  
  volumes:
  - name: data
    emptyDir: {}

Common Volume Types

# configmap-volume.yaml
apiVersion: v1
kind: Pod
metadata:
  name: configmap-demo
spec:
  containers:
  - name: app
    image: myapp:latest
    volumeMounts:
    - name: config
      mountPath: /etc/config
  volumes:
  - name: config
    configMap:
      name: app-config

# secret-volume.yaml
apiVersion: v1
kind: Pod
metadata:
  name: secret-demo
spec:
  containers:
  - name: app
    image: myapp:latest
    volumeMounts:
    - name: secrets
      mountPath: /etc/secrets
      readOnly: true
  volumes:
  - name: secrets
    secret:
      secretName: app-secrets

# hostpath-volume.yaml
apiVersion: v1
kind: Pod
metadata:
  name: hostpath-demo
spec:
  containers:
  - name: app
    image: busybox:latest
    command: ['sh', '-c', 'echo "Host data" >> /host-data/file.txt && sleep 3600']
    volumeMounts:
    - name: host-data
      mountPath: /host-data
  volumes:
  - name: host-data
    hostPath:
      path: /tmp/host-data
      type: DirectoryOrCreate

Pod Health Checks and Probes

Types of Probes

# health-checks.yaml
apiVersion: v1
kind: Pod
metadata:
  name: health-check-demo
spec:
  containers:
  - name: webapp
    image: myapp:latest
    ports:
    - containerPort: 8080
    
    # Liveness probe - checks if container is alive
    livenessProbe:
      httpGet:
        path: /health
        port: 8080
      initialDelaySeconds: 30
      periodSeconds: 10
      timeoutSeconds: 5
      failureThreshold: 3
    
    # Readiness probe - checks if container is ready to serve traffic
    readinessProbe:
      httpGet:
        path: /ready
        port: 8080
      initialDelaySeconds: 5
      periodSeconds: 5
      timeoutSeconds: 3
      successThreshold: 1
    
    # Startup probe - checks if application has started
    startupProbe:
      httpGet:
        path: /startup
        port: 8080
      initialDelaySeconds: 10
      periodSeconds: 5
      timeoutSeconds: 3
      failureThreshold: 30

Probe Configuration Options

# Different probe types
livenessProbe:
  # HTTP probe
  httpGet:
    path: /health
    port: 8080
    httpHeaders:
    - name: X-Custom-Header
      value: health-check
  
  # OR TCP probe
  tcpSocket:
    port: 8080
  
  # OR Exec probe
  exec:
    command:
    - cat
    - /tmp/healthy
  
  # Common timing parameters
  initialDelaySeconds: 30  # Wait before first probe
  periodSeconds: 10        # How often to probe
  timeoutSeconds: 5        # Timeout for each probe
  successThreshold: 1      # Minimum consecutive successes
  failureThreshold: 3      # Minimum consecutive failures

Pod Resource Management

Resource Requests and Limits

# resource-management.yaml
apiVersion: v1
kind: Pod
metadata:
  name: resource-demo
spec:
  containers:
  - name: app
    image: myapp:latest
    resources:
      requests:
        memory: "256Mi"      # Minimum guaranteed memory
        cpu: "250m"          # Minimum guaranteed CPU (250 millicores)
        ephemeral-storage: "1Gi"  # Storage requests
      limits:
        memory: "512Mi"      # Maximum allowed memory
        cpu: "500m"          # Maximum allowed CPU
        ephemeral-storage: "2Gi"  # Storage limits

Quality of Service Classes

# Check QoS class
kubectl get pod resource-demo -o jsonpath='{.status.qosClass}'
# Output: Guaranteed, Burstable, or BestEffort

QoS Classes:

  • Guaranteed: Both requests and limits set, and they’re equal
  • Burstable: Requests set, limits may be higher or unset
  • BestEffort: No requests or limits set

Pod Scheduling and Node Selection

Node Selection

# node-selection.yaml
apiVersion: v1
kind: Pod
metadata:
  name: node-selection-demo
spec:
  # Node selector - simple node selection
  nodeSelector:
    disktype: ssd
    zone: us-west-1a
  
  containers:
  - name: app
    image: myapp:latest

Node Affinity

# node-affinity.yaml
apiVersion: v1
kind: Pod
metadata:
  name: node-affinity-demo
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
        nodeSelectorTerms:
        - matchExpressions:
          - key: kubernetes.io/arch
            operator: In
            values:
            - amd64
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 1
        preference:
          matchExpressions:
          - key: disk-type
            operator: In
            values:
            - ssd
  
  containers:
  - name: app
    image: myapp:latest

Pod Affinity and Anti-Affinity

# pod-affinity.yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod-affinity-demo
spec:
  affinity:
    podAffinity:
      requiredDuringSchedulingIgnoredDuringExecution:
      - labelSelector:
          matchExpressions:
          - key: app
            operator: In
            values:
            - webapp
        topologyKey: kubernetes.io/hostname
    
    podAntiAffinity:
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 100
        podAffinityTerm:
          labelSelector:
            matchExpressions:
            - key: app
              operator: In
              values:
              - database
          topologyKey: kubernetes.io/hostname
  
  containers:
  - name: app
    image: myapp:latest

Pod Disruption and Availability

Pod Disruption Budgets

# pdb.yaml
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: myapp-pdb
spec:
  minAvailable: 2  # Minimum pods that must be available
  # maxUnavailable: 1  # Alternative: maximum pods that can be unavailable
  selector:
    matchLabels:
      app: myapp

Priority and Preemption

# priority-class.yaml
apiVersion: scheduling.k8s.io/v1
kind: PriorityClass
metadata:
  name: high-priority
value: 1000
globalDefault: false
description: "High priority class for critical pods"

# pod-with-priority.yaml
apiVersion: v1
kind: Pod
metadata:
  name: high-priority-pod
spec:
  priorityClassName: high-priority
  containers:
  - name: app
    image: myapp:latest

Pod Troubleshooting

Common Issues and Solutions

# Pod stuck in Pending
kubectl describe pod <pod-name>
# Check events for scheduling issues

# Pod stuck in CrashLoopBackOff
kubectl logs <pod-name> --previous
# Check previous container logs

# Pod not ready
kubectl get pod <pod-name> -o yaml | grep -A 10 conditions
# Check readiness conditions

# Resource issues
kubectl top pod <pod-name>
# Check resource usage

# Network issues
kubectl exec -it <pod-name> -- nslookup kubernetes.default
# Test DNS resolution

Debugging Commands

# Get detailed pod information
kubectl get pod <pod-name> -o yaml

# Check events
kubectl get events --field-selector involvedObject.name=<pod-name>

# Access pod for debugging
kubectl exec -it <pod-name> -- /bin/bash

# Copy files from pod
kubectl cp <pod-name>:/path/to/file /local/path

# Port forward for testing
kubectl port-forward pod/<pod-name> 8080:80

# Run debugging pod
kubectl run debug --image=busybox --rm -it -- /bin/sh

Best Practices for Pods

1. Always Use Health Checks

livenessProbe:
  httpGet:
    path: /health
    port: 8080
readinessProbe:
  httpGet:
    path: /ready
    port: 8080

2. Set Resource Requests and Limits

resources:
  requests:
    memory: "256Mi"
    cpu: "250m"
  limits:
    memory: "512Mi"
    cpu: "500m"

3. Use Labels Consistently

metadata:
  labels:
    app: myapp
    version: v1.0
    environment: production
    team: backend

4. Handle Graceful Shutdown

spec:
  terminationGracePeriodSeconds: 30
  containers:
  - name: app
    lifecycle:
      preStop:
        exec:
          command: ["/bin/sh", "-c", "sleep 10 && nginx -s quit"]

5. Use Namespaces for Organization

kubectl create namespace production
kubectl config set-context --current --namespace=production

6. Use Init Containers for Setup

initContainers:
- name: setup
  image: busybox:latest
  command: ['sh', '-c', 'until nc -z database 5432; do sleep 1; done']

7. Configure Proper Restart Policies

spec:
  restartPolicy: Always  # Options: Always, OnFailure, Never

Key Takeaways

  • Pods are the smallest deployable units in Kubernetes
  • Multi-container pods share network, storage, and lifecycle
  • Init containers run before main containers for setup tasks
  • Health probes ensure application availability and correctness
  • Resource management prevents resource contention and ensures QoS
  • Scheduling controls determine where pods run in the cluster
  • Volume types provide various storage options for different use cases

Command Reference Cheatsheet

# Pod Management
kubectl get pods                                    # List pods
kubectl get pods --all-namespaces                   # List all pods
kubectl get pod <name> -o wide                      # Detailed pod info
kubectl describe pod <name>                         # Pod details and events
kubectl create -f pod.yaml                          # Create from YAML
kubectl apply -f pod.yaml                           # Apply/update from YAML
kubectl delete pod <name>                           # Delete pod
kubectl delete -f pod.yaml                          # Delete from YAML file

# Pod Operations
kubectl logs <pod-name>                             # View logs
kubectl logs -f <pod-name>                          # Follow logs
kubectl logs <pod-name> -c <container>              # Specific container logs
kubectl logs <pod-name> --previous                  # Previous container logs
kubectl exec -it <pod-name> -- /bin/bash            # Execute command
kubectl exec -it <pod-name> -c <container> -- bash  # Specific container
kubectl cp <pod-name>:/remote/path /local/path      # Copy from pod
kubectl cp /local/path <pod-name>:/remote/path      # Copy to pod
kubectl port-forward pod/<name> 8080:80             # Port forward
kubectl attach -it <pod-name>                       # Attach to main process

# Pod Information
kubectl get pod <name> -o yaml                      # YAML output
kubectl get pod <name> -o json                      # JSON output
kubectl get pods --show-labels                      # Show labels
kubectl get pods --field-selector status.phase=Running  # Filter by phase
kubectl get pods --sort-by=.metadata.creationTimestamp  # Sort by creation time

# Health and Status
kubectl get events --field-selector involvedObject.name=<pod-name>  # Events
kubectl top pod <name>                              # Resource usage
kubectl get pod <name> -o jsonpath='{.status.phase}'  # Pod phase
kubectl get pod <name> -o jsonpath='{.status.podIP}'  # Pod IP
kubectl get pod <name> -o jsonpath='{.spec.nodeName}' # Node name

# Labels and Annotations
kubectl label pod <name> app=myapp                  # Add label
kubectl label pod <name> app-                       # Remove label
kubectl annotate pod <name> description="My app"    # Add annotation
kubectl get pods -l app=myapp                       # Filter by label
kubectl get pods --show-labels                      # Show all labels

Next Steps

Now that you understand pods, the basic building blocks of Kubernetes, you’re ready to explore Deployments in the next post. We’ll learn how to manage application lifecycle, perform rolling updates, and ensure high availability at scale.

Resources for Further Learning


Series Navigation:

Complete Series: Kubernetes A-to-Z Series Overview