Understanding Pods
Why Pods Matter
The Atomic Unit: Pods are the smallest deployable units in Kubernetes - not containers!
Key Insight: A Pod can contain multiple containers that share storage and network.
Real Impact: Understanding Pods is crucial for designing scalable applications.
Real-World Analogy
Think of a Pod as an apartment:
- Pod = The apartment unit
- Containers = Roommates sharing the apartment
- Network = Shared WiFi (localhost communication)
- Volumes = Shared storage closets
- Node = The apartment building
Pod Architecture
nginx:latest
logging-agent
Basic Pod YAML Structure
apiVersion: v1
kind: Pod
metadata:
name: my-first-pod
labels:
app: web
environment: development
spec:
containers:
- name: web-container
image: nginx:1.21
ports:
- containerPort: 80
name: http
resources:
requests:
memory: "64Mi"
cpu: "250m"
limits:
memory: "128Mi"
cpu: "500m"
Key Concepts
- One IP per Pod: All containers in a Pod share the same network namespace
- Ephemeral by Design: Pods are meant to be disposable and recreatable
- Co-location: Containers in a Pod are always scheduled on the same node
- Shared Lifecycle: All containers start and stop together
Pod Lifecycle
Pod Phases
| Phase | Description | What's Happening |
|---|---|---|
| Pending | Pod accepted but not running | Downloading images, waiting for resources, scheduling in progress |
| Running | Pod bound to node | At least one container running, may be starting or restarting |
| Succeeded | All containers terminated successfully | Job completed, exit code 0, won't be restarted |
| Failed | All containers terminated, at least one failed | Non-zero exit code, system error, resource issues |
| Unknown | Pod state cannot be determined | Node communication lost, network issues |
Container States
# Get pod status
kubectl get pods
# Detailed pod information
kubectl describe pod my-pod
# Get pod events
kubectl get events --field-selector involvedObject.name=my-pod
# Watch pod status changes
kubectl get pods -w
# Check container logs
kubectl logs my-pod -c container-name
# Previous container logs (after restart)
kubectl logs my-pod -c container-name --previous
Init Containers
apiVersion: v1
kind: Pod
metadata:
name: app-with-init
spec:
# Init containers run before app containers
initContainers:
- name: init-db
image: busybox:1.28
command: ['sh', '-c', 'until nc -z db-service 5432; do echo waiting for db; sleep 2; done;']
- name: init-cache
image: busybox:1.28
command: ['sh', '-c', 'until nc -z cache-service 6379; do echo waiting for cache; sleep 2; done;']
# App containers start after all init containers complete
containers:
- name: app
image: myapp:latest
ports:
- containerPort: 8080
Init Container Rules
- Run sequentially (one at a time)
- Must complete successfully before app containers start
- Perfect for setup tasks (database migrations, waiting for services)
- Share volumes with app containers
- Have separate resource limits
Multi-Container Patterns
Sidecar Pattern
Helper container that enhances the main container: log collection, monitoring agents, proxy/service mesh.
Ambassador Pattern
Proxy container for network communication: API gateway, database proxy, load balancing.
Adapter Pattern
Standardizes output from main container: log formatting, metrics conversion, data transformation.
Sidecar Pattern Example
apiVersion: v1
kind: Pod
metadata:
name: app-with-logging
spec:
containers:
# Main application container
- name: app
image: myapp:latest
volumeMounts:
- name: logs
mountPath: /var/log/app
# Sidecar container for log shipping
- name: log-shipper
image: fluentd:latest
volumeMounts:
- name: logs
mountPath: /var/log/app
readOnly: true
env:
- name: ELASTICSEARCH_HOST
value: "elasticsearch.logging.svc.cluster.local"
volumes:
- name: logs
emptyDir: {}
Ambassador Pattern Example
apiVersion: v1
kind: Pod
metadata:
name: app-with-proxy
spec:
containers:
# Main application
- name: app
image: myapp:latest
env:
- name: DATABASE_HOST
value: "localhost:5432" # Connect via ambassador
# Ambassador container - database proxy
- name: db-proxy
image: cloudsql-proxy:latest
command:
- "/cloud_sql_proxy"
- "-instances=project:region:instance=tcp:5432"
Hands-On Examples
Create Your First Multi-Container Pod
Let's create a web application with nginx and a sidecar for logs.
Step 1: Create the Pod
apiVersion: v1
kind: Pod
metadata:
name: web-app
labels:
app: web
spec:
containers:
# Web server
- name: nginx
image: nginx:alpine
ports:
- containerPort: 80
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
- name: logs
mountPath: /var/log/nginx
# Log reader sidecar
- name: log-reader
image: busybox
command: ['/bin/sh']
args: ['-c', 'tail -f /var/log/nginx/access.log']
volumeMounts:
- name: logs
mountPath: /var/log/nginx
# Content updater
- name: content-updater
image: busybox
command: ['/bin/sh']
args:
- -c
- |
while true; do
echo "<h1>Current Time: $(date)</h1>" > /html/index.html
sleep 10
done
volumeMounts:
- name: html
mountPath: /html
volumes:
- name: html
emptyDir: {}
- name: logs
emptyDir: {}
Step 2: Deploy and Test
# Create the pod
kubectl apply -f web-with-sidecar.yaml
# Check pod status
kubectl get pod web-app
# Check all containers in the pod
kubectl get pod web-app -o jsonpath='{.spec.containers[*].name}'
# Port forward to access the web server
kubectl port-forward pod/web-app 8080:80
# In another terminal, check logs from different containers
kubectl logs web-app -c nginx
kubectl logs web-app -c log-reader
kubectl logs web-app -c content-updater
# Exec into a specific container
kubectl exec -it web-app -c nginx -- /bin/sh
Step 3: Resource Management
apiVersion: v1
kind: Pod
metadata:
name: resource-demo
spec:
containers:
- name: app
image: nginx
resources:
requests:
memory: "64Mi" # Minimum guaranteed
cpu: "250m" # 0.25 CPU cores
limits:
memory: "128Mi" # Maximum allowed
cpu: "500m" # 0.5 CPU cores
livenessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 10
periodSeconds: 5
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 5
periodSeconds: 3
Troubleshooting Pods
Common Pod Issues
ImagePullBackOff
Problem: Kubernetes cannot pull the container image.
Common Causes: Image doesn't exist or typo in name, private registry requires authentication, network issues.
# Check events
kubectl describe pod pod-name
# Verify image name
kubectl get pod pod-name -o jsonpath='{.spec.containers[*].image}'
# Create image pull secret for private registry
kubectl create secret docker-registry regcred \
--docker-server=registry.example.com \
--docker-username=user \
--docker-password=pass
CrashLoopBackOff
Problem: Container keeps crashing and restarting.
Common Causes: Application error/crash, missing configuration, insufficient resources.
# Check logs
kubectl logs pod-name --previous
# Check events
kubectl describe pod pod-name
# Get more details
kubectl get pod pod-name -o yaml | grep -A 10 lastState
Pending Pod
Problem: Pod stuck in Pending state.
Common Causes: Insufficient resources on nodes, node selector/affinity not matching, PVC not bound.
# Check events
kubectl describe pod pod-name
# Check node resources
kubectl top nodes
# Check PVC status
kubectl get pvc
# Check scheduling constraints
kubectl get pod pod-name -o yaml | grep -A 5 nodeSelector
Debugging Commands
# Get pod details with wide output
kubectl get pods -o wide
# Describe pod for events and status
kubectl describe pod pod-name
# Get pod logs
kubectl logs pod-name
kubectl logs pod-name -c container-name # Specific container
kubectl logs pod-name --previous # Previous instance
kubectl logs pod-name --tail=50 # Last 50 lines
kubectl logs pod-name -f # Follow logs
# Execute commands in pod
kubectl exec pod-name -- command
kubectl exec -it pod-name -- /bin/bash
# Copy files to/from pod
kubectl cp pod-name:/path/to/file ./local-file
kubectl cp ./local-file pod-name:/path/to/file
# Port forwarding for debugging
kubectl port-forward pod-name 8080:80
# Get pod YAML
kubectl get pod pod-name -o yaml
# Debug with ephemeral container (K8s 1.23+)
kubectl debug pod-name -it --image=busybox
Practice Exercises
Easy Create a Multi-Container Pod
Create a pod with nginx as the main container and haproxy as a reverse proxy. Configure shared volumes for configuration and set appropriate resource limits.
Use shared volumes between containers and remember that containers in a Pod communicate via localhost.
apiVersion: v1
kind: Pod
metadata:
name: web-with-proxy
spec:
containers:
- name: nginx
image: nginx:alpine
ports:
- containerPort: 80
resources:
requests:
memory: "64Mi"
cpu: "100m"
limits:
memory: "128Mi"
cpu: "200m"
volumeMounts:
- name: config
mountPath: /etc/nginx/conf.d
- name: haproxy
image: haproxy:alpine
ports:
- containerPort: 8080
resources:
requests:
memory: "64Mi"
cpu: "100m"
limits:
memory: "128Mi"
cpu: "200m"
volumes:
- name: config
configMap:
name: nginx-config
Medium Implement Init Container
Use init containers for setup tasks: create an init container that downloads content and shares it with the main container via a volume.
Init containers run sequentially before app containers start. Use an emptyDir volume to share data between init and app containers.
apiVersion: v1
kind: Pod
metadata:
name: web-with-content
spec:
initContainers:
- name: content-downloader
image: busybox
command:
- wget
- "-O"
- "/content/index.html"
- "https://example.com/index.html"
volumeMounts:
- name: content
mountPath: /content
containers:
- name: web-server
image: nginx:alpine
ports:
- containerPort: 80
volumeMounts:
- name: content
mountPath: /usr/share/nginx/html
volumes:
- name: content
emptyDir: {}
Hard Implement Health Checks
Add liveness, readiness, and startup probes to a pod. Configure appropriate delays and thresholds for each probe type.
Use httpGet for liveness, tcpSocket for readiness, and httpGet for startup. Set initialDelaySeconds higher for liveness than readiness to give the app time to start.
apiVersion: v1
kind: Pod
metadata:
name: app-with-probes
spec:
containers:
- name: app
image: nginx
ports:
- containerPort: 80
livenessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 15
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
tcpSocket:
port: 80
initialDelaySeconds: 5
periodSeconds: 5
successThreshold: 1
failureThreshold: 3
startupProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 0
periodSeconds: 10
failureThreshold: 30
Additional Challenges
- Create a pod with all three multi-container patterns
- Implement a pod with persistent volume claims
- Create a debugging pod with network tools
- Build a pod with environment-specific configuration