Interview Prep Hub

Docker, Kubernetes & CI/CD

Modern backend engineering requires deep knowledge of containerization and orchestration. Interviewers look for best practices, security, and architectural understanding.

How Docker Actually Works

Containers app + libs~50 MB app + libs~50 MB app + libs~50 MB container runtimeHOST KERNEL — sharedhardware starts in ms · MBs · namespaces + cgroups Virtual machines appGUEST OS~1 GB appGUEST OS~1 GB appGUEST OS~1 GB hypervisorhost OShardware starts in seconds · GBs · full hardware isolation Sharing the kernel is the trade: cheaper and faster, but a weaker isolation boundary than a VM.
Containers share the host kernel and isolate with namespaces and cgroups. A VM ships an entire guest OS, which is where the size and boot time go.

Docker is not a Virtual Machine. A VM virtualizes the hardware and runs a full Guest OS. Docker virtualizes the OS kernel. Containers share the host kernel.

  • Namespaces: Provide isolation. Ensure a container only sees its own processes, network interfaces, and file system. (What it can see).
  • Cgroups (Control Groups): Provide resource limitation. Limit the CPU and Memory a container can use. (What it can use).
  • UnionFS (Union File System): Creates the layered, read-only file system. When a container runs, a thin read-write layer is added on top.

Dockerfile Best Practices

COPY . . before install FROM node:20 CACHED COPY . . INVALIDATED RUN npm install REBUILT — 90s RUN npm run build REBUILT one character changed → dependencies reinstalled manifest first FROM node:20 CACHED COPY package*.json CACHED RUN npm ci CACHED — 0s COPY . . rebuilt only dependencies reinstall only when the manifest changes Order layers from least to most frequently changing. A layer invalidates every layer below it. A deleted file still lives in the layer that added it — so a secret is not removed by a later RUN rm. Multi-stage builds fix image size: build in a fat stage, COPY --from into a slim runtime.
Each instruction is a cached layer. Copying source before installing dependencies invalidates the install on every code change.

A poorly written Dockerfile creates massive, slow, and insecure images.

# 1. Use an official, lightweight base image
FROM node:20.11-alpine AS builder
WORKDIR /app

# 2. Leverage Docker Cache: Copy package.json FIRST
# If source code changes but dependencies don't, this layer is cached
COPY package*.json ./
RUN npm ci

# 3. Copy the rest of the code
COPY . .
RUN npm run build

# 4. Multi-Stage Build: Create a fresh, tiny production image
FROM node:20.11-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules

# 5. Security: Never run as root
USER node

# 6. Expose ports and define startup command
EXPOSE 3000
CMD ["node", "dist/main.js"]

Key Directives:

  • ENTRYPOINT vs CMD: ENTRYPOINT ["node"] sets the main executable. CMD ["app.js"] provides default arguments. You can override CMD easily at runtime, but not ENTRYPOINT.
  • ADD vs COPY: Always use COPY. ADD has magic features (extracting tars, downloading URLs) that make builds unpredictable.

Kubernetes (K8s) Architecture

Docker runs containers; Kubernetes orchestrates them across a cluster of machines.

Core Components

  • Pod: The smallest deployable unit. Usually contains one container. Pods are ephemeral (they die and are replaced, getting new IPs).
  • Deployment: Manages Pods declaratively. You state "I want 3 replicas of this image", and the Deployment ensures exactly 3 are running. Handles Rolling Updates and Rollbacks.
  • Service: Provides a stable IP address and DNS name to a group of Pods. Load balances traffic across them. (Since Pod IPs constantly change, you talk to the Service).
  • Ingress: Exposes HTTP/HTTPS routes from outside the cluster to Services within the cluster (like an API Gateway/Nginx router).
  • ConfigMap & Secret: Injects configuration and passwords into Pods as environment variables or files, decoupling config from images.
  • StatefulSet: Like a Deployment, but for stateful apps (databases). Guarantees stable network IDs and persistent storage across restarts.

The Control Plane (Master Node)

  • API Server: The front-end of the control plane. All kubectl commands talk to this.
  • etcd: Highly available key-value store holding the cluster's state.
  • Scheduler: Watches for new Pods and assigns them to worker nodes based on resource limits.
  • Controller Manager: Runs controller loops (e.g., ensuring Deployments have the correct number of replicas).

CI/CD Pipelines

Continuous Integration / Continuous Deployment is about automating the path from git push to production.

Typical Pipeline Stages:

  1. Lint & Format: Fails fast if syntax is wrong (ESLint, Prettier, gofmt).
  2. Unit Testing: Runs isolated tests.
  3. Security Scanning: SAST (Static Application Security Testing) and dependency vulnerability scans (Dependabot/Snyk).
  4. Build: Compiles code, builds the Docker image.
  5. Push: Pushes the image to a Container Registry (DockerHub, AWS ECR, GCR) tagged with the git commit hash.
  6. Deploy to Staging: Updates the Kubernetes Deployment in a staging environment.
  7. Integration / E2E Tests: Tests the running application (Cypress, Selenium).
  8. Deploy to Prod: Often gated by manual approval or progressive rollout.

Deployment Strategies

  • Rolling Deployment: (K8s Default). Gradually replaces old instances with new ones. Zero downtime, but you have both versions running simultaneously for a brief period (backward compatibility required!).
  • Blue/Green: Spin up a completely new environment (Green) alongside the old one (Blue). Test Green. Flip the load balancer to route 100% traffic to Green. Immediate rollback if needed. Requires 2x infrastructure cost.
  • Canary: Route 5% of traffic to the new version. Monitor error rates. If stable, increase to 10%, 50%, 100%. Safest, but complex to orchestrate.

Docker Compose

Docker Compose defines multi-container applications in a single YAML file. Essential for local development environments.

# docker-compose.yml
version: '3.8'

services:
  api:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - "3000:3000"
    environment:
      - DATABASE_URL=postgres://user:pass@db:5432/myapp
      - REDIS_URL=redis://cache:6379
    depends_on:
      db:
        condition: service_healthy
      cache:
        condition: service_started
    volumes:
      - ./src:/app/src  # Hot reload in development
    networks:
      - backend

  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: pass
      POSTGRES_DB: myapp
    volumes:
      - pg_data:/var/lib/postgresql/data  # Persist data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U user"]
      interval: 5s
      timeout: 5s
      retries: 5
    networks:
      - backend

  cache:
    image: redis:7-alpine
    command: redis-server --maxmemory 256mb --maxmemory-policy allkeys-lru
    networks:
      - backend

volumes:
  pg_data:

networks:
  backend:
    driver: bridge

Key Commands

  • docker compose up -d — Start all services in background
  • docker compose down -v — Stop and remove volumes
  • docker compose logs -f api — Follow logs for a specific service
  • docker compose exec api sh — Shell into a running container
  • docker compose build --no-cache — Rebuild without cache

Docker Networking

Network TypeUse CaseNotes
bridge (default)Containers on same host talk to each otherContainers resolved by name in custom bridge networks
hostContainer shares host's network stackNo port mapping needed. Best performance. No isolation.
noneComplete network isolationContainer has no network access at all
overlayMulti-host networking (Docker Swarm/K8s)Containers across different machines communicate
# Port mapping: HOST:CONTAINER
docker run -p 8080:3000 myapp    # Host 8080 → Container 3000
docker run -p 127.0.0.1:8080:3000 myapp  # Only localhost

# DNS resolution in compose
# Services can reach each other by service name:
# api can connect to "db:5432" and "cache:6379"

Container Security Best Practices

  • Never run as root: Use USER node or USER 1001 in Dockerfile. Root in container ≈ root on host (with misconfigured runtimes).
  • Use minimal base images: alpine (~5MB) or distroless (no shell at all). Fewer packages = fewer CVEs.
  • Scan images: Use docker scout, Trivy, or Snyk to scan for known vulnerabilities before deploying.
  • Don't store secrets in images: Use build-time secrets (--mount=type=secret) or runtime secrets (K8s Secrets, Vault). Never ENV SECRET_KEY=... in Dockerfile.
  • Read-only filesystem: Run containers with --read-only flag and mount only necessary writable paths.
  • Pin image versions: Use node:20.11-alpine not node:latest. Reproducible builds.
  • Use .dockerignore: Exclude node_modules, .git, .env files from the build context.

Storage & Volumes

# Named volumes (managed by Docker — best for databases)
docker volume create pg_data
docker run -v pg_data:/var/lib/postgresql/data postgres

# Bind mounts (map host directory — best for development)
docker run -v $(pwd)/src:/app/src myapp

# tmpfs mounts (in-memory only — for sensitive temp data)
docker run --tmpfs /tmp myapp

# Volume gotchas:
# - Named volumes persist across container restarts
# - Bind mounts follow host file permissions
# - Don't store important data in the container's writable layer

Helm Charts (K8s Package Manager)

Helm packages Kubernetes manifests into reusable, version-controlled charts. Like npm for K8s.

# Install a chart
helm install my-release bitnami/postgresql

# Upgrade with new values
helm upgrade my-release bitnami/postgresql --set primary.persistence.size=50Gi

# Chart structure
my-chart/
  Chart.yaml          # Metadata (name, version, dependencies)
  values.yaml         # Default configuration values
  templates/          # K8s manifest templates with Go templating
    deployment.yaml
    service.yaml
    ingress.yaml
  charts/             # Sub-chart dependencies

Common Troubleshooting

# Debug a failing container
docker logs <container_id>               # Check logs
docker exec -it <container_id> sh        # Shell into running container
docker inspect <container_id>            # Full metadata (env vars, mounts, network)
docker stats                             # Live CPU/Memory usage

# Debug build issues
docker build --no-cache .                # Rebuild without cache
docker build --progress=plain .          # See full build output

# Clean up disk space
docker system prune -a                   # Remove all unused images/containers
docker volume prune                      # Remove unused volumes
docker system df                         # Show disk usage

Interview Quick Reference

TopicKey Points to Mention
Docker vs VMContainers share host kernel (namespaces + cgroups). VMs virtualize hardware. Containers: faster, lighter, less isolation.
DockerfileMulti-stage builds, layer caching (COPY package.json first), alpine base, USER non-root, ENTRYPOINT vs CMD.
NetworkingBridge (default, DNS by name), host (no isolation), overlay (multi-host). Port mapping HOST:CONTAINER.
K8s ArchitecturePod (smallest unit), Deployment (manages replicas), Service (stable DNS/IP), Ingress (HTTP routing).
K8s Control PlaneAPI Server, etcd (state store), Scheduler, Controller Manager.
Deployment StrategiesRolling (default, zero downtime), Blue/Green (instant rollback, 2x cost), Canary (gradual, safest).
SecurityNon-root user, minimal base images, scan for CVEs, don't embed secrets, .dockerignore, read-only FS.