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"
| Tool | Requires Privileged? | Requires Docker Daemon? | Best For |
|---|---|---|---|
| DinD | Yes | Yes (inside container) | Legacy CI, full Docker feature set |
| DooD | No (but socket = root) | Host Docker | Simple CI, shared cache |
| Kaniko | No | No | K8s-native builds, security-first |
| BuildKit | No (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
| Feature | Compose | Swarm | Kubernetes |
|---|---|---|---|
| Scope | Single host, dev/test | Multi-host cluster | Multi-host production |
| Complexity | Low | Medium | High |
| Scaling | Manual (--scale) | Built-in service scaling | HPA, VPA, Cluster Autoscaler |
| Service discovery | DNS on compose network | Built-in DNS + VIP | CoreDNS + Service resources |
| Load balancing | None (round-robin DNS) | Built-in ingress LB | Ingress controllers, Service mesh |
| Rolling updates | Recreate only | Rolling update built-in | Rolling, blue-green, canary |
| Secrets | .env files, docker secret | Encrypted secrets (Raft) | K8s Secrets + external managers |
| Health checks | HEALTHCHECK in Dockerfile | HEALTHCHECK + restart policy | Liveness, readiness, startup probes |
| Ecosystem | docker-compose.yml | docker stack deploy | Helm, 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 │ │
│ └──────────┘ └──────────────┘ │
└─────────────────────────────────┘
| Mesh | Proxy | Key Strength | Complexity |
|---|---|---|---|
| Istio | Envoy | Feature-rich (traffic mgmt, observability, security) | High |
| Linkerd | linkerd2-proxy (Rust) | Lightweight, simple, fast | Medium |
| Cilium | eBPF (no sidecar!) | Performance, L7 policies without sidecars | Medium |
| Consul Connect | Envoy or built-in | Multi-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)
| Tool | Model | Key Feature |
|---|---|---|
| ArgoCD | Pull-based | UI dashboard, app-of-apps pattern, RBAC |
| Flux | Pull-based | GitOps 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
- “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.
- “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.
- “When Compose vs Swarm vs K8s?” → Compose for dev. K8s for production. Swarm for simple production with small teams. Most skip Swarm.
- “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.
- “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).