Beginner

Pods in Kubernetes

Define, deploy and administer pods — lifecycle, init containers, probes and resource limits.

An introductory course on Kubernetes Pods, their design, deployment, and administration.


Table of Contents

  1. Kubernetes Ecosystem Overview
  2. Module 1 — Defining Kubernetes Pods
  3. Module 2 — Deploying and Administering Pods
  4. Pod Lifecycle
  5. Init Containers
  6. Probes — Liveness, Readiness, Startup
  7. Resource Requests and Limits
  8. Reference Tables
  9. Reference YAML Snippets
  10. Essential kubectl Commands

1. Kubernetes Ecosystem Overview

A Pod is the smallest deployable unit in Kubernetes. It represents an instance of a running process in a cluster and can contain one or more tightly coupled containers that share network, storage, and lifecycle.

Key Components Surrounding Pods

ComponentRole
ClusterExecution environment where Pods are deployed
DeploymentDefines the desired state for Pods (replica count, image, etc.)
ReplicaSetCreated by the Deployment — guarantees the correct number of Pods is always running
ServiceExposes Pods on the network, handles load balancing and service discovery
ConfigMapStores configuration data (env vars, CLI args) separately from application code
PersistentVolumeProvides reliable access to stable data resources (e.g., databases) for stateful Pods
flowchart TD
    User([User / kubectl]) --> Deployment
    Deployment --> RS[ReplicaSet]
    RS --> Pod1[Pod 1]
    RS --> Pod2[Pod 2]
    RS --> Pod3[Pod 3]
    Pod1 --> Container1[Container app]
    Pod2 --> Container2[Container app]
    Pod3 --> Container3[Container app]
    SVC[Service\nLoad Balancer] --> Pod1
    SVC --> Pod2
    SVC --> Pod3
    CM[ConfigMap] -.->|env vars| Pod1
    PV[PersistentVolume] -.->|storage| Pod1

    style Pod1 fill:#326ce5,color:#fff
    style Pod2 fill:#326ce5,color:#fff
    style Pod3 fill:#326ce5,color:#fff
    style SVC fill:#f0ad4e,color:#000
    style RS fill:#5bc0de,color:#000
    style Deployment fill:#5cb85c,color:#fff

2. Module 1 — Defining Kubernetes Pods

Pod Anatomy

A Pod encapsulates one or more containers, shared volumes, and a common network configuration. All containers in the same Pod share the same IP address and network namespace (localhost between them).

graph TB
    subgraph POD["Pod (Kubernetes atomic unit)"]
        direction TB
        subgraph NET["Shared network namespace"]
            IP["Unique Pod IP\n(e.g.: 10.244.0.5)"]
        end
        subgraph C1["Main container\n(e.g.: Flask app)"]
            P1["Port 5000"]
        end
        subgraph C2["Sidecar container\n(e.g.: log shipper)"]
            P2["Port 8080"]
        end
        subgraph VOL["Shared volumes"]
            V1["emptyDir\n/var/log"]
            V2["Projected Volume\n/var/run/secrets"]
        end
        NET --> C1
        NET --> C2
        C1 --> VOL
        C2 --> VOL
    end

    style POD fill:#e8f4f8,stroke:#326ce5,stroke-width:2px
    style NET fill:#dff0d8,stroke:#3c763d
    style C1 fill:#326ce5,color:#fff
    style C2 fill:#5bc0de,color:#000
    style VOL fill:#fcf8e3,stroke:#8a6d3b

Designing a Single-Container Pod

The simplest and most common Pod type. The value lies in the ephemeral nature of the Pod: if a Pod fails, the Deployment automatically replaces it, often imperceptibly to users.

Single-container Pod YAML manifest structure:

apiVersion: v1
kind: Pod
metadata:
  name: simple-app
  labels:
    app: simple-app
spec:
  containers:
  - name: simple-app
    image: myrepo/simple-app:latest
    ports:
    - containerPort: 5000

Pre-deployment validation (dry-run):

# Client-side dry-run
kubectl apply -f pod.yaml --dry-run=client

# Server-side dry-run (checks compatibility with admission controllers)
kubectl apply -f pod.yaml --dry-run=server

kubectl apply vs kubectl create

  • kubectl apply: creates or updates the targeted resource
  • kubectl create: creates a new resource — fails if a previous version exists

Designing a Multi-Container Pod

A multi-container Pod groups two or more containers that collaborate closely. The most common pattern is the sidecar pattern: a main container and an auxiliary container (e.g., log collection).

Example: Flask app + logging sidecar

apiVersion: v1
kind: Pod
metadata:
  name: multi-container-app
  labels:
    app: multi-container-app
spec:
  containers:
  - name: simple-app
    image: myrepo/simple-app:latest
    ports:
    - containerPort: 5000

  - name: log-sidecar
    image: busybox
    command: ["/bin/sh", "-c"]
    args:
    - while true; do
        echo "$(date) - Log event from sidecar";
        sleep 5;
      done

Common multi-container patterns:

PatternDescription
SidecarAuxiliary container extending main container capabilities (logs, monitoring)
AmbassadorProxies the main container’s network connections to the outside
AdapterTransforms main container output to normalize it

3. Module 2 — Deploying and Administering Pods

Launching Pods with kubectl

# Deploy a Pod from a manifest
kubectl apply -f pod.yaml

# List active Pods
kubectl get pods

# List Pods with more details
kubectl get pods -o wide

# Describe a Pod (status, conditions, events)
kubectl describe pod simple-app

# Port-forwarding to access container locally
kubectl port-forward pod/simple-app 5000:5000

# Read logs from a specific container
kubectl logs multi-container-app -c simple-app
kubectl logs multi-container-app -c log-sidecar

# Delete a Pod
kubectl delete pod simple-app

Updating a Running Pod

To update a standalone Pod (created directly, without a Deployment):

# 1. Modify the application code
# 2. Rebuild and push the Docker image
docker build -t myrepo/simple-app:latest .
docker push myrepo/simple-app:latest

# 3. Delete the old Pod (kubectl apply doesn't work for standalone Pods)
kubectl delete pod simple-app

# 4. Recreate the Pod with the new image
kubectl apply -f pod.yaml

Important: For Pods managed by a Deployment or ReplicaSet, kubectl apply handles progressive non-destructive replacement (rolling update), avoiding service interruptions. This is why Deployments are preferred over standalone Pods in production.

Scaling Pods with Deployments

A Deployment defines the desired number of replicas. If a Pod fails, the ReplicaSet automatically creates a new one as replacement.

Deployment manifest with 3 replicas:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: deployment1
spec:
  replicas: 3
  selector:
    matchLabels:
      app: app2
  template:
    metadata:
      labels:
        app: app2
    spec:
      containers:
      - name: my-container
        image: nginx:1.27.3
        ports:
        - containerPort: 80

Scaling commands:

# Deploy the Deployment
kubectl apply -f deployment1.yaml

# List Deployments
kubectl get deployments

# Scale via command line (without modifying YAML)
kubectl scale deployment deployment1 --replicas=5

# Scale to 0 (stops the Deployment without deleting it)
kubectl scale deployment deployment1 --replicas=0

Scheduling and Security Context

In Kubernetes, scheduling refers to placing Pods on cluster nodes. Since nodes have different hardware and software profiles, scheduling allows associating workloads with the most appropriate nodes.

Snippet with Security Context and resource constraints:

spec:
  securityContext:
    fsGroup: 2000          # GID for mounted volumes (applies to all containers)

  containers:
  - name: secure-container
    image: nginx:latest
    securityContext:
      runAsUser: 1000                    # UID under which the container runs
      runAsGroup: 3000                   # GID under which the container runs
      allowPrivilegeEscalation: false    # Forbids privilege escalation
      readOnlyRootFilesystem: true       # Read-only root filesystem
      capabilities:
        drop:
        - ALL                            # Drop all Linux capabilities
        add:
        - NET_BIND_SERVICE               # Only allow binding to network ports

    resources:
      requests:                          # Minimum required resources
        memory: "64Mi"
        cpu: "250m"
      limits:                            # Resource ceiling
        memory: "128Mi"
        cpu: "500m"

  nodeSelector:
    disktype: ssd                        # Deploy only on nodes with label disktype=ssd

4. Pod Lifecycle

stateDiagram-v2
    [*] --> Pending : kubectl apply

    Pending --> Running : Containers started successfully
    Pending --> Failed : Cannot pull image / init error

    Running --> Succeeded : All containers exited with code 0
    Running --> Failed : At least one container exited with non-0 code
    Running --> Unknown : Lost communication with Node

    Succeeded --> [*]
    Failed --> Pending : restartPolicy = Always or OnFailure

    note right of Pending
        - Scheduling in progress
        - Image pull in progress
        - Init containers running
    end note

    note right of Running
        - At least one container active
        - Liveness probes active
    end note

    note right of Succeeded
        - All containers terminated
        - Exit code = 0
        - Typical for Jobs
    end note

Pod Phases

PhaseDescription
PendingPod accepted by cluster but one or more containers not yet started
RunningPod is bound to a node and at least one container is running
SucceededAll containers terminated successfully (exit code 0) — typical for Jobs
FailedAll containers terminated and at least one failed (non-0 exit code)
UnknownPod state cannot be determined, usually due to node communication error

5. Init Containers

Init containers are special containers that run before the main application containers. They must complete successfully before application containers can start.

Common use cases:

  • Wait for a dependent service to be available
  • Pre-configure the filesystem
  • Clone a Git repository
  • Perform database migrations
apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
  labels:
    app: myapp
spec:
  initContainers:
  - name: init-wait-db
    image: busybox
    command: ['sh', '-c',
      'until nc -z postgres-service 5432; do
        echo "Waiting for database...";
        sleep 2;
      done;
      echo "Database available!"']

  - name: init-migrations
    image: myapp:migrations
    command: ['python', 'manage.py', 'migrate']
    env:
    - name: DB_HOST
      value: "postgres-service"

  containers:
  - name: myapp
    image: myapp:latest
    ports:
    - containerPort: 8000

Differences from main containers:

CharacteristicInit ContainerApp Container
ExecutionSequential, onceParallel, continuous
RestartIf fails, Pod restarts (per restartPolicy)Per restartPolicy
ProbesNo liveness/readiness probeProbes supported
Volume sharingYes (with app containers)Yes

6. Probes — Liveness, Readiness, Startup

Probes are diagnostics executed periodically by the kubelet to monitor container health.

flowchart LR
    subgraph PROBES["Kubernetes Probes"]
        SP["Startup Probe\nAt startup\nProtects slow apps"]
        LP["Liveness Probe\nContinuous\nRestarts on failure"]
        RP["Readiness Probe\nContinuous\nRemoves from Service on failure"]
    end

    C[Container] --> SP
    SP -->|Success| LP
    SP -->|Success| RP
    LP -->|Failure| KILL[Container killed\nand restarted]
    RP -->|Failure| REMOVE[Removed from\nService Endpoint]

    style SP fill:#f0ad4e,color:#000
    style LP fill:#d9534f,color:#fff
    style RP fill:#5cb85c,color:#fff

Probe Types

ProbeTriggerAction on Failure
Liveness ProbeContainer stuck / deadlockContainer restarted
Readiness ProbeContainer not yet ready to receive trafficContainer removed from Service endpoints
Startup ProbeSlow-starting applicationDisables liveness/readiness until success

Probe Mechanisms

# 1. HTTP GET
livenessProbe:
  httpGet:
    path: /healthz
    port: 8080
  initialDelaySeconds: 15    # Wait before first probe
  periodSeconds: 10          # Probe frequency
  failureThreshold: 3        # Failures before action
  successThreshold: 1        # Successes to consider OK
  timeoutSeconds: 5          # Timeout per probe

# 2. TCP Socket
readinessProbe:
  tcpSocket:
    port: 5432
  initialDelaySeconds: 5
  periodSeconds: 10

# 3. Exec (command in container)
livenessProbe:
  exec:
    command:
    - cat
    - /tmp/healthy
  initialDelaySeconds: 5
  periodSeconds: 5

# 4. gRPC (since Kubernetes 1.24)
livenessProbe:
  grpc:
    port: 2379
  initialDelaySeconds: 10

Complete example with all three probes:

apiVersion: v1
kind: Pod
metadata:
  name: myapp-with-probes
spec:
  containers:
  - name: myapp
    image: myapp:latest
    ports:
    - containerPort: 8080

    startupProbe:
      httpGet:
        path: /healthz
        port: 8080
      failureThreshold: 30      # Allows up to 5 minutes (30 × 10s) to start
      periodSeconds: 10

    livenessProbe:
      httpGet:
        path: /healthz
        port: 8080
      initialDelaySeconds: 0
      periodSeconds: 10
      failureThreshold: 3

    readinessProbe:
      httpGet:
        path: /ready
        port: 8080
      initialDelaySeconds: 5
      periodSeconds: 5
      failureThreshold: 3

7. Resource Requests and Limits

Resource requests and limits allow the Kubernetes scheduler to place Pods on appropriate nodes and prevent a container from consuming more than its fair share of resources.

resources:
  requests:        # Guaranteed resources — used for scheduling
    memory: "64Mi"
    cpu: "250m"    # 250 millicores = 0.25 CPU
  limits:          # Ceiling — container cannot exceed these values
    memory: "128Mi"
    cpu: "500m"

QoS Classes (Quality of Service)

Kubernetes automatically assigns a QoS class to each Pod based on its resource requests/limits:

QoS ClassConditionBehavior During Shortage
GuaranteedRequests = Limits for all containersLast to be evicted (OOM Kill)
BurstableRequests < Limits or partially undefinedEvicted after BestEffort
BestEffortNo requests or limits definedFirst to be evicted

8. Reference Tables

Pod Phases

PhaseShort Description
PendingScheduled but not yet started
RunningAt least one container active
SucceededAll containers exited with code 0
FailedAt least one container exited with error
UnknownState undeterminable

Restart Policies

PolicyBehavior
Always (default)Restarts container on any stop (success or failure)
OnFailureRestarts only if container exited with non-0 code
NeverNever restarts the container

QoS Classes

ClassRequestsLimitsEviction Priority
GuaranteedDefinedEqual to requestsLowest (evicted last)
BurstableDefinedGreater than requestsIntermediate
BestEffortNot definedNot definedHighest (evicted first)

kubectl apply vs kubectl create

CommandIf Resource ExistsIf Resource Doesn’t Exist
kubectl applyUpdatesCreates
kubectl createErrorCreates
kubectl replaceCompletely replacesError

9. Reference YAML Snippets

Minimal Single-Container Pod

apiVersion: v1
kind: Pod
metadata:
  name: simple-app
  labels:
    app: simple-app
spec:
  containers:
  - name: simple-app
    image: myrepo/simple-app:latest
    ports:
    - containerPort: 5000

Multi-Container Pod (Sidecar Pattern)

apiVersion: v1
kind: Pod
metadata:
  name: multi-container-app
spec:
  containers:
  - name: app
    image: myrepo/simple-app:latest
    ports:
    - containerPort: 5000
    volumeMounts:
    - name: shared-logs
      mountPath: /var/log/app

  - name: log-sidecar
    image: busybox
    command: ["/bin/sh", "-c"]
    args:
    - tail -f /var/log/app/app.log
    volumeMounts:
    - name: shared-logs
      mountPath: /var/log/app

  volumes:
  - name: shared-logs
    emptyDir: {}

Deployment with 3 Replicas

apiVersion: apps/v1
kind: Deployment
metadata:
  name: deployment1
spec:
  replicas: 3
  selector:
    matchLabels:
      app: app2
  template:
    metadata:
      labels:
        app: app2
    spec:
      containers:
      - name: my-container
        image: nginx:1.27.3
        ports:
        - containerPort: 80

10. Essential kubectl Commands

Pods

# Create / update a Pod
kubectl apply -f pod.yaml

# List Pods
kubectl get pods
kubectl get pods -o wide           # With extended info
kubectl get pods -n <namespace>    # In a specific namespace

# Inspect a Pod
kubectl describe pod <name>
kubectl logs <pod-name>            # Container logs
kubectl logs <pod-name> -c <container>  # Specific container
kubectl logs <pod-name> -f         # Follow logs

# Access a Pod
kubectl exec -it <pod-name> -- bash
kubectl port-forward pod/<name> 8080:80

# Delete a Pod
kubectl delete pod <name>
kubectl delete -f pod.yaml

Deployments

# Create / update a Deployment
kubectl apply -f deployment.yaml

# List Deployments
kubectl get deployments

# Scale
kubectl scale deployment <name> --replicas=5

# Rolling update
kubectl set image deployment/<name> <container>=<new-image>
kubectl rollout status deployment/<name>
kubectl rollout undo deployment/<name>

Cluster Information

# Cluster info
kubectl cluster-info

# List nodes
kubectl get nodes

# Check kubectl version
kubectl version --client

Course Summary:

This course covers the essentials of Kubernetes Pods:

  • Designing single-container and multi-container Pods via YAML manifests
  • Deploying, updating, and deleting Pods with kubectl
  • Using Deployments for automatic scaling and high availability
  • Security Contexts for applying the principle of least privilege
  • Scheduling to optimize workload placement on nodes
  • Advanced concepts: init containers, probes (liveness/readiness/startup), resource requests/limits, and QoS classes

Search Terms

pods · kubernetes · containers · pod · kubectl · classes · deployments · designing · multi-container · phases · probe · qos · reference · single-container

Interested in this course?

Contact us to book it or get a custom training plan for your team.