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
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
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:
ENTRYPOINTvsCMD:ENTRYPOINT ["node"]sets the main executable.CMD ["app.js"]provides default arguments. You can override CMD easily at runtime, but not ENTRYPOINT.ADDvsCOPY: Always useCOPY.ADDhas 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:
- Lint & Format: Fails fast if syntax is wrong (ESLint, Prettier, gofmt).
- Unit Testing: Runs isolated tests.
- Security Scanning: SAST (Static Application Security Testing) and dependency vulnerability scans (Dependabot/Snyk).
- Build: Compiles code, builds the Docker image.
- Push: Pushes the image to a Container Registry (DockerHub, AWS ECR, GCR) tagged with the git commit hash.
- Deploy to Staging: Updates the Kubernetes Deployment in a staging environment.
- Integration / E2E Tests: Tests the running application (Cypress, Selenium).
- 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: bridgeKey Commands
docker compose up -d— Start all services in backgrounddocker compose down -v— Stop and remove volumesdocker compose logs -f api— Follow logs for a specific servicedocker compose exec api sh— Shell into a running containerdocker compose build --no-cache— Rebuild without cache
Docker Networking
| Network Type | Use Case | Notes |
|---|---|---|
| bridge (default) | Containers on same host talk to each other | Containers resolved by name in custom bridge networks |
| host | Container shares host's network stack | No port mapping needed. Best performance. No isolation. |
| none | Complete network isolation | Container has no network access at all |
| overlay | Multi-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 nodeorUSER 1001in Dockerfile. Root in container ≈ root on host (with misconfigured runtimes). - Use minimal base images:
alpine(~5MB) ordistroless(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). NeverENV SECRET_KEY=...in Dockerfile. - Read-only filesystem: Run containers with
--read-onlyflag and mount only necessary writable paths. - Pin image versions: Use
node:20.11-alpinenotnode:latest. Reproducible builds. - Use .dockerignore: Exclude
node_modules,.git,.envfiles 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 layerHelm 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 dependenciesCommon 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 usageInterview Quick Reference
| Topic | Key Points to Mention |
|---|---|
| Docker vs VM | Containers share host kernel (namespaces + cgroups). VMs virtualize hardware. Containers: faster, lighter, less isolation. |
| Dockerfile | Multi-stage builds, layer caching (COPY package.json first), alpine base, USER non-root, ENTRYPOINT vs CMD. |
| Networking | Bridge (default, DNS by name), host (no isolation), overlay (multi-host). Port mapping HOST:CONTAINER. |
| K8s Architecture | Pod (smallest unit), Deployment (manages replicas), Service (stable DNS/IP), Ingress (HTTP routing). |
| K8s Control Plane | API Server, etcd (state store), Scheduler, Controller Manager. |
| Deployment Strategies | Rolling (default, zero downtime), Blue/Green (instant rollback, 2x cost), Canary (gradual, safest). |
| Security | Non-root user, minimal base images, scan for CVEs, don't embed secrets, .dockerignore, read-only FS. |