Docker & Kubernetes Deep Dive
Docker Compose simplifies multi-container orchestration on a single host, but achieving production-ready deployments requires understanding advanced patterns. This guide explores sophisticated configurations, service orchestration, dependency management, health monitoring, and real-world deployment strategies that transform Docker Compose from a development tool into a powerful production facilitator.
Docker Compose starts services in dependency order, but starting a service doesn't guarantee it's ready to accept connections. Modern applications require intelligent waiting mechanisms to prevent failures when upstream services haven't fully initialized.
Implement application-level health checks that report readiness to dependent services:
version: '3.9'
services:
database:
image: postgres:15
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD: secret
POSTGRES_DB: production
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app"]
interval: 5s
timeout: 5s
retries: 5
start_period: 10s
api:
image: myapp:latest
depends_on:
database:
condition: service_healthy
environment:
DATABASE_URL: postgresql://app:secret@database:5432/production
ports:
- "8080:3000"
cache:
image: redis:7-alpine
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 3s
retries: 5
worker:
image: myapp-worker:latest
depends_on:
database:
condition: service_healthy
cache:
condition: service_healthy
environment:
DATABASE_URL: postgresql://app:secret@database:5432/production
REDIS_URL: redis://cache:6379
The healthcheck directive verifies service readiness before dependent services start. The condition: service_healthy parameter ensures strict ordering based on application state rather than just process startup.
For services without native health check commands, implement custom verification scripts:
version: '3.9'
services:
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:8.0.0
environment:
- discovery.type=single-node
- xpack.security.enabled=false
healthcheck:
test: curl -s http://localhost:9200 >/dev/null || exit 1
interval: 30s
timeout: 10s
retries: 5
start_period: 40s
kibana:
image: docker.elastic.co/kibana/kibana:8.0.0
depends_on:
elasticsearch:
condition: service_healthy
ports:
- "5601:5601"
environment:
ELASTICSEARCH_HOSTS: http://elasticsearch:9200
Overlay custom networks to create logical service tiers with isolated communication patterns:
version: '3.9'
networks:
frontend:
driver: bridge
backend:
driver: bridge
admin:
driver: bridge
services:
nginx:
image: nginx:alpine
networks:
- frontend
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
api:
image: myapp:latest
networks:
- frontend
- backend
environment:
DATABASE_HOST: postgres
REDIS_HOST: redis
depends_on:
- postgres
- redis
postgres:
image: postgres:15
networks:
- backend
- admin
volumes:
- pgdata:/var/lib/postgresql/data
environment:
POSTGRES_PASSWORD: secure_password
redis:
image: redis:7-alpine
networks:
- backend
- admin
command: redis-server --requirepass secure_password
adminer:
image: adminer:latest
networks:
- admin
ports:
- "8081:8080"
environment:
ADMINER_DEFAULT_SERVER: postgres
volumes:
pgdata:
This configuration isolates frontend services (nginx, api) from backend infrastructure (postgres, redis) and admin tools (adminer). Services only communicate across explicitly defined networks, enhancing security and reducing attack surface.
Use multiple compose files to manage environment-specific configurations without modifying the base definition:
docker-compose.yml (base):
version: '3.9'
services:
api:
image: myapp:${VERSION:-latest}
environment:
LOG_LEVEL: ${LOG_LEVEL:-info}
DATABASE_URL: ${DATABASE_URL}
ports:
- "${API_PORT:-8000}:3000"
docker-compose.prod.yml (production override):
version: '3.9'
services:
api:
restart: always
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
reservations:
cpus: '0.25'
memory: 256M
Deploy with: docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d
Handle sensitive data securely using Docker Compose secrets or external secret stores:
version: '3.9'
secrets:
db_password:
file: ./secrets/db_password.txt
api_key:
file: ./secrets/api_key.txt
jwt_secret:
external: true
services:
database:
image: postgres:15
secrets:
- db_password
environment:
POSTGRES_USER: app
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
volumes:
- pgdata:/var/lib/postgresql/data
api:
image: myapp:latest
secrets:
- api_key
- jwt_secret
environment:
API_KEY_FILE: /run/secrets/api_key
JWT_SECRET_FILE: /run/secrets/jwt_secret
depends_on:
database:
condition: service_healthy
volumes:
pgdata:
Secrets are mounted read-only at /run/secrets/<secret_name> inside containers. This prevents accidental exposure in environment variables and logs.
Implement blue-green deployment patterns using reverse proxy routing:
version: '3.9'
services:
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx-upstream.conf:/etc/nginx/conf.d/default.conf:ro
depends_on:
- api-blue
- api-green
api-blue:
image: myapp:1.0.0
environment:
VERSION: blue
PORT: 3000
api-green:
image: myapp:2.0.0
environment:
VERSION: green
PORT: 3001
The nginx configuration routes traffic to the active version while the other version updates, enabling instant fallback if issues arise.
Distribute traffic across multiple instances using docker-compose scale and service discovery:
version: '3.9'
services:
lb:
image: haproxy:2.8
ports:
- "80:80"
- "8404:8404"
volumes:
- ./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro
depends_on:
- api
api:
image: myapp:latest
environment:
PORT: 3000
expose:
- "3000"
postgres:
image: postgres:15
environment:
POSTGRES_PASSWORD: password
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:
Scale with: docker-compose up -d --scale api=3
Aggregate logs from all services using the ELK stack or similar:
version: '3.9'
services:
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:8.0.0
environment:
- discovery.type=single-node
- xpack.security.enabled=false
ports:
- "9200:9200"
volumes:
- esdata:/usr/share/elasticsearch/data
logstash:
image: docker.elastic.co/logstash/logstash:8.0.0
ports:
- "5000:5000/udp"
environment:
- "LS_JAVA_OPTS=-Xmx256m -Xms256m"
volumes:
- ./logstash.conf:/usr/share/logstash/pipeline/logstash.conf:ro
depends_on:
- elasticsearch
kibana:
image: docker.elastic.co/kibana/kibana:8.0.0
ports:
- "5601:5601"
environment:
ELASTICSEARCH_HOSTS: http://elasticsearch:9200
depends_on:
- elasticsearch
api:
image: myapp:latest
ports:
- "8000:3000"
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
labels: "service=api,environment=prod"
postgres:
image: postgres:15
environment:
POSTGRES_PASSWORD: password
logging:
driver: "json-file"
options:
max-size: "5m"
max-file: "2"
volumes:
esdata:
Monitor containerized applications using Prometheus and Grafana integration:
version: '3.9'
services:
prometheus:
image: prom/prometheus:latest
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
- promdata:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
grafana:
image: grafana/grafana:latest
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
volumes:
- grafanadata:/var/lib/grafana
node-exporter:
image: prom/node-exporter:latest
ports:
- "9100:9100"
volumes:
- /proc:/host/proc:ro
- /sys:/host/sys:ro
- /:/rootfs:ro
command:
- '--path.procfs=/host/proc'
- '--path.sysfs=/host/sys'
- '--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)'
volumes:
promdata:
grafanadata:
Ensure your Docker Compose deployment meets production standards:
restart: always for critical services to handle unexpected failuresOptimize image sizes and build times using Dockerfile multi-stage builds referenced in Compose:
version: '3.9'
services:
api:
build:
context: .
dockerfile: Dockerfile
target: production
image: myapp:prod
ports:
- "8000:3000"
dev:
build:
context: .
dockerfile: Dockerfile
target: development
image: myapp:dev
ports:
- "8001:3000"
volumes:
- .:/app
environment:
NODE_ENV: development
Use profiles to enable/disable services for different scenarios:
version: '3.9'
services:
api:
image: myapp:latest
ports:
- "8000:3000"
postgres:
image: postgres:15
profiles:
- local
- testing
environment:
POSTGRES_PASSWORD: password
redis:
image: redis:7-alpine
profiles:
- local
monitoring:
image: prom/prometheus:latest
profiles:
- debug
ports:
- "9090:9090"
Start with specific profiles: docker-compose --profile local up -d