Progress · 0/10 sections
- 01 — L4 vs L7 Load Balancing (Days 56–57)
- 02 — Reverse Proxies & API Gateways (Days 58–59)
- 03 — VPC & Cloud Networking (Days 60–61)
- 04 — Docker Internals (Days 62–63)
- 05 — Kubernetes Core Concepts (Days 64–65)
- 06 — Terraform & IaC (Days 66–67)
- 07 — CI/CD Pipelines (Days 68–69)
- 08 — Deployment Strategies (Days 70–71)
- 09 — Vault & Secret Management (Days 72–73)
- 10 — mTLS & Production Security (Days 74–75)
04 — Docker Internals (Days 62–63)
7 min read · Days 62–63
04 — Docker Internals (Days 62–63)
Core Mental Model: Containers = isolated processes. VMs nahi hain. Isolation kernel features se aati hai — namespaces aur cgroups. Yeh samajhna zaroori hai kyunki production mein container escape, resource leaks, aur image vulnerabilities real problems hain.
Container vs VM
Virtual Machine:
┌─────────────────────────────────┐
│ Guest OS (Ubuntu) │
│ App Libs Binaries │
├─────────────────────────────────┤
│ Hypervisor (VMware/KVM) │
├─────────────────────────────────┤
│ Host OS │
│ Hardware │
└─────────────────────────────────┘
Startup: minutes (boot full OS)
Memory: GBs (full OS)
Isolation: very strong (separate kernel)
Container:
┌──────────┐ ┌──────────┐ ┌──────────┐
│ App A │ │ App B │ │ App C │
│ Libs │ │ Libs │ │ Libs │
├──────────┴──┴──────────┴──┴──────────┤
│ Container Runtime │
│ (Docker/containerd) │
├───────────────────────────────────────┤
│ Host OS Kernel │
│ Hardware │
└───────────────────────────────────────┘
Startup: milliseconds (process start only)
Memory: MBs (shared kernel)
Isolation: good but shared kernel (same OS)
Interview question: "Container isolated kyun nahi hai VM jaisa?"
Answer: "Same kernel share hota hai. Kernel vulnerability = all containers affected."
Linux Namespaces — Process Isolation
Namespaces = container ke andar process ko "alag duniya" dikhate hain.
PID Namespace:
Container inside: PID 1 = your app (naya hierarchy)
Host outside: PID 1 = systemd, your app = PID 8457
App sochta hai "main PID 1 hoon" — baaki kuch nahi dikhta
Network Namespace:
Container: eth0 → 172.17.0.2 (virtual interface)
Host: eth0 → 10.0.1.5 (actual NIC)
Container ko apna virtual network stack milta hai
Mount Namespace:
Container: / → overlay filesystem (container's view)
Host: / → actual host filesystem
Container host filesystem nahi dekh sakta
UTS Namespace:
Container ko apna hostname set karne deta hai
Container: hostname = "user-service-abc123"
Host: hostname = "ip-10-0-1-5.ap-south-1.compute.internal"
IPC Namespace:
Inter-process communication isolation
Container ke processes dusre container se IPC nahi kar sakte
cgroups — Resource Limits
cgroups (control groups) = Linux kernel feature
Limits aur accounts karta hai resource usage per process group.
Container ke cgroups:
CPU: "Yeh container max 1 CPU core use kar sakta hai"
Memory: "Yeh container max 512MB use kar sakta hai. OOM kill otherwise."
I/O: "Disk read/write bandwidth limit"
Network: "Network bandwidth limit (tc qdisc via tools)"
Kubernetes mein:
resources:
requests:
cpu: "250m" # scheduler ke liye: "mujhe 0.25 CPU chahiye"
memory: "256Mi"
limits:
cpu: "1000m" # cgroup CPU quota set hoti hai → throttle
memory: "512Mi" # cgroup memory limit → OOM kill if exceeded
OOM Kill:
Container 512Mi se zyada allocate karne ki koshish karta hai
Kernel: OOM killer triggers
Container process killed (pod restart in K8s)
Metric: watch OOMKilled events in production!
kubectl get events --field-selector reason=OOMKilling
Docker Image Layers
Dockerfile = recipe for image
Each instruction = one layer (read-only)
Container = layers stack + writable layer on top
Dockerfile:
FROM golang:1.22 ← Layer 1: base Go image (~700MB)
RUN apt-get install git ← Layer 2: git installed (~10MB)
COPY go.mod go.sum ./ ← Layer 3: dependency files
RUN go mod download ← Layer 4: dependencies downloaded (~100MB)
COPY . . ← Layer 5: source code
RUN go build -o app ./cmd ← Layer 6: compiled binary
Container layer stack:
[golang:1.22 base] Read-only
[git layer] Read-only
[go.mod layer] Read-only
[dependencies layer] Read-only
[source code layer] Read-only
[binary layer] Read-only
[container layer] WRITABLE (runtime changes here)
Why layers matter:
Layer shared ho sakte hain between images (same base image = same layer)
Layer cache kiya jaata hai (rebuild mein reuse)
Pull sirf missing layers hoti hai
Layer Caching — Optimize Build Time
# ❌ WRONG — every code change = full rebuild (dependencies re-download)
FROM golang:1.22-alpine
WORKDIR /app
COPY . . # source code pehle copy
RUN go mod download # dependencies baad mein
RUN go build -o app ./cmd/server
# Every time koi bhi file change = cache miss = go mod download again
# 50 engineers × 10 builds/day × 2 min download = 1000 min/day wasted# ✅ CORRECT — dependencies separately cache karo
FROM golang:1.22-alpine AS builder
WORKDIR /app
# Step 1: ONLY dependency files copy karo (rarely change)
COPY go.mod go.sum ./
RUN go mod download && go mod verify # cached jab tak go.mod nahi badalti
# Step 2: Source code copy karo (often changes)
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -o /app/server ./cmd/server
# If only source code changes: Layer 1 (go mod) cached ✅, only steps after COPY . . rebuildMulti-Stage Builds
Problem: Go binary compile karne ke liye Go SDK chahiye (~700MB).
Compiled binary run karne ke liye Go SDK NOT chahiye.
Production image = 700MB? Unacceptable.
Solution: Multi-stage build.
# Stage 1: Build (heavy image with all tools)
FROM golang:1.22-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
# Build static binary (no dynamic linking)
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
go build -ldflags="-w -s" -o /app/server ./cmd/server
# -w: strip debug info
# -s: strip symbol table
# Result: ~10MB binary instead of ~15MB
# Stage 2: Runtime (minimal image)
FROM gcr.io/distroless/static-debian11
# Copy ONLY the binary from builder stage
COPY --from=builder /app/server /server
# Non-root user (distroless has nonroot user built-in)
USER nonroot:nonroot
EXPOSE 8080
ENTRYPOINT ["/server"]
# Final image size comparison:
# golang:1.22 based: ~700MB
# alpine based: ~15MB
# distroless: ~5MB ← production winnerDistroless Images
Normal alpine image:
✅ Small (~5MB base)
✅ sh shell available (debugging)
❌ Shell = attack surface (container escape easier)
❌ Package manager = can install tools in compromised container
❌ More CVEs (packages have vulnerabilities)
Distroless (Google):
✅ EXTREMELY small (~2MB base)
✅ No shell (container escape harder)
✅ No package manager
✅ Minimal CVEs
❌ No debugging tools (exec into container → no shell!)
Debugging distroless in production:
kubectl debug -it <pod> --image=ubuntu:22.04 --target=<container>
# Adds ephemeral debug container with tools, shares process namespace
Available distroless images:
gcr.io/distroless/static:nonroot ← Go static binaries (best)
gcr.io/distroless/base:nonroot ← Go with some C runtime
gcr.io/distroless/java17:nonroot ← Java apps
Always use :nonroot tag → runs as non-root user by default.
Docker Networking Modes
Bridge (default):
Container gets virtual eth0 on docker0 bridge
NAT for outbound: container IP → host IP
Port mapping: -p 8080:8080 (host:container)
Containers in same network can talk to each other
Host:
Container shares host network stack directly
No virtual eth0, no NAT
Performance: best (no virtual network overhead)
Security: worst (no network isolation)
Use: network-intensive tools, not applications
Overlay (multi-host):
VXLAN-based overlay spans multiple hosts
Containers on different machines talk to each other
Used by: Docker Swarm, Kubernetes (CNI plugins)
Kubernetes CNI: Flannel, Calico, Cilium — all implement overlay networking
None:
No network at all
Use: batch jobs that don't need network, security-sensitive processes
Production Dockerfile — Complete Example
# syntax=docker/dockerfile:1.6
FROM golang:1.22-alpine AS builder
# Install CA certificates (for HTTPS calls to external APIs)
RUN apk add --no-cache ca-certificates tzdata
WORKDIR /app
# Layer cache: dependencies
COPY go.mod go.sum ./
RUN go mod download && go mod verify
# Build
COPY . .
ARG VERSION=dev
ARG COMMIT=unknown
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
go build \
-ldflags="-w -s -X main.version=${VERSION} -X main.commit=${COMMIT}" \
-o /app/server \
./cmd/server
# Security scan on dependencies
RUN go install golang.org/x/vuln/cmd/govulncheck@latest && \
govulncheck ./...
# --- Runtime image ---
FROM gcr.io/distroless/static-debian11:nonroot
# Copy CA certificates and timezone data
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=builder /usr/share/zoneinfo /usr/share/zoneinfo
# Copy binary
COPY --from=builder /app/server /server
USER nonroot:nonroot
EXPOSE 8080
# Health check (K8s probes override this, but useful for docker run)
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
CMD ["/server", "-health"]
ENTRYPOINT ["/server"]# Build with version info
docker build \
--build-arg VERSION=$(git describe --tags --always) \
--build-arg COMMIT=$(git rev-parse --short HEAD) \
-t myapp:$(git rev-parse --short HEAD) .
# Image size check
docker images myapp
# REPOSITORY TAG SIZE
# myapp abc1234 8.2MB ← distroless Go binary
# Security scan
docker scout cves myapp:abc1234
trivy image myapp:abc1234