Skip to content
DevOps Architect

    Syllabus / Foundations / 04

    STARTUP TO ENTERPRISE
    04 / 12 — RUNTIME FOUNDATION

    🐳Docker & Containers

    Master container fundamentals that power modern platforms. Build lean, secure images. Orchestrate multi-service apps. Enforce security at every layer from development to production.

    Multi-stage Compose 3-tier Rootless + Distroless + Cosign Killercoda

    Architecture & How It Works 04.1

    Docker sits on top of containerd and runc. Containers isolate processes using Linux namespaces and control resources via cgroups v2. Images are built from read-only layers using OverlayFS union mounts. The Docker daemon manages the lifecycle on the host.

    graph TD subgraph "User Space" App[Your App Process] DockerCLI[docker CLI] end subgraph "Docker Engine" Daemon[dockerd] containerd[containerd] runc[runc] end subgraph "Linux Kernel" NS[Namespaces
    pid,net,mnt,uts,ipc,user] CG[cgroups v2] OV[OverlayFS layers] end App --> Daemon DockerCLI --> Daemon Daemon --> containerd --> runc runc --> NS runc --> CG runc --> OV

    Image Layers & Caching

    Each instruction in a Dockerfile creates a layer. Docker reuses layers when possible. Order instructions from least-changing (base image, dependencies) to most-changing (source code) for best cache performance.

    Networking Models

    bridge (default isolated), host (shares host network), overlay (Swarm / Kubernetes), macvlan (direct L2). Always create custom bridge networks for multi-container apps instead of relying on default bridge.

    Core Components & Concepts 04.2

    ConceptKey PrimitivesProduction Notes
    DockerfileFROM, RUN, COPY --chown, CMD, ENTRYPOINT, USER, HEALTHCHECKMulti-stage + non-root + read-only rootfs
    Composeservices, volumes, networks, profiles, depends_onUse compose v2, healthchecks, restart policies
    VolumesNamed volumes vs bind mountsPrefer named volumes in prod; backup with docker cp
    Networkingdocker network create mynetUse DNS names between services inside custom networks
    SecurityTrivy, docker scout, cosign, --cap-drop, read-onlyScan in pipeline; sign images; run rootless
    Pro Tip
    Use docker buildx with --cache-from and --cache-to in CI for massive speed gains on subsequent builds.

    WSL Hands-On Lab 04.3

    Full End-to-End: Install → Build → Optimize → Scan → Compose → Rootless

    wsl — docker install
    $ curl -fsSL https://get.docker.com | sh
    $ sudo usermod -aG docker $USER && newgrp docker
    $ docker run hello-world

    Optimized Multi-Stage Dockerfile (Node example)

    Dockerfile
    # syntax=docker/dockerfile:1
    FROM node:20-alpine AS deps
    WORKDIR /app
    COPY package*.json ./
    RUN npm ci --only=production && npm cache clean --force
    
    FROM node:20-alpine AS runner
    RUN apk add --no-cache dumb-init
    ENV NODE_ENV=production
    USER node
    WORKDIR /app
    COPY --from=deps --chown=node:node /app/node_modules ./node_modules
    COPY --chown=node:node . .
    EXPOSE 3000
    HEALTHCHECK --interval=30s --timeout=3s CMD node -e "require('http').get('http://localhost:3000/health', r => process.exit(r.statusCode === 200 ? 0 : 1))"
    ENTRYPOINT ["dumb-init", "--"]
    CMD ["node", "server.js"]
    build + scan + size
    $ docker build -t myapi:prod .
    $ docker images | grep myapi
    $ # Install trivy
    $ curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin
    $ trivy image --severity HIGH,CRITICAL myapi:prod

    docker-compose.yml — 3-tier production-ready

    docker-compose.yml
    version: '3.8'
    services:
      api:
        build: .
        environment:
          DATABASE_URL: postgres://app:secret@db:5432/appdb
        depends_on: { db: { condition: service_healthy } }
        networks: [appnet]
        read_only: true
        security_opt: ["no-new-privileges:true"]
      db:
        image: postgres:16-alpine
        environment:
          POSTGRES_USER: app
          POSTGRES_PASSWORD: secret
          POSTGRES_DB: appdb
        volumes: [pgdata:/var/lib/postgresql/data]
        healthcheck: { test: ["CMD-SHELL", "pg_isready -U app"], interval: 10s }
        networks: [appnet]
      redis:
        image: redis:7-alpine
        networks: [appnet]
      nginx:
        image: nginx:alpine
        ports: ["80:80"]
        volumes: ["./nginx.conf:/etc/nginx/conf.d/default.conf:ro"]
        depends_on: [api]
        networks: [appnet]
    volumes: { pgdata: {} }
    networks: { appnet: { driver: bridge } }
    run it
    $ docker compose up -d --build
    $ docker compose ps
    $ docker compose logs -f api
    $ # Rootless experiment (requires Docker 20.10+)
    $ dockerd-rootless-setuptool.sh install

    Lab Checklist — Track Progress

    • Install Docker Engine in WSL2 and validate with hello-world
    • Write naive Dockerfile and measure final image size
    • Rewrite using multi-stage, alpine, non-root, HEALTHCHECK
    • Scan final image with Trivy, document HIGH/CRITICAL fixes
    • Create full docker-compose.yml for 3-tier app with healthchecks
    • Run compose stack and validate inter-service networking
    • Complete Play with Docker + Killercoda Docker fundamentals labs↗

    Real-World Project — Startup → Enterprise 04.4

    Containerize Express API for a single service

    One Dockerfile, .dockerignore, basic CI build. Push to GHCR. Deploy manually or with simple compose on a VM.

    Multi-service Compose with postgres, redis, nginx reverse proxy

    Environment-specific compose overrides, named volumes, custom bridge networks, healthchecks, rolling updates via compose.

    Rootless containers + read-only filesystems + distroless + OCI compliance

    Run daemon rootless, drop all capabilities except NET_BIND_SERVICE, use chainguard or distroless images, sign every image with cosign, enforce image signing in admission controller.

    Troubleshooting Guide 04.5

    docker logs <id>
    docker inspect <id> | jq '.[0].State'
    docker run --rm -it --entrypoint sh myimage

    Use dive tool. Combine RUN commands. Use alpine or distroless. Remove apt/yum caches in same layer.

    Confirm they are on same custom network. Use service names for DNS. Check exposed vs published ports.

    Match container UID/GID to host folder owner or use init container chown. Prefer named volumes managed by Docker.

    Read logs. Increase restart policy grace. Add liveness/readiness probes. Check resource limits causing OOM.

    30-Day Learning Roadmap 04.6

    WEEK 1
    Dockerfile Mastery
    • Every instruction & cache rules
    • Multi-stage patterns
    • Small base images
    • Non-root users
    WEEK 2
    Compose & Networking
    • Compose file deep dive
    • Custom networks
    • Volumes & secrets
    • Healthchecks & depends
    WEEK 3
    Security & Scanning
    • Trivy + Grype + dockle
    • Rootless + capabilities
    • Distroless images
    • Image signing
    WEEK 4
    Registry & CI Integration
    • GHCR / ECR / Harbor
    • Buildx + cache export
    • Cosign + SBOM
    • Registry policies

    Deep Dive: Image Optimization & Security Patterns 04.7

    Layer Optimization Techniques

    Combine RUN commands. Use .dockerignore aggressively. Copy only what is needed. Use build args for environment-specific layers. Leverage BuildKit cache mounts for npm/yarn/pip.

    # syntax=docker/dockerfile:1
    RUN --mount=type=cache,target=/root/.npm npm ci

    Security Hardening Checklist

    • ✓ Non-root user in final stage
    • ✓ Read-only root filesystem
    • ✓ Drop all capabilities except required
    • ✓ No package managers in prod image
    • ✓ Signed images + SBOM

    Run: curl -fsSL https://get.docker.com | sh

    Extra commands from this lesson (14) are kept out of this page. Quizzes were not in the source HTML.