Beginner

Docker and Kubernetes: The Dynamic Duo

Core concepts of containers and orchestration and how Docker and Kubernetes work together with GitOps.

“Docker takes your app and builds it into a container image. Kubernetes then runs it in production.”


Table of Contents

  1. Overview
  2. Module 1 — Core Concepts
  3. Module 2 — Docker and Containers
  4. Module 3 — Kubernetes and Orchestration
  5. Module 4 — GitOps Workflows
  6. Architecture Diagrams
  7. Code Snippets
  8. Reference Tables
  9. Key Concepts to Remember

1. Overview

Docker and Kubernetes form the central duo of modern application infrastructure. They are often described as “build and runtime cousins”:

ToolPrimary role
DockerBuilds container images and runs containers locally
KubernetesOrchestrates containers in production at scale

The course instructor (author of the first book ever written on Kubernetes and one of the first on Docker) notes that even with extensive command experience, it took time to grasp the fundamental principles that unlock the full power of these technologies. This course fills exactly those gaps.

What this course covers:

  • Shared kernel architecture and VM isolation
  • Microservices design patterns
  • Immutability concept for images
  • CI/CD pipelines and GitOps workflows
  • Standards and specifications: OCI, CNCF, Moby

2. Module 1 — Core Concepts

The Problem Solved: “It works on my machine”

Before containers, the number one problem in application deployment was the infamous phrase “it worked on my machine”. The root cause: library files and config files shared between applications on the same server.

Classic scenario without containers:

Server
├── App 1  ──────┐
│                ├── shared_lib v1.2  ← App 2 updates → shared_lib v2.0 → App 1 BREAKS
└── App 2  ──────┘

The Docker solution:

Each container image bundles everything the application needs:

  • Application code
  • Libraries and dependencies
  • Configuration files
  • Complete filesystem
Container Image
├── /app/         ← your code
├── /usr/lib/     ← libraries specific to this version
├── /etc/         ← configuration
└── ...           ← everything else

Thus, regardless of the execution environment (dev, test, prod), the image is identical everywhere. The “it worked on my machine” problem is eliminated structurally.


Container Architecture: Shared Kernels vs VM Isolation

Typical container stack

┌─────────────────────────────────────┐
│  App 1  │  App 2  │  App 3  │  ...  │  ← Applications
├─────────┴─────────┴─────────┴───────┤
│         Container Runtime            │  ← containerd + runc
├──────────────────────────────────────┤
│              Linux OS                │  ← Shared kernel
├──────────────────────────────────────┤
│             Hardware / Cloud         │
└──────────────────────────────────────┘

Shared Kernels Model (dominant)

All containers on the same host share the host’s kernel. Each container gets:

  • Its own process tree with a PID1
  • Its own shared memory
  • Its own network stack (interfaces, routing tables, firewall rules)
  • Its own filesystem (via union mounts)

Advantages: Fast, resource-efficient
Risk: If a container compromises the kernel, all containers are at risk

VM Isolation Model (enhanced security)

Each container runs in its own lightweight micro-VM. No kernel sharing.

Advantages: Strong isolation, maximum security
Disadvantages: Slower, consumes more resources
Good news: No workflow change — works with standard OCI images

VMs vs Containers Comparison

CriterionTraditional VMsDocker Containers
SizeSeveral GBA few MB to 100s MB
StartupMinutesSeconds
IsolationFull kernel per VMShared kernel
OverheadHigh (hypervisor)Minimal
PortabilityLimitedExcellent (OCI)
DensityLowVery high

Linux vs Windows vs Mac

Reality: ~99.9999% of containers are Linux containers.

PlatformSituation
LinuxNative containers, direct kernel
WindowsWindows containers exist but very rare; Linux containers via WSL2 (hidden VM)
MacLinux containers via hidden lightweight Linux VM

Workflow on Windows/Mac: Code is written normally, then Docker runs everything in a transparent Linux VM. The resulting image is identical to one produced on Linux — compatible with Kubernetes and the entire ecosystem.


The Microservices Revolution

Before: Monolithic Applications

Monolithic Application
┌──────────────────────────────────────────┐
│  User interface                          │
│  + Database                              │
│  + User management                       │
│  + Reporting middleware                  │
│  + Business logic module                 │
│  + ...everything else...                 │
└──────────────────────────────────────────┘
         ↓ Update = entire weekend

Problems with monoliths:

  • Updated once a year, planned 6 months in advance
  • Requires the entire team: networking, storage, DBAs, Linux admins, application vendor
  • Full weekend of work under maximum pressure
  • Everything is coupled — modifying reporting risks breaking the database

After: Microservices Architecture with Docker

Microservice 1   Microservice 2   Microservice 3   Microservice 4
┌─────────────┐  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐
│  Interface  │  │  User Mgmt  │  │  Reporting  │  │  Compute    │
│  (React)    │  │  (Node.js)  │  │  (Python)   │  │  (Java)     │
│             │  │             │  │             │  │             │
│ Container 1 │  │ Container 2 │  │ Container 3 │  │ Container 4 │
└─────────────┘  └─────────────┘  └─────────────┘  └─────────────┘

Advantages:

  • Independent updates for each service
  • Dedicated team per service
  • Granular horizontal scaling
  • Zero-downtime deployments (rolling updates)
  • Each service exposes a well-defined REST API

The Need for Orchestration

The downside of microservices: container sprawl (container proliferation).

Before (monolith)After (microservices)
1 app = 2 VMs (HA)1 app = 20 to 30+ containers
Simple to manageRequires an orchestrator

Kubernetes is the dominant orchestrator. Its role:

  • Scheduling: place containers on the right nodes
  • Self-healing: restart failing containers, replace dead nodes
  • Scaling: automatically increase/decrease replica count
  • Rolling updates: deploy without service interruption
  • Load balancing: distribute traffic across replicas
  • Service discovery: allow services to find each other

Course analogy: Kubernetes is the coach of a soccer team — it organizes players (containers), replaces them if injured (self-healing), and adapts strategy mid-game (dynamic scaling).


Standards and Specifications: OCI, CNCF, Moby

These standards are the foundation of trust in the container ecosystem. Course analogy: Like the standardization of railroad gauges in the 19th century catalyzed global railway innovation, OCI specs catalyze container innovation.

OCI — Open Container Initiative

Vendor-neutral standard defining three specifications:

OCI SpecWhat it standardizes
Image SpecStructure and format of container images
Distribution SpecHow to store and retrieve images in registries
Runtime SpecHow to execute an image as a container

CNCF — Cloud Native Computing Foundation

  • Governance and development of Kubernetes and many cloud-native projects
  • Members: Google, Amazon, Microsoft, IBM, Red Hat, etc.
  • Ensures ecosystem neutrality and longevity

Moby Project

  • Open-source infrastructure underlying Docker
  • Modular components reused throughout the industry

Practical importance: Choosing tools that respect these standards guarantees interoperability and protects your investments long-term.


High-Level Workflow

Simplified workflow

Code  →  docker build  →  docker push  →  Kubernetes run

Real workflow with CI/CD and GitOps

1. Developer commits code  →  Git repo (app code)
2. CI tool detects the commit
3. CI: docker build (image)
4. CI: docker push (registry)
5. CI: run automated tests
6. CI: updates config repo (production YAML)
7. GitOps tool detects change in config repo
8. GitOps: kubectl apply on Kubernetes cluster
9. Production updated automatically

Everything is automatic and pull-based after the developer’s commit. That’s the magic of GitOps.


3. Module 2 — Docker and Containers

Docker History

2008: Solomon Hykes and associates found dotCloud, a PaaS company that uses Linux containers (not VMs) under the hood. Linux containers existed, but were difficult to use.

Internal solution: To simplify their own work, they create an internal tool — initially called “DC” — that would become Docker.

March 2013: Solomon Hykes presents Docker in 5 minutes at PyCon in Santa Clara. “Hello World” demo in front of the entire world. Instant revolution.

Evolution:

  • Early days: Docker Inc. controlled everything (monolithic daemon)
  • Rapid growth but governance issues
  • Contribution to open-source projects: containerd (CNCF), runc (OCI)
  • Today: modular architecture, community-governed components

Docker Inc. (big D) vs docker (little d):

  • Docker Inc. = the company in Palo Alto, California
  • docker = the technology that builds images and runs containers

Docker Architecture

┌─────────────────────────────────────────────────────────┐
│                    Docker Client (CLI)                   │
│              $ docker build / push / run                 │
└──────────────────────┬──────────────────────────────────┘
                       │ API calls (REST)
┌──────────────────────▼──────────────────────────────────┐
│                   Docker Daemon                          │
│              (long-running daemon process)               │
│  ┌───────────┐  ┌──────────────┐  ┌──────────────────┐  │
│  │ BuildKit  │  │  containerd  │  │   libnetwork     │  │
│  │ (builds)  │  │  (lifecycle) │  │   (networking)   │  │
│  └───────────┘  └──────┬───────┘  └──────────────────┘  │
│                        │                                  │
│                  ┌─────▼──────┐                          │
│                  │    runc    │                           │
│                  │(OCI runtime│                           │
│                  │  spec)     │                           │
│                  └────────────┘                          │
└─────────────────────────────────────────────────────────┘

Key components:

ComponentRole
Docker CLIUser interface, converts commands to API calls
Docker DaemonServer, exposes REST API, delegates to specialized components
BuildKitImage build engine (replaces legacy builder)
containerdManages container lifecycle (start, stop, pause…)
runcLow-level runtime, creates containers per OCI spec
libnetworkContainer network management

Client-server architecture: The client and server can run on different machines, but in practice they are on the same machine (including in the hidden Linux VM on Windows/Mac).


Docker Demo: Build, Push, Run

# Build an image from the Dockerfile
docker build -t myapp:v1.0 .

# Push to Docker Hub (upload)
docker push myaccount/myapp:v1.0

# Pull and run from any machine (download + execute)
docker run -d -p 8080:80 myaccount/myapp:v1.0

# Check running containers
docker ps

# View logs
docker logs <container_id>

What happens under the hood during docker build:

  1. The CLI sends the build context to the daemon via the API
  2. The daemon delegates to BuildKit
  3. BuildKit reads the Dockerfile instruction by instruction
  4. Each instruction creates a layer in the image
  5. The final image is a stack of layers (union filesystem)
  6. The image is OCI-compliant — portable everywhere

4. Module 3 — Kubernetes and Orchestration

Kubernetes History

Before Kubernetes at Google: Google processed billions of containers per week for Search, Gmail and other services, before Docker even existed. They used internal tools: Borg and Omega.

2014: Google engineers draw inspiration from Borg/Omega to create a new tool from scratch, developed open-source on GitHub. It is neither a port nor a rename — it is an original creation.

Anecdote: They wanted to call it Seven of Nine (a Borg drone from Star Trek — a nod to Borg, Google’s internal tool + Borg from Star Trek). Copyright law prevailed. They settled on Kubernetes, from ancient Greek kybernḗtēs = “helmsman”. Hence the logo with the ship’s wheel.

k8s: Official abbreviation — “K” + 8 letters + “s”. Popular contraction of “Kubernetes”.

Governance: Kubernetes is now under the umbrella of CNCF (Cloud Native Computing Foundation), ensuring vendor neutrality and longevity.


Kubernetes: The Cloud OS

The analogy is powerful: Kubernetes does for clouds what an OS does for hardware.

Traditional OS:                      Kubernetes:
┌────────────────────┐               ┌────────────────────────┐
│   Applications     │               │   Your apps (containers)│
├────────────────────┤               ├────────────────────────┤
│   Linux / Windows  │               │      Kubernetes        │
│   (abstracts HW)   │               │  (abstracts the cloud) │
├────────────────────┤               ├────────────────────────┤
│ CPU / RAM / Disk   │               │ AWS / Azure / GCP /    │
│   (doesn't matter  │               │ On-premises / Home lab │
│    which)          │               │   (doesn't matter)     │
└────────────────────┘               └────────────────────────┘

Practical consequences:

  • Migrating from AWS to Azure? Kubernetes abstracts the differences
  • Bursting to the cloud from on-premises? Kubernetes handles it
  • Multi-cloud? Kubernetes unifies the interface
  • No magic — there are always considerations — but the abstraction makes everything much simpler

The Declarative Model

Imperative (scripts): “Do X, then Y, then Z…”
Declarative (Kubernetes): “Here’s what I want. Figure it out.”

Course analogy: Telling your architect “I want an open-plan kitchen connected to the living room, with glass doors on the garden side and a level floor with the terrace” — that’s declarative. You declare the desired state and let the expert handle the details.

How it works:

1. You write a YAML file (desired state)
2. kubectl apply -f deployment.yaml
3. Kubernetes compares desired state vs current state
4. Kubernetes executes actions to reach desired state
5. Kubernetes continuously monitors and maintains desired state

Control loop:

         ┌─────────────────────────────────┐
         │         Kubernetes              │
         │                                 │
Desired  │  ┌──────────────────────────┐   │
State    │  │  Observe current state   │   │
(YAML)──►│  │  Compare with desired    │   │
         │  │  Take corrective action  │   │
         │  └──────────────────────────┘   │
         │            ↕ (infinite loop)    │
         └─────────────────────────────────┘

Kubernetes Demo: Deployment and Self-Healing

Cluster used in the demo: 1 control plane node + 3 worker nodes (4 nodes total)

# Apply declarative configuration
kubectl apply -f deployment.yaml

# View deployed pods
kubectl get pods

# See which nodes the pods are running on
kubectl get pods -o wide

# Scale manually
kubectl scale deployment myapp --replicas=6

# View deployments
kubectl get deployments

# Describe a deployment for details
kubectl describe deployment myapp

# View nodes
kubectl get nodes

Self-healing in action:

# Simulate a node failure
kubectl cordon node3    # mark node as non-schedulable
kubectl drain node3     # evacuate pods to other nodes

# Kubernetes automatically redeploys pods to remaining nodes
kubectl get pods -w    # -w = watch (real-time)

Result with 4 replicas on 3 nodes: Kubernetes automatically distributes — one node receives 2 containers. When a node fails, containers are automatically rescheduled on available nodes.


5. Module 4 — GitOps Workflows

Complete Workflow Summary

This module recaps the entire Docker → Kubernetes pipeline in a real context and introduces GitOps as the standard pattern in production.

OCI Specs Recap

SpecFunction
Image SpecStandardizes container image structure
Distribution SpecStandardizes storage/retrieval in registries
Runtime SpecStandardizes container execution

These three specs guarantee that Docker images work on Kubernetes (and vice-versa) without modification.

GitOps — Modern Deployment Pattern

Fundamental principle: Git is the single source of truth for everything — both application code AND infrastructure configuration.

Two Git repos:

  1. App repo: application source code
  2. Config repo: production Kubernetes YAML files

Popular GitOps tools: ArgoCD, Flux

Developer
    │ git commit + push
    ▼
App Git Repo ──────────────────────────────►
    │                                        │
    │ (CI tool observes)                     │
    ▼                                        │
CI Pipeline (GitHub Actions, Jenkins...)     │
    │ docker build                           │
    │ docker push → Registry                 │
    │ update config repo (new image tag)     │
    ▼                                        │
Config Git Repo ◄────────────────────────────
    │
    │ (GitOps tool observes)
    ▼
GitOps Tool (ArgoCD/Flux) on Kubernetes
    │ kubectl apply (automatic)
    ▼
Production Kubernetes Cluster ✓

GitOps advantages:

  • Full auditability: every deployment is a traceable Git commit
  • Easy rollback: git revert = deployment rollback
  • No direct access to production cluster (principle of least privilege)
  • Consistency: cluster state always reflects Git
  • Pull-based: cluster pulls config from Git (vs push-based)

6. Architecture Diagrams

Docker + Kubernetes Architecture

graph TB
    subgraph "Developer Machine"
        DEV[Source Code]
        DF[Dockerfile]
        DC[docker-compose.yml]
    end

    subgraph "Docker Build Pipeline"
        BK[BuildKit]
        IMG[Container Image OCI]
    end

    subgraph "Registry"
        DH[Docker Hub]
        ECR[AWS ECR]
        ACR[Azure ACR]
    end

    subgraph "Kubernetes Cluster"
        CP[Control Plane]
        subgraph "Worker Nodes"
            N1[Node 1\ncontainerd + runc]
            N2[Node 2\ncontainerd + runc]
            N3[Node 3\ncontainerd + runc]
        end
        subgraph "Pods"
            P1[Pod 1]
            P2[Pod 2]
            P3[Pod 3]
            P4[Pod 4]
        end
    end

    DEV --> DF
    DF --> BK
    BK --> IMG
    IMG --> DH
    IMG --> ECR
    IMG --> ACR
    DH --> CP
    ECR --> CP
    ACR --> CP
    CP --> N1
    CP --> N2
    CP --> N3
    N1 --> P1
    N1 --> P2
    N2 --> P3
    N3 --> P4

Containerization to K8s Deployment Flow

flowchart LR
    A[Source Code\nApp Microservice] -->|Dockerfile| B[docker build]
    B -->|OCI Image| C[Container Image]
    C -->|docker push| D[Registry\nDocker Hub / ECR / ACR]
    D -->|kubectl apply| E[Kubernetes\nDeployment]
    E -->|scheduling| F[Pods on Workers]
    F -->|expose| G[Service\nLoadBalancer / ClusterIP]
    G -->|external routing| H[Ingress Controller]
    H -->|HTTPS| I[Users]

    style A fill:#4A90D9,color:#fff
    style C fill:#2496ED,color:#fff
    style D fill:#FF6B6B,color:#fff
    style E fill:#326CE5,color:#fff
    style F fill:#326CE5,color:#fff
    style G fill:#326CE5,color:#fff
    style H fill:#326CE5,color:#fff

Complete GitOps Pipeline

sequenceDiagram
    participant Dev as Developer
    participant AppRepo as App Git Repo
    participant CI as CI Pipeline
    participant Reg as Container Registry
    participant CfgRepo as Config Git Repo
    participant GitOps as GitOps Tool (ArgoCD/Flux)
    participant K8s as Kubernetes Cluster

    Dev->>AppRepo: git push (new code)
    AppRepo->>CI: webhook trigger
    CI->>CI: docker build
    CI->>Reg: docker push (new image)
    CI->>CI: run automated tests
    CI->>CfgRepo: update image tag in deployment.yaml
    CfgRepo->>GitOps: observe change (pull)
    GitOps->>K8s: kubectl apply -f deployment.yaml
    K8s->>K8s: rolling update of pods
    K8s-->>Dev: deployment complete ✓

Shared Kernels vs VM Isolation Model

graph TB
    subgraph "Shared Kernels Model (fast)"
        HW1[Hardware]
        OS1[Linux OS + SHARED Kernel]
        CT1[containerd + runc]
        C1A[Container A]
        C1B[Container B]
        C1C[Container C]
        HW1 --> OS1 --> CT1
        CT1 --> C1A
        CT1 --> C1B
        CT1 --> C1C
    end

    subgraph "VM Isolation Model (secure)"
        HW2[Hardware]
        OS2[Linux OS]
        VM1[Micro-VM 1\nIsolated Kernel]
        VM2[Micro-VM 2\nIsolated Kernel]
        VM3[Micro-VM 3\nIsolated Kernel]
        C2A[Container A]
        C2B[Container B]
        C2C[Container C]
        HW2 --> OS2
        OS2 --> VM1 --> C2A
        OS2 --> VM2 --> C2B
        OS2 --> VM3 --> C2C
    end

7. Code Snippets

Reference Dockerfile

# ─── Stage 1: Build ───────────────────────────────────────────────────────────
FROM node:20-alpine AS builder

# Set working directory
WORKDIR /app

# Copy dependency files first (cache layer optimization)
COPY package.json package-lock.json ./

# Install dependencies
RUN npm ci --only=production

# ─── Stage 2: Production Image ────────────────────────────────────────────────
FROM node:20-alpine AS production

# Principle of least privilege: don't run as root
RUN addgroup -S appgroup && adduser -S appuser -G appgroup

WORKDIR /app

# Copy dependencies from build stage
COPY --from=builder /app/node_modules ./node_modules

# Copy source code
COPY . .

# Change file ownership
RUN chown -R appuser:appgroup /app

# Use non-root user
USER appuser

# Expose port
EXPOSE 3000

# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1

# Start command
CMD ["node", "server.js"]

Kubernetes Deployment YAML

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
  namespace: production
  labels:
    app: myapp
    version: v1.0
spec:
  # Desired number of replicas (containers) — desired state
  replicas: 4
  
  selector:
    matchLabels:
      app: myapp
  
  # Rolling update strategy (zero-downtime deployment)
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1        # max extra pods during update
      maxUnavailable: 0  # no pods unavailable during update
  
  template:
    metadata:
      labels:
        app: myapp
        version: v1.0
    spec:
      containers:
        - name: myapp
          image: myaccount/myapp:v1.0  # Image from Docker Hub
          ports:
            - containerPort: 3000
          
          # Resources (limits and requests)
          resources:
            requests:
              memory: "128Mi"
              cpu: "250m"
            limits:
              memory: "256Mi"
              cpu: "500m"
          
          # Health checks
          livenessProbe:
            httpGet:
              path: /health
              port: 3000
            initialDelaySeconds: 15
            periodSeconds: 20
          
          readinessProbe:
            httpGet:
              path: /ready
              port: 3000
            initialDelaySeconds: 5
            periodSeconds: 10
          
          # Environment variables (no hardcoded secrets!)
          env:
            - name: NODE_ENV
              value: "production"
            - name: DB_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: db-secrets
                  key: password
      
      # Spread pods across different nodes (high availability)
      topologySpreadConstraints:
        - maxSkew: 1
          topologyKey: kubernetes.io/hostname
          whenUnsatisfiable: DoNotSchedule
          labelSelector:
            matchLabels:
              app: myapp

Kubernetes Service YAML

# service.yaml
apiVersion: v1
kind: Service
metadata:
  name: myapp-service
  namespace: production
spec:
  selector:
    app: myapp        # Targets all pods with this label
  
  ports:
    - protocol: TCP
      port: 80         # Port exposed by the Service
      targetPort: 3000 # Container port
  
  # Service types:
  # ClusterIP  : accessible only within the cluster (default)
  # NodePort   : accessible from outside via node port
  # LoadBalancer: creates a cloud load balancer (AWS ELB, Azure LB, etc.)
  type: ClusterIP

---
# For external access via cloud load balancer:
apiVersion: v1
kind: Service
metadata:
  name: myapp-lb
  namespace: production
spec:
  selector:
    app: myapp
  ports:
    - port: 80
      targetPort: 3000
  type: LoadBalancer

Kubernetes Ingress YAML

# ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: myapp-ingress
  namespace: production
  annotations:
    # Annotations depend on ingress controller used (nginx, traefik, etc.)
    nginx.ingress.kubernetes.io/rewrite-target: /
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    # Rate limiting
    nginx.ingress.kubernetes.io/limit-rps: "100"
spec:
  # TLS / HTTPS
  tls:
    - hosts:
        - myapp.example.com
      secretName: myapp-tls-secret  # Secret containing the SSL certificate
  
  rules:
    - host: myapp.example.com
      http:
        paths:
          # Main route to frontend app
          - path: /
            pathType: Prefix
            backend:
              service:
                name: myapp-service
                port:
                  number: 80
          
          # Route to backend API
          - path: /api
            pathType: Prefix
            backend:
              service:
                name: api-service
                port:
                  number: 8080

Reference docker-compose.yml

# docker-compose.yml (local development)
version: "3.9"

services:
  # Frontend application
  frontend:
    build:
      context: ./frontend
      dockerfile: Dockerfile
    ports:
      - "3000:3000"
    environment:
      - API_URL=http://backend:8080
    depends_on:
      backend:
        condition: service_healthy
    networks:
      - app-network

  # Backend application / API
  backend:
    build:
      context: ./backend
      dockerfile: Dockerfile
    ports:
      - "8080:8080"
    environment:
      - DB_HOST=postgres
      - DB_PORT=5432
      - DB_NAME=myapp
      - DB_USER=appuser
      - DB_PASSWORD=${DB_PASSWORD}  # Env variable from .env
    depends_on:
      postgres:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "wget", "-q", "--spider", "http://localhost:8080/health"]
      interval: 30s
      timeout: 10s
      retries: 3
    networks:
      - app-network

  # PostgreSQL database
  postgres:
    image: postgres:16-alpine
    volumes:
      - postgres_data:/var/lib/postgresql/data
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql
    environment:
      - POSTGRES_DB=myapp
      - POSTGRES_USER=appuser
      - POSTGRES_PASSWORD=${DB_PASSWORD}
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U appuser -d myapp"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - app-network

  # Redis cache
  redis:
    image: redis:7-alpine
    command: redis-server --requirepass ${REDIS_PASSWORD}
    networks:
      - app-network

volumes:
  postgres_data:

networks:
  app-network:
    driver: bridge

8. Reference Tables

Docker vs kubectl Commands

ActionDockerkubectl
List running resourcesdocker pskubectl get pods
List all resourcesdocker ps -akubectl get pods --all-namespaces
View logsdocker logs <id>kubectl logs <pod>
Execute a commanddocker exec -it <id> bashkubectl exec -it <pod> -- bash
Inspect a resourcedocker inspect <id>kubectl describe pod <pod>
Build an imagedocker build -t name:tag .(handled by CI/CD)
Push to registrydocker push name:tag(handled by CI/CD)
Pull from registrydocker pull name:tag(automatic on deploy)
Stop a containerdocker stop <id>kubectl delete pod <pod>
Remove a resourcedocker rm <id>kubectl delete deployment <name>
Scaledocker-compose up --scale app=3kubectl scale deployment <name> --replicas=3
Apply a configdocker-compose up -f compose.ymlkubectl apply -f deployment.yaml
View resource configdocker inspect <id>kubectl get deployment <name> -o yaml
View eventsdocker eventskubectl get events
Port forwardingdocker run -p 8080:80kubectl port-forward pod/<name> 8080:80
View system resourcesdocker statskubectl top pods
Rollout history(N/A)kubectl rollout history deployment/<name>
Rollback(rebuild)kubectl rollout undo deployment/<name>

Essential Kubernetes Objects

K8s ObjectDescriptionUsage
PodBasic unit — 1 or more containers sharing network and storageRarely created directly
DeploymentManages pod replicas with rolling updates and self-healingStateless apps in production
StatefulSetLike Deployment but with stable identity (for DBs, etc.)PostgreSQL, MongoDB, Kafka
DaemonSet1 pod per node (automatically)Monitoring agents, log collectors
Service (ClusterIP)Exposes pods internally within the clusterInter-service communication
Service (LoadBalancer)Exposes pods via a cloud load balancerSimple external access
IngressHTTP/HTTPS routing to services, TLS managementMain entry point
ConfigMapNon-sensitive configuration storageEnv variables, config files
SecretSensitive data storage (base64 encoded)Passwords, tokens, certificates
PersistentVolume (PV)Storage resource in the clusterCloud disks, NFS
PersistentVolumeClaim (PVC)Storage request by a podClaim a PV
NamespaceLogical isolation within a clusterdev/staging/prod separation
HorizontalPodAutoscalerAutomatic scaling based on metricsAutomatic load management
NetworkPolicyFirewall rules between podsIntra-cluster network security

OCI Specs — Summary

OCI SpecProblem solvedImplementation example
Image SpecStandard format for container imagesDocker images, Podman images
Distribution SpecStandard API for push/pull of imagesDocker Hub, AWS ECR, GitHub Container Registry
Runtime SpecStandard execution of containersrunc, crun, kata-containers

Guarantee: An image built with Docker (Image Spec) → pushed to Docker Hub (Distribution Spec) → run by Kubernetes via containerd/runc (Runtime Spec) = it works, always.


9. Key Concepts to Remember

Fundamental Principles

1. Image Immutability

A container image is immutable. You never modify an image while it’s running. To update, build a new image with a new tag and redeploy.

2. Declarative vs Imperative

Kubernetes works in declarative mode: you declare the desired state (desired state) in YAML files, and Kubernetes continuously ensures the cluster matches that state.

3. Everything in Git

GitOps principle: everything — application code AND infrastructure configuration — belongs in Git. Git is the single source of truth. Absolute exception: secrets do not go in Git (use HashiCorp Vault, AWS Secrets Manager, Kubernetes Secrets with encryption at rest).

4. Microservices and single responsibility

Each microservice does one thing and exposes it via a REST API. Each microservice = its own container image = its own independent deployment cycle.

5. Self-healing by design

Never rely on a container or node being always available. Design for failure. Kubernetes automatically detects and fixes failures.

Essential Terminology

TermQuick definition
ContainerRunning instance of an image, in an isolated environment
Container ImageImmutable blueprint of a container (application + dependencies)
RegistryContainer image repository (Docker Hub, ECR, ACR, GCR)
ContainerizationAction of packaging an app into a container image
OrchestrationAutomated management of containers at scale
Desired StateWhat you declare wanting in your K8s YAML files
Current StateThe actual state of the cluster at a given moment
Self-healingKubernetes automatically brings current state = desired state
Rolling UpdateProgressive update without service interruption
GitOpsPattern where Git is the source of truth for deployments
Pull-basedCluster pulls its configuration from Git (vs push)
NodeMachine (physical or VM) in the Kubernetes cluster
PodSmallest deployable unit in Kubernetes (1+ containers)
ReplicaIdentical copy of a pod for high availability and scaling
NamespaceLogical isolation in a K8s cluster

Anti-Patterns to Avoid

Anti-patternWhat to do instead
Modifying a running container (docker exec → edit)Build a new image, redeploy
Storing secrets in images or YAML filesUse Kubernetes Secrets + encryption, or Vault
Single replica in productionMinimum 2-3 replicas with PodDisruptionBudget
No resource limits/requestsAlways define resources.requests and resources.limits
Running as root in the containerUSER nonrootuser in the Dockerfile
Direct kubectl exec access in productionUse GitOps + observability (logs, metrics)
Images with latest tag in productionAlways use precise tags (e.g.: v1.2.3 or SHA)

Search Terms

docker · kubernetes · dynamic · duo · containerization · containers · architecture · deployment · gitops · model · oci · workflow · container · isolation · kernels · reference · shared · yaml · cloud · cncf · concepts · essential · history · microservices

Interested in this course?

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