Skip to content

Domain 1: Application Design and Build (20%)

Overview

This domain covers the fundamentals of designing and building applications for Kubernetes. You need to understand Pod design patterns, multi-container configurations, container images, batch workloads (Jobs and CronJobs), and persistent storage.

Pods

πŸ“– Pods - Core Pod documentation πŸ“– Pod Lifecycle - Phases, conditions, and container states

Pod Fundamentals

  • A Pod is the smallest deployable unit in Kubernetes
  • Pods contain one or more containers that share network (same IP, localhost communication) and storage
  • Pods are ephemeral - they are not restarted, they are replaced
  • Always use a controller (Deployment, Job, etc.) to manage Pods in production

Pod Phases

Phase Description
Pending Pod accepted but containers not yet running (scheduling, image pull)
Running Pod bound to a node, at least one container running
Succeeded All containers terminated successfully (exit code 0)
Failed All containers terminated, at least one with failure
Unknown Pod state cannot be determined (node communication failure)

Creating Pods

Imperative:

# Basic Pod
kubectl run nginx --image=nginx:1.25

# Pod with port and labels
kubectl run nginx --image=nginx:1.25 --port=80 --labels="app=web,tier=frontend"

# Pod with environment variables
kubectl run myapp --image=myapp:1.0 --env="DB_HOST=localhost" --env="DB_PORT=5432"

# Pod with resource requests
kubectl run nginx --image=nginx:1.25 --requests="cpu=100m,memory=64Mi" --limits="cpu=200m,memory=128Mi"

# Generate YAML without creating
kubectl run nginx --image=nginx:1.25 --dry-run=client -o yaml > pod.yaml

Declarative (YAML):

apiVersion: v1
kind: Pod
metadata:
  name: myapp
  labels:
    app: myapp
    tier: frontend
spec:
  containers:
    - name: myapp
      image: myapp:1.0
      ports:
        - containerPort: 8080
      env:
        - name: DB_HOST
          value: "localhost"
      resources:
        requests:
          cpu: 100m
          memory: 64Mi
        limits:
          cpu: 200m
          memory: 128Mi


Multi-Container Pod Patterns

πŸ“– Init Containers - Initialization containers πŸ“– Sidecar Containers - Native sidecar support πŸ“– Container Lifecycle Hooks - PostStart and PreStop

Sidecar Pattern

A helper container that runs alongside the main application container for the entire Pod lifecycle. Common use cases: - Log shipping (read logs from shared volume, forward to logging system) - Configuration reloading (watch for config changes, signal the main app) - Proxy (handle TLS termination, service mesh sidecar)

apiVersion: v1
kind: Pod
metadata:
  name: sidecar-example
spec:
  containers:
    - name: app
      image: myapp:1.0
      volumeMounts:
        - name: logs
          mountPath: /var/log/app
    - name: log-shipper
      image: fluentd:latest
      volumeMounts:
        - name: logs
          mountPath: /var/log/app
          readOnly: true
  volumes:
    - name: logs
      emptyDir: {}

Init Container Pattern

Containers that run to completion before any app containers start. They run sequentially - the next init container only starts after the previous one succeeds.

Common use cases: - Wait for a dependent service to be available - Populate a shared volume with data - Run database migrations - Fetch configuration from an external source

apiVersion: v1
kind: Pod
metadata:
  name: init-example
spec:
  initContainers:
    - name: wait-for-db
      image: busybox:1.36
      command: ['sh', '-c', 'until nslookup db-service.default.svc.cluster.local; do echo "Waiting for DB..."; sleep 2; done']
    - name: init-data
      image: busybox:1.36
      command: ['sh', '-c', 'wget -O /data/config.json http://config-service/config']
      volumeMounts:
        - name: config
          mountPath: /data
  containers:
    - name: app
      image: myapp:1.0
      volumeMounts:
        - name: config
          mountPath: /app/config
          readOnly: true
  volumes:
    - name: config
      emptyDir: {}

Ambassador Pattern

A proxy container that handles external communication. The main container connects to localhost, and the ambassador routes requests to the appropriate external service.

apiVersion: v1
kind: Pod
metadata:
  name: ambassador-example
spec:
  containers:
    - name: app
      image: myapp:1.0
      env:
        - name: DB_HOST
          value: "localhost"
        - name: DB_PORT
          value: "5432"
    - name: db-proxy
      image: ambassador-proxy:1.0
      ports:
        - containerPort: 5432

Adapter Pattern

A container that transforms or standardizes the main container's output. For example, converting log formats or normalizing metrics.

apiVersion: v1
kind: Pod
metadata:
  name: adapter-example
spec:
  containers:
    - name: app
      image: myapp:1.0
      volumeMounts:
        - name: logs
          mountPath: /var/log/app
    - name: log-adapter
      image: log-transformer:1.0
      volumeMounts:
        - name: logs
          mountPath: /var/log/app
          readOnly: true
  volumes:
    - name: logs
      emptyDir: {}

Container Images

πŸ“– Images - Image concepts and policies πŸ“– Pull an Image from a Private Registry - Private registry authentication

Image Pull Policies

Policy Behavior
Always Always pull the image (default for :latest tag)
IfNotPresent Pull only if not already on the node (default for specific tags)
Never Never pull - image must exist locally

Dockerfile Best Practices for Kubernetes

  • Use specific image tags, not :latest
  • Use multi-stage builds to minimize image size
  • Run as non-root user (USER instruction)
  • Include health check endpoints in your application
  • Minimize layers and clean up in the same layer
  • Use .dockerignore to exclude unnecessary files

Private Registry Authentication

# Create a docker-registry Secret
kubectl create secret docker-registry my-registry-secret \
  --docker-server=registry.example.com \
  --docker-username=user \
  --docker-password=password \
  --docker-email=user@example.com

# Reference in Pod spec
spec:
  containers:
    - name: app
      image: registry.example.com/myapp:1.0
  imagePullSecrets:
    - name: my-registry-secret

Jobs

πŸ“– Jobs - Run-to-completion workloads πŸ“– TTL Controller for Finished Resources - Automatic cleanup

Job Configuration

apiVersion: batch/v1
kind: Job
metadata:
  name: data-processor
spec:
  completions: 5          # Total successful completions needed
  parallelism: 2          # Pods running concurrently
  backoffLimit: 3          # Retries before failing
  activeDeadlineSeconds: 300  # Max duration for the Job
  ttlSecondsAfterFinished: 60  # Auto-cleanup after completion
  template:
    spec:
      restartPolicy: Never    # Must be Never or OnFailure
      containers:
        - name: processor
          image: busybox:1.36
          command: ['sh', '-c', 'echo "Processing..." && sleep 30']

Imperative Job creation:

kubectl create job my-job --image=busybox:1.36 -- echo "Hello from Job"
kubectl create job my-job --image=busybox:1.36 --dry-run=client -o yaml > job.yaml

Key Points

  • restartPolicy must be Never or OnFailure (never Always)
  • completions defaults to 1 (single-completion Job)
  • parallelism defaults to 1 (sequential execution)
  • Use completionMode: Indexed to give each Pod a unique index via JOB_COMPLETION_INDEX

CronJobs

πŸ“– CronJobs - Scheduled Jobs

CronJob Configuration

apiVersion: batch/v1
kind: CronJob
metadata:
  name: backup
spec:
  schedule: "0 2 * * *"        # Every day at 2 AM
  concurrencyPolicy: Forbid     # Don't run if previous is still running
  startingDeadlineSeconds: 200  # Max delay for starting a missed Job
  successfulJobsHistoryLimit: 3
  failedJobsHistoryLimit: 1
  suspend: false
  jobTemplate:
    spec:
      template:
        spec:
          restartPolicy: OnFailure
          containers:
            - name: backup
              image: backup-tool:1.0
              command: ['/bin/sh', '-c', 'backup.sh']

Cron Schedule Syntax

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€ minute (0-59)
β”‚ β”Œβ”€β”€β”€β”€β”€β”€β”€ hour (0-23)
β”‚ β”‚ β”Œβ”€β”€β”€β”€β”€ day of month (1-31)
β”‚ β”‚ β”‚ β”Œβ”€β”€β”€ month (1-12)
β”‚ β”‚ β”‚ β”‚ β”Œβ”€ day of week (0-6, Sunday=0)
β”‚ β”‚ β”‚ β”‚ β”‚
* * * * *

Common examples: - */5 * * * * - Every 5 minutes - 0 * * * * - Every hour - 0 0 * * * - Every day at midnight - 0 2 * * 0 - Every Sunday at 2 AM - 30 8 1 * * - 8:30 AM on the 1st of every month

Concurrency Policies

Policy Behavior
Allow Multiple Jobs can run concurrently (default)
Forbid Skip new Job if previous is still running
Replace Cancel the currently running Job and start a new one

Imperative CronJob creation:

kubectl create cronjob my-cron --image=busybox:1.36 --schedule="*/5 * * * *" -- echo "Hello"


Persistent and Ephemeral Volumes

πŸ“– Volumes - Volume types πŸ“– Persistent Volumes - PV and PVC lifecycle πŸ“– Storage Classes - Dynamic provisioning

Volume Types for CKAD

Type Lifetime Use Case
emptyDir Pod lifetime Temporary storage, shared data between containers
hostPath Node lifetime Access node filesystem (testing only)
persistentVolumeClaim Independent Persistent data that survives Pod restarts
configMap ConfigMap lifetime Mount configuration files
secret Secret lifetime Mount sensitive data as files

PersistentVolumeClaim Example

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: my-pvc
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: standard
  resources:
    requests:
      storage: 5Gi
---
apiVersion: v1
kind: Pod
metadata:
  name: app-with-storage
spec:
  containers:
    - name: app
      image: nginx:1.25
      volumeMounts:
        - name: data
          mountPath: /usr/share/nginx/html
  volumes:
    - name: data
      persistentVolumeClaim:
        claimName: my-pvc

Access Modes

Mode Abbreviation Description
ReadWriteOnce RWO Read-write by a single node
ReadOnlyMany ROX Read-only by multiple nodes
ReadWriteMany RWX Read-write by multiple nodes
ReadWriteOncePod RWOP Read-write by a single Pod

Reclaim Policies

  • Retain: Keep the PV and its data after PVC is deleted (manual cleanup)
  • Delete: Delete the PV and underlying storage when PVC is deleted (default for dynamic provisioning)
  • Recycle: Deprecated - basic scrub (rm -rf /thevolume/*)