Docker Compose Core Concepts and Fundamentals
This section explores the fundamental concepts that make Docker Compose a powerful orchestration tool, covering advanced service configuration, networking patterns, and volume management strategies.
Service Configuration Deep Dive
Build Context and Arguments
version: '3.8'
services:
web:
build:
context: ./web
dockerfile: Dockerfile.prod
args:
- NODE_ENV=production
- API_VERSION=v2
target: production
cache_from:
- node:16-alpine
- myapp:latest
image: myapp:${TAG:-latest}
Multi-stage Dockerfile with build args:
# Dockerfile.prod
ARG NODE_ENV=development
ARG API_VERSION=v1
FROM node:16-alpine AS base
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
FROM base AS development
RUN npm ci
COPY . .
CMD ["npm", "run", "dev"]
FROM base AS production
COPY . .
RUN npm run build
EXPOSE 3000
CMD ["npm", "start"]
Resource Constraints
version: '3.8'
services:
web:
image: nginx
deploy:
resources:
limits:
cpus: '0.50'
memory: 512M
reservations:
cpus: '0.25'
memory: 256M
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
window: 120s
worker:
image: myapp:worker
deploy:
replicas: 3
update_config:
parallelism: 1
delay: 10s
failure_action: rollback
rollback_config:
parallelism: 1
delay: 5s
Environment Variable Patterns
version: '3.8'
services:
app:
image: myapp
environment:
# Direct assignment
- NODE_ENV=production
- PORT=3000
# From host environment
- SECRET_KEY=${SECRET_KEY}
- DATABASE_URL=${DATABASE_URL:-postgresql://localhost:5432/myapp}
# Computed values
- APP_URL=https://${DOMAIN:-localhost}:${PORT:-3000}
env_file:
- .env
- .env.local
- .env.${NODE_ENV:-development}
.env file structure:
# .env
NODE_ENV=development
DEBUG=1
LOG_LEVEL=info
# Database
DATABASE_HOST=db
DATABASE_PORT=5432
DATABASE_NAME=myapp
DATABASE_USER=user
DATABASE_PASSWORD=password
# Redis
REDIS_HOST=redis
REDIS_PORT=6379
REDIS_DB=0
# External Services
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=[email protected]
SMTP_PASSWORD=app-password
Advanced Networking
Custom Network Configuration
version: '3.8'
services:
web:
image: nginx
networks:
frontend:
aliases:
- web-server
- nginx-proxy
backend:
ipv4_address: 172.20.0.10
api:
image: node:16
networks:
- backend
- database
db:
image: postgres:13
networks:
database:
aliases:
- postgres-server
networks:
frontend:
driver: bridge
ipam:
config:
- subnet: 172.19.0.0/16
gateway: 172.19.0.1
backend:
driver: bridge
ipam:
config:
- subnet: 172.20.0.0/16
database:
driver: bridge
internal: true # No external access
ipam:
config:
- subnet: 172.21.0.0/16
Network Isolation Patterns
version: '3.8'
services:
# Public-facing services
nginx:
image: nginx
ports:
- "80:80"
- "443:443"
networks:
- frontend
# Application services
web:
build: ./web
networks:
- frontend
- backend
depends_on:
- api
api:
build: ./api
networks:
- backend
- database
depends_on:
- db
- redis
# Data services (isolated)
db:
image: postgres:13
networks:
- database
volumes:
- postgres_data:/var/lib/postgresql/data
redis:
image: redis:7-alpine
networks:
- database
volumes:
- redis_data:/data
networks:
frontend:
driver: bridge
backend:
driver: bridge
database:
driver: bridge
internal: true
volumes:
postgres_data:
redis_data:
Service Discovery and Load Balancing
version: '3.8'
services:
nginx:
image: nginx
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
depends_on:
- web
networks:
- frontend
web:
build: ./web
deploy:
replicas: 3
networks:
- frontend
- backend
environment:
- API_URL=http://api:8000
api:
build: ./api
deploy:
replicas: 2
networks:
- backend
environment:
- DATABASE_URL=postgresql://user:pass@db:5432/myapp
networks:
frontend:
backend:
nginx.conf for load balancing:
events {
worker_connections 1024;
}
http {
upstream web_servers {
server web:3000;
# Docker Compose automatically load balances across replicas
}
server {
listen 80;
location / {
proxy_pass http://web_servers;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
}
Volume Management Strategies
Volume Types and Use Cases
version: '3.8'
services:
web:
image: nginx
volumes:
# Named volume for persistent data
- web_data:/var/www/html
# Bind mount for development
- ./src:/var/www/html:ro
# Tmpfs for temporary files
- type: tmpfs
target: /tmp
tmpfs:
size: 100M
# Volume with specific options
- type: volume
source: web_logs
target: /var/log/nginx
volume:
nocopy: true
db:
image: postgres:13
volumes:
# Database data persistence
- postgres_data:/var/lib/postgresql/data
# Configuration files
- ./postgres/postgresql.conf:/etc/postgresql/postgresql.conf:ro
# Initialization scripts
- ./postgres/init:/docker-entrypoint-initdb.d:ro
# Backup location
- backup_data:/backup
volumes:
web_data:
driver: local
web_logs:
driver: local
driver_opts:
type: none
o: bind
device: /host/logs/web
postgres_data:
driver: local
driver_opts:
type: none
o: bind
device: /data/postgres
backup_data:
external: true
Volume Backup and Restore
version: '3.8'
services:
app:
image: myapp
volumes:
- app_data:/data
backup:
image: alpine
volumes:
- app_data:/source:ro
- backup_storage:/backup
command: |
sh -c "
tar czf /backup/app_data_$$(date +%Y%m%d_%H%M%S).tar.gz -C /source .
find /backup -name 'app_data_*.tar.gz' -mtime +7 -delete
"
profiles:
- backup
restore:
image: alpine
volumes:
- app_data:/target
- backup_storage:/backup
command: |
sh -c "
if [ -f /backup/restore.tar.gz ]; then
cd /target && tar xzf /backup/restore.tar.gz
else
echo 'No restore file found'
fi
"
profiles:
- restore
volumes:
app_data:
backup_storage:
Run backup/restore:
# Create backup
docker-compose --profile backup run --rm backup
# Restore from backup
cp backup_file.tar.gz backup_storage/restore.tar.gz
docker-compose --profile restore run --rm restore
Configuration Management
Secrets Management
version: '3.8'
services:
web:
image: myapp
secrets:
- db_password
- api_key
environment:
- DB_PASSWORD_FILE=/run/secrets/db_password
- API_KEY_FILE=/run/secrets/api_key
db:
image: postgres:13
secrets:
- db_password
environment:
- POSTGRES_PASSWORD_FILE=/run/secrets/db_password
secrets:
db_password:
file: ./secrets/db_password.txt
api_key:
external: true
Configuration Files
version: '3.8'
services:
nginx:
image: nginx
configs:
- source: nginx_config
target: /etc/nginx/nginx.conf
mode: 0644
- source: ssl_cert
target: /etc/ssl/certs/server.crt
mode: 0644
app:
image: myapp
configs:
- source: app_config
target: /app/config.json
configs:
nginx_config:
file: ./config/nginx.conf
ssl_cert:
file: ./certs/server.crt
app_config:
external: true
Template-based Configuration
docker-compose.template.yml:
version: '3.8'
services:
web:
image: ${WEB_IMAGE}:${WEB_TAG}
ports:
- "${WEB_PORT}:3000"
environment:
- NODE_ENV=${NODE_ENV}
- API_URL=${API_URL}
db:
image: postgres:${POSTGRES_VERSION}
environment:
- POSTGRES_DB=${DB_NAME}
- POSTGRES_USER=${DB_USER}
- POSTGRES_PASSWORD=${DB_PASSWORD}
Generate configuration script:
#!/bin/bash
# generate-compose.sh
export WEB_IMAGE="myapp"
export WEB_TAG="${1:-latest}"
export WEB_PORT="${2:-3000}"
export NODE_ENV="${3:-production}"
export API_URL="https://api.${DOMAIN}"
export POSTGRES_VERSION="13"
export DB_NAME="myapp"
export DB_USER="user"
export DB_PASSWORD="$(openssl rand -base64 32)"
envsubst < docker-compose.template.yml > docker-compose.yml
Service Dependencies and Health Checks
Advanced Dependency Management
version: '3.8'
services:
web:
build: ./web
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
migration:
condition: service_completed_successfully
migration:
build: ./web
command: python manage.py migrate
depends_on:
db:
condition: service_healthy
restart: "no"
db:
image: postgres:13
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 10s
timeout: 5s
retries: 5
start_period: 30s
redis:
image: redis:7-alpine
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 3s
retries: 3
Custom Health Check Scripts
health-check.sh:
#!/bin/bash
# Custom health check for web application
# Check if application is responding
if curl -f http://localhost:3000/health > /dev/null 2>&1; then
echo "Application is healthy"
exit 0
else
echo "Application health check failed"
exit 1
fi
version: '3.8'
services:
web:
build: ./web
healthcheck:
test: ["CMD", "/app/health-check.sh"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
Wait Strategies
wait-for-it.sh integration:
version: '3.8'
services:
web:
build: ./web
command: ["./wait-for-it.sh", "db:5432", "--", "python", "app.py"]
depends_on:
- db
api:
build: ./api
command: |
sh -c "
./wait-for-it.sh db:5432 --timeout=60 --strict -- \
./wait-for-it.sh redis:6379 --timeout=30 --strict -- \
python manage.py migrate && \
python manage.py runserver 0.0.0.0:8000
"
depends_on:
- db
- redis
Scaling and Performance
Horizontal Scaling
version: '3.8'
services:
web:
build: ./web
deploy:
replicas: 3
update_config:
parallelism: 1
delay: 10s
failure_action: rollback
monitor: 60s
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
worker:
build: ./worker
deploy:
replicas: 5
resources:
limits:
cpus: '0.5'
memory: 512M
reservations:
cpus: '0.25'
memory: 256M
Scale services dynamically:
# Scale web service to 5 replicas
docker-compose up --scale web=5
# Scale multiple services
docker-compose up --scale web=3 --scale worker=10
Performance Optimization
version: '3.8'
services:
web:
build: ./web
# Optimize container startup
init: true
# Limit log size
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
# Resource limits
mem_limit: 512m
memswap_limit: 512m
cpu_count: 2
cpu_percent: 50
nginx:
image: nginx:alpine
# Use tmpfs for temporary files
tmpfs:
- /var/cache/nginx:noexec,nosuid,size=100m
- /tmp:noexec,nosuid,size=50m
# Optimize shared memory
shm_size: 128m
Summary
In this section, you’ve mastered:
Advanced Service Configuration
- Build Contexts: Multi-stage builds with arguments and caching
- Resource Management: CPU and memory limits with deployment strategies
- Environment Patterns: Complex variable management and file structures
Networking Mastery
- Custom Networks: IPAM configuration and network isolation
- Service Discovery: Load balancing and inter-service communication
- Security Patterns: Network segmentation and access control
Volume Strategies
- Volume Types: Named volumes, bind mounts, and tmpfs usage
- Data Management: Backup, restore, and migration strategies
- Performance: Optimized volume configurations
Configuration Management
- Secrets: Secure credential handling
- Templates: Dynamic configuration generation
- Health Checks: Advanced dependency and readiness management
Next Steps: In Part 3, we’ll explore practical applications including real-world multi-service architectures, development workflows, and production deployment patterns that demonstrate these concepts in action.