iwantcoding.com
🔥 Daily 👥 Rooms 🏆 Top Log in Sign up

Pods

A Pod is the smallest deployable unit in Kubernetes — one or more containers sharing network + storage. Usually you don’t create them directly; Deployments / StatefulSets / DaemonSets create + manage them.

Spec, multi-container, init, lifecycle

EXAMPLE
# 1) Minimal Pod
apiVersion: v1
kind: Pod
metadata:
    name: nginx
    labels: { app: web }
spec:
    containers:
        - name: nginx
          image: nginx:1.27
          ports:
              - containerPort: 80

# kubectl apply -f nginx.yaml
# kubectl get pods
# kubectl logs nginx
# kubectl exec -it nginx -- sh
# kubectl describe pod nginx
# kubectl delete pod nginx

# 2) Container resources + probes
spec:
    containers:
        - name: web
          image: myapp:1.0
          ports: [{ containerPort: 3000 }]
          env:
              - name: NODE_ENV
                value: production
              - name: DB_URL
                valueFrom:
                    secretKeyRef:
                        name: db-creds
                        key:  url
          resources:
              requests: { cpu: 100m, memory: 128Mi }   # SCHEDULER uses
              limits:   { cpu: 500m, memory: 512Mi }    # KERNEL enforces
          readinessProbe:
              httpGet: { path: /health, port: 3000 }
              periodSeconds: 5
              initialDelaySeconds: 5
          livenessProbe:
              httpGet: { path: /health, port: 3000 }
              periodSeconds: 30
              initialDelaySeconds: 30
          startupProbe:                                 # for slow-starting apps
              httpGet: { path: /health, port: 3000 }
              periodSeconds: 5
              failureThreshold: 30                       # 150s grace

# Probe types:
#   httpGet   — request the path; 2xx-3xx = healthy
#   tcpSocket — open a connection on port
#   exec      — run a command; exit 0 = healthy

# readiness  → controls Service routing; failing pod removed from endpoint list
# liveness   → controls restart; failing pod killed + restarted by kubelet
# startup    → bypasses liveness during slow init; once succeeds, liveness takes over

# 3) Multi-container Pod — shared network + volumes
spec:
    containers:
        - name: app
          image: myapp:1.0
          ports: [{ containerPort: 3000 }]
          volumeMounts:
              - { name: shared, mountPath: /var/log/app }
        - name: log-sidecar
          image: fluent/fluent-bit:latest
          volumeMounts:
              - { name: shared, mountPath: /logs }
    volumes:
        - name: shared
          emptyDir: {}
# Both containers share localhost networking; the volume shares files.

# Common sidecar patterns:
#   - Logging (fluent-bit, vector, fluentd)
#   - Metrics (node-exporter, prometheus-statsd)
#   - Proxy (Envoy, Istio sidecar)
#   - Auth (oauth2-proxy)
#   - Init / config refresh

# 4) Init containers — run BEFORE main containers
spec:
    initContainers:
        - name: wait-for-db
          image: alpine
          command: ['sh', '-c', 'until nc -zv db 5432; do sleep 1; done']
        - name: migrate
          image: myapp:1.0
          command: ['npm', 'run', 'migrate']
          envFrom:
              - secretRef: { name: db-creds }
    containers:
        - name: app
          image: myapp:1.0
# Init containers run SEQUENTIALLY; main containers start after all succeed.
# Use for: waiting for dependencies, schema migrations, fetching config, permission setup.

# 5) Volumes — Pod-local storage
spec:
    containers:
        - name: app
          image: myapp:1.0
          volumeMounts:
              - { name: data, mountPath: /data }
              - { name: config, mountPath: /etc/app, readOnly: true }
    volumes:
        - name: data
          persistentVolumeClaim: { claimName: app-data }
        - name: config
          configMap: { name: app-config }
        - name: secret-files
          secret:
              secretName:  app-secrets
              defaultMode: 0400
        - name: scratch
          emptyDir: { sizeLimit: 1Gi }
        - name: host-log
          hostPath: { path: /var/log/host, type: DirectoryOrCreate }

# 6) Pod lifecycle phases
#   Pending    — accepted by API; containers not yet running
#   Running    — at least one container is running
#   Succeeded  — all containers exited successfully (Jobs)
#   Failed     — all containers exited; at least one failed
#   Unknown    — kubelet unreachable

kubectl get pods
# NAME      READY   STATUS              RESTARTS   AGE
# web-1     2/2     Running             0          5m
# web-2     1/2     CrashLoopBackOff    3          2m

# 7) Restart policies
spec:
    restartPolicy: Always       # Default for Deployments; restart on any exit
    # OnFailure                  # restart only on non-zero exit; Jobs use this
    # Never                      # don't restart; Pods used for batch

# 8) Pod security context
spec:
    securityContext:
        runAsUser:    1000
        runAsGroup:   3000
        fsGroup:      2000
        runAsNonRoot: true
        seccompProfile: { type: RuntimeDefault }
    containers:
        - name: app
          image: myapp:1.0
          securityContext:
              allowPrivilegeEscalation: false
              readOnlyRootFilesystem:   true
              capabilities:
                  drop: ['ALL']

# 9) Affinity + tolerations — control scheduling
spec:
    nodeSelector:
        disktype: ssd

    affinity:
        # Prefer pods on nodes with SSDs
        nodeAffinity:
            requiredDuringSchedulingIgnoredDuringExecution:
                nodeSelectorTerms:
                    - matchExpressions:
                          - { key: disktype, operator: In, values: [ssd] }

        # Spread replicas across zones
        podAntiAffinity:
            preferredDuringSchedulingIgnoredDuringExecution:
                - weight: 100
                  podAffinityTerm:
                      labelSelector:
                          matchExpressions:
                              - { key: app, operator: In, values: [web] }
                      topologyKey: topology.kubernetes.io/zone

    tolerations:
        - key:      node.kubernetes.io/unschedulable
          operator: Exists
          effect:   NoSchedule

# 10) Topology spread (newer + simpler than anti-affinity)
spec:
    topologySpreadConstraints:
        - maxSkew:           1
          topologyKey:       topology.kubernetes.io/zone
          whenUnsatisfiable: DoNotSchedule
          labelSelector:
              matchLabels: { app: web }
# Spreads replicas evenly across zones; refuses to schedule if it can't.

# 11) ImagePullSecrets — private registry auth
spec:
    imagePullSecrets:
        - name: ghcr-creds

# 12) Service account — Pod's identity for API access
spec:
    serviceAccountName: app-sa
    automountServiceAccountToken: false      # disable if Pod doesn't need API access

# 13) Pod priority + preemption
spec:
    priorityClassName: high-priority
# Higher-priority Pods can preempt lower-priority ones when nodes are full.

# 14) Termination grace period
spec:
    terminationGracePeriodSeconds: 60        # default 30
# SIGTERM → wait → SIGKILL
# Set high enough for graceful drain (e.g. close DB connections, finish requests)

# 15) Pod debugging
kubectl get pods -o wide                     # node + IP
kubectl describe pod web-1                    # events, conditions
kubectl logs web-1 -c app                     # specific container
kubectl logs web-1 --previous                 # logs from crashed previous instance
kubectl exec -it web-1 -c app -- sh
kubectl port-forward pod/web-1 8080:3000      # local debug
kubectl debug -it web-1 --image=busybox       # ephemeral debug container (k8s 1.25+)

# 16) Common bugs
#   ❌ Forgetting requests / limits → no scheduling guarantees; OOMKilled later
#   ❌ Liveness probe too aggressive → restart loop on slow paths
#   ❌ Readiness probe missing → traffic to unready Pods, 502s
#   ❌ Forgetting startupProbe for slow-starting Java/Python apps
#   ❌ Init containers running too long → ECS-like timeouts
#   ❌ Privileged Pods (runAsUser: 0) → security risk
#   ❌ Same Pod definition for prod + dev — diverge resource requests

# 17) When to use Pod directly vs higher-level resources
# Bare Pod         : one-off debug, never in production
# Deployment       : stateless app with N replicas
# StatefulSet      : stateful (DB, cache cluster) with stable identity
# DaemonSet        : one Pod per node (log collector, monitoring agent)
# Job              : one-shot work that completes
# CronJob          : periodic Job

# 18) Pod design principles
#   • One process per container (12-factor)
#   • Multi-container only for tightly-coupled sidecar pattern
#   • Stateless preferred; PVC + StatefulSet for state
#   • Resource requests + limits ALWAYS
#   • Probes: readiness gates traffic; liveness restarts; startup for slow init
#   • Security: non-root, read-only root FS, dropped capabilities
#   • Graceful shutdown: handle SIGTERM, drain connections
#   • Logs to stdout/stderr; metrics on /metrics; health on /health

Why it matters

Pods are the unit, but you almost never create them directly — let Deployments / StatefulSets manage them. Set requests + limits + all three probes (startup + readiness + liveness); the scheduler and traffic routing depend on them.

Tip: Tweak the snippet with Try it Yourself », then sit the quiz at the bottom of the page.

Example

Example
apiVersion: v1
kind: Pod
metadata: { name: web }
spec:
    containers:
        - name: web
            image: nginx:1.27
            ports: [{ containerPort: 80 }]
Try it Yourself »

Exercise

Smallest deployable unit.

Test yourself

Q1. A Pod is…
Q2. Pods are usually…
Q3. You manage pods at scale through…

Discussion

Loading…