← Docker & Containers Advanced

Advanced Patterns

Advanced Patterns

Architecture patterns that show up in principal-level interviews and production systems. Sidecar, init containers, DinD, Docker Compose vs Swarm vs K8s, service mesh, and GitOps.


Multi-Container Pod Patterns

Sidecar Pattern

A helper container that extends or enhances the main container without modifying it.

┌─────────────────────────────────┐
│ Pod                              │
│  ┌──────────┐  ┌──────────────┐ │
│  │ Main App │  │ Sidecar      │ │
│  │ (nginx)  │←→│ (log shipper)│ │
│  └──────────┘  └──────────────┘ │
│       shared volume / network    │
└─────────────────────────────────┘

Real-world sidecars:

  • Log shipper: Fluent Bit reads app logs from shared volume, ships to Elasticsearch
  • Service mesh proxy: Envoy/Istio intercepts all network traffic for mTLS, observability
  • Config reloader: Watches ConfigMap changes, signals main app to reload
  • Vault agent: Fetches secrets from HashiCorp Vault, writes to shared tmpfs

ELI5: A sidecar is like a motorcycle sidecar — it’s attached to the main vehicle, shares the same road (network), and carries supporting cargo (logs, config, security). The motorcycle works without it, but the sidecar adds capabilities without modifying the motorcycle itself.

K8s native sidecars (1.28+):

initContainers:
- name: log-shipper
  image: fluent/fluent-bit:latest
  restartPolicy: Always    # This makes it a sidecar, not an init container

The restartPolicy: Always on an init container tells K8s to keep it running for the pod’s lifetime. It starts before the main container and stays running alongside it.

Init Container Pattern

Runs to completion before main containers start. Used for setup tasks.

initContainers:
- name: wait-for-db
  image: busybox
  command: ['sh', '-c', 'until nc -z db-service 5432; do sleep 2; done']
- name: run-migrations
  image: myapp:latest
  command: ['python', 'manage.py', 'migrate']
containers:
- name: api
  image: myapp:latest

Use cases:

  • Wait for dependencies (database, message queue) to be ready
  • Run database migrations before app starts
  • Fetch configuration or secrets from external service
  • Set file permissions on shared volumes

Think of it this way: Init containers are the opening act before the main concert. They set up the stage (run migrations), check the sound system (wait for dependencies), and then leave. The main act (your app) only starts when all opening acts finish successfully.

Common mistake: Using init containers for long-running tasks or health checks. Init containers run to completion — if they never exit, the pod never starts. Use sidecars for ongoing tasks.

Ambassador Pattern

A proxy container that handles outbound connections on behalf of the main app.

Main App → Ambassador (local proxy) → External Service
                                      (handles auth, retries, circuit breaking)

The app connects to localhost; the ambassador handles the complexity of talking to external services (TLS, authentication, connection pooling, retries).

Adapter Pattern

Transforms the main app’s output into a standard format.

Main App (custom log format) → Adapter → Standard format (JSON, Prometheus metrics)

Example: Your legacy app writes logs in a custom format. An adapter sidecar reads those logs and converts them to structured JSON that your logging pipeline expects.


Docker-in-Docker (DinD) vs Docker-out-of-Docker (DooD)

DinD: Running Docker Inside Docker

# DinD: privileged container running its own Docker daemon
services:
  dind:
    image: docker:dind
    privileged: true         # Required — and dangerous
    volumes:
      - docker-certs:/certs
  builder:
    image: docker:cli
    environment:
      DOCKER_HOST: tcp://dind:2376
    volumes:
      - docker-certs:/certs:ro

Pros: Fully isolated Docker environment. Clean state every time. No interference with host Docker. Cons: Requires --privileged (massive security risk). Filesystem-in-filesystem performance overhead. Cache doesn’t persist between runs.

DooD: Mount Host Docker Socket

docker run -v /var/run/docker.sock:/var/run/docker.sock docker:cli docker ps

Pros: No privileged mode. Uses host Docker cache. Fast. Cons: Container has full access to host Docker — can create/delete any container, access any volume. Essentially root access to the host.

ELI5: DinD is like building a workshop inside your workshop — full isolation but expensive and you need special permissions. DooD is like giving someone the key to your workshop — they can use all your tools but they can also trash the place.

Modern Alternative: Kaniko, BuildKit

# Kaniko: build images in K8s without Docker daemon or privileged mode
containers:
- name: kaniko
  image: gcr.io/kaniko-project/executor:latest
  args:
  - "--dockerfile=Dockerfile"
  - "--context=git://github.com/myorg/myrepo"
  - "--destination=myregistry/myapp:v1"
ToolRequires Privileged?Requires Docker Daemon?Best For
DinDYesYes (inside container)Legacy CI, full Docker feature set
DooDNo (but socket = root)Host DockerSimple CI, shared cache
KanikoNoNoK8s-native builds, security-first
BuildKitNo (rootless mode)No (standalone mode)Advanced builds, modern CI

Decision framework: Use Kaniko or rootless BuildKit for Kubernetes CI/CD. Use DooD only in trusted CI environments. Avoid DinD unless you specifically need an isolated Docker environment.


Docker Compose vs Swarm vs Kubernetes

FeatureComposeSwarmKubernetes
ScopeSingle host, dev/testMulti-host clusterMulti-host production
ComplexityLowMediumHigh
ScalingManual (--scale)Built-in service scalingHPA, VPA, Cluster Autoscaler
Service discoveryDNS on compose networkBuilt-in DNS + VIPCoreDNS + Service resources
Load balancingNone (round-robin DNS)Built-in ingress LBIngress controllers, Service mesh
Rolling updatesRecreate onlyRolling update built-inRolling, blue-green, canary
Secrets.env files, docker secretEncrypted secrets (Raft)K8s Secrets + external managers
Health checksHEALTHCHECK in DockerfileHEALTHCHECK + restart policyLiveness, readiness, startup probes
Ecosystemdocker-compose.ymldocker stack deployHelm, Kustomize, ArgoCD, massive ecosystem
Production ready?No (single host)Yes (simple workloads)Yes (complex workloads)

Decision framework: Local development / CI → Compose. Simple production with small team, Docker expertise → Swarm. Production at scale, complex requirements, large team → Kubernetes. Most companies go Compose (dev) → Kubernetes (prod) and skip Swarm entirely.


Service Mesh

A dedicated infrastructure layer for service-to-service communication. Handles mTLS, observability, traffic management, and resilience without changing application code.

┌─────────────────────────────────┐
│ Pod                              │
│  ┌──────────┐  ┌──────────────┐ │
│  │ Your App │←→│ Envoy Proxy  │ │  ← Sidecar proxy
│  └──────────┘  └──────┬───────┘ │
└─────────────────────────┼───────┘
                          │ mTLS
┌─────────────────────────┼───────┐
│ Pod                      │       │
│  ┌──────────┐  ┌────────┴─────┐ │
│  │ Other App│←→│ Envoy Proxy  │ │
│  └──────────┘  └──────────────┘ │
└─────────────────────────────────┘
MeshProxyKey StrengthComplexity
IstioEnvoyFeature-rich (traffic mgmt, observability, security)High
Linkerdlinkerd2-proxy (Rust)Lightweight, simple, fastMedium
CiliumeBPF (no sidecar!)Performance, L7 policies without sidecarsMedium
Consul ConnectEnvoy or built-inMulti-platform (K8s + VMs)Medium

When to use a service mesh: You have >10 services, need mTLS between all of them, want traffic splitting for canary deploys, need distributed tracing without code changes. Don’t use if you have <5 services — the overhead isn’t worth it.


GitOps

Infrastructure and application deployment managed through Git as the single source of truth.

Developer pushes → Git repo updated → GitOps agent detects → Applies to cluster
                                       (ArgoCD / Flux)
ToolModelKey Feature
ArgoCDPull-basedUI dashboard, app-of-apps pattern, RBAC
FluxPull-basedGitOps Toolkit, Helm/Kustomize native

Pull vs Push:

  • Push (traditional CI/CD): Jenkins/GitHub Actions pushes changes to cluster. CI needs cluster credentials.
  • Pull (GitOps): Agent inside cluster watches Git repo, pulls changes. No external cluster access needed. More secure.

Why GitOps matters for interviews: It’s the modern standard for K8s deployments. “How do you deploy to production?” → “We merge to main, ArgoCD detects the change and syncs the cluster. Full audit trail in Git. Rollback = git revert.”


Key Takeaways for Interviews

  1. “Explain the sidecar pattern” → Helper container in the same pod. Shares network + volumes with main container. Examples: log shipper, service mesh proxy, vault agent. K8s 1.28+ has native sidecar support.
  2. “DinD vs DooD?” → DinD runs Docker daemon inside container (needs privileged). DooD mounts host socket (simpler but gives host access). Modern: use Kaniko or BuildKit — no daemon, no privileged.
  3. “When Compose vs Swarm vs K8s?” → Compose for dev. K8s for production. Swarm for simple production with small teams. Most skip Swarm.
  4. “When do you need a service mesh?” → >10 services, need mTLS everywhere, traffic splitting, observability without code changes. Istio for features, Linkerd for simplicity, Cilium for performance.
  5. “Explain GitOps” → Git is the source of truth. Agent in cluster pulls desired state from Git and reconciles. ArgoCD or Flux. Audit trail, declarative, secure (no external cluster access).