549 lines
9.4 KiB
Markdown
549 lines
9.4 KiB
Markdown
# Docker & Containerization Rules
|
|
|
|
Essential rules for Docker, Docker Compose, Docker Swarm, and container technologies.
|
|
|
|
## Dockerfile Best Practices
|
|
|
|
### Layer Optimization
|
|
|
|
- Minimize layers by combining commands
|
|
- Order layers from least to most frequently changing
|
|
- Use multi-stage builds to reduce image size
|
|
- Clean up package manager caches
|
|
|
|
```dockerfile
|
|
# ✅ Good: Multi-stage build with layer optimization
|
|
FROM node:20-alpine AS builder
|
|
WORKDIR /app
|
|
COPY package*.json ./
|
|
RUN npm ci --only=production
|
|
|
|
FROM node:20-alpine
|
|
WORKDIR /app
|
|
COPY --from=builder /app/node_modules ./node_modules
|
|
COPY . .
|
|
USER node
|
|
EXPOSE 3000
|
|
CMD ["node", "server.js"]
|
|
|
|
# ❌ Bad: Single stage, many layers
|
|
FROM node:20
|
|
RUN npm install -g nodemon
|
|
WORKDIR /app
|
|
COPY . .
|
|
RUN npm install
|
|
EXPOSE 3000
|
|
CMD ["nodemon", "server.js"]
|
|
```
|
|
|
|
### Security
|
|
|
|
- Run as non-root user
|
|
- Use specific image versions, not `latest`
|
|
- Scan images for vulnerabilities
|
|
- Don't store secrets in images
|
|
|
|
```dockerfile
|
|
# ✅ Good
|
|
FROM node:20-alpine
|
|
RUN addgroup -g 1001 appgroup && \
|
|
adduser -u 1001 -G appgroup -D appuser
|
|
WORKDIR /app
|
|
COPY --chown=appuser:appgroup . .
|
|
USER appuser
|
|
CMD ["node", "server.js"]
|
|
|
|
# ❌ Bad
|
|
FROM node:latest # Unpredictable version
|
|
# Running as root (default)
|
|
COPY . .
|
|
CMD ["node", "server.js"]
|
|
```
|
|
|
|
### Caching Strategy
|
|
|
|
```dockerfile
|
|
# ✅ Good: Dependencies cached separately
|
|
COPY package*.json ./
|
|
RUN npm ci
|
|
COPY . .
|
|
|
|
# ❌ Bad: All code copied before dependencies
|
|
COPY . .
|
|
RUN npm install
|
|
```
|
|
|
|
## Docker Compose
|
|
|
|
### Service Structure
|
|
|
|
- Use version 3.8+ for modern features
|
|
- Define services in logical order
|
|
- Use environment variables for configuration
|
|
- Set resource limits
|
|
|
|
```yaml
|
|
# ✅ Good
|
|
version: '3.8'
|
|
|
|
services:
|
|
app:
|
|
image: myapp:latest
|
|
build:
|
|
context: .
|
|
dockerfile: Dockerfile
|
|
environment:
|
|
- NODE_ENV=production
|
|
- DATABASE_URL=postgres://db:5432/app
|
|
depends_on:
|
|
db:
|
|
condition: service_healthy
|
|
networks:
|
|
- app-network
|
|
deploy:
|
|
resources:
|
|
limits:
|
|
cpus: '0.5'
|
|
memory: 512M
|
|
healthcheck:
|
|
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
|
|
interval: 30s
|
|
timeout: 10s
|
|
retries: 3
|
|
start_period: 40s
|
|
|
|
db:
|
|
image: postgres:15-alpine
|
|
volumes:
|
|
- postgres-data:/var/lib/postgresql/data
|
|
environment:
|
|
POSTGRES_DB: app
|
|
POSTGRES_USER: ${DB_USER}
|
|
POSTGRES_PASSWORD: ${DB_PASSWORD}
|
|
networks:
|
|
- app-network
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "pg_isready -U $POSTGRES_USER"]
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 5
|
|
|
|
networks:
|
|
app-network:
|
|
driver: bridge
|
|
|
|
volumes:
|
|
postgres-data:
|
|
```
|
|
|
|
### Environment Variables
|
|
|
|
- Use `.env` files for local development
|
|
- Never commit `.env` files with secrets
|
|
- Use Docker secrets for sensitive data in Swarm
|
|
|
|
```bash
|
|
# .env (gitignored)
|
|
NODE_ENV=production
|
|
DB_PASSWORD=secure_password_here
|
|
JWT_SECRET=your_jwt_secret_here
|
|
```
|
|
|
|
```yaml
|
|
# docker-compose.yml
|
|
services:
|
|
app:
|
|
env_file:
|
|
- .env
|
|
# OR explicit for non-sensitive
|
|
environment:
|
|
- NODE_ENV=production
|
|
# Secrets for sensitive data in Swarm
|
|
secrets:
|
|
- db_password
|
|
```
|
|
|
|
### Network Patterns
|
|
|
|
```yaml
|
|
# ✅ Good: Separated networks for security
|
|
networks:
|
|
frontend:
|
|
driver: bridge
|
|
backend:
|
|
driver: bridge
|
|
internal: true # No external access
|
|
|
|
services:
|
|
web:
|
|
networks:
|
|
- frontend
|
|
- backend
|
|
api:
|
|
networks:
|
|
- backend
|
|
db:
|
|
networks:
|
|
- backend
|
|
```
|
|
|
|
### Volume Management
|
|
|
|
```yaml
|
|
# ✅ Good: Named volumes with labels
|
|
volumes:
|
|
postgres-data:
|
|
driver: local
|
|
labels:
|
|
- "app=myapp"
|
|
- "type=database"
|
|
|
|
services:
|
|
db:
|
|
volumes:
|
|
- postgres-data:/var/lib/postgresql/data
|
|
- ./init-scripts:/docker-entrypoint-initdb.d:ro
|
|
```
|
|
|
|
## Docker Swarm
|
|
|
|
### Service Deployment
|
|
|
|
```yaml
|
|
# docker-compose.yml (Swarm compatible)
|
|
version: '3.8'
|
|
|
|
services:
|
|
api:
|
|
image: myapp/api:latest
|
|
deploy:
|
|
mode: replicated
|
|
replicas: 3
|
|
update_config:
|
|
parallelism: 1
|
|
delay: 10s
|
|
failure_action: rollback
|
|
rollback_config:
|
|
parallelism: 1
|
|
delay: 10s
|
|
restart_policy:
|
|
condition: on-failure
|
|
delay: 5s
|
|
max_attempts: 3
|
|
window: 120s
|
|
placement:
|
|
constraints:
|
|
- node.role == worker
|
|
preferences:
|
|
- spread: node.id
|
|
resources:
|
|
limits:
|
|
cpus: '0.5'
|
|
memory: 512M
|
|
reservations:
|
|
cpus: '0.25'
|
|
memory: 256M
|
|
networks:
|
|
- app-network
|
|
secrets:
|
|
- db_password
|
|
- jwt_secret
|
|
configs:
|
|
- app_config
|
|
|
|
networks:
|
|
app-network:
|
|
driver: overlay
|
|
attachable: true
|
|
|
|
secrets:
|
|
db_password:
|
|
external: true
|
|
jwt_secret:
|
|
external: true
|
|
|
|
configs:
|
|
app_config:
|
|
external: true
|
|
```
|
|
|
|
### Stack Deployment
|
|
|
|
```bash
|
|
# Deploy stack
|
|
docker stack deploy -c docker-compose.yml mystack
|
|
|
|
# List services
|
|
docker stack services mystack
|
|
|
|
# Scale service
|
|
docker service scale mystack_api=5
|
|
|
|
# Update service
|
|
docker service update --image myapp/api:v2 mystack_api
|
|
|
|
# Rollback
|
|
docker service rollback mystack_api
|
|
```
|
|
|
|
### Health Checks
|
|
|
|
```yaml
|
|
services:
|
|
api:
|
|
# Health check in Dockerfile
|
|
healthcheck:
|
|
test: ["CMD", "node", "healthcheck.js"]
|
|
interval: 30s
|
|
timeout: 10s
|
|
retries: 3
|
|
start_period: 60s
|
|
|
|
# Or in compose
|
|
healthcheck:
|
|
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
|
|
interval: 30s
|
|
timeout: 10s
|
|
retries: 3
|
|
```
|
|
|
|
### Secrets Management
|
|
|
|
```bash
|
|
# Create secret
|
|
echo "my_secret_password" | docker secret create db_password -
|
|
|
|
# Create secret from file
|
|
docker secret create jwt_secret ./jwt_secret.txt
|
|
|
|
# List secrets
|
|
docker secret ls
|
|
|
|
# Use in compose
|
|
secrets:
|
|
db_password:
|
|
external: true
|
|
```
|
|
|
|
### Config Management
|
|
|
|
```bash
|
|
# Create config
|
|
docker config create app_config ./config.json
|
|
|
|
# Use in compose
|
|
configs:
|
|
app_config:
|
|
external: true
|
|
|
|
services:
|
|
api:
|
|
configs:
|
|
- app_config
|
|
```
|
|
|
|
## Container Security
|
|
|
|
### Image Security
|
|
|
|
```bash
|
|
# Scan image for vulnerabilities
|
|
docker scout vulnerabilities myapp:latest
|
|
trivy image myapp:latest
|
|
|
|
# Check image for secrets
|
|
gitleaks --image myapp:latest
|
|
```
|
|
|
|
### Runtime Security
|
|
|
|
```dockerfile
|
|
# ✅ Good: Security measures
|
|
FROM node:20-alpine
|
|
|
|
# Create non-root user
|
|
RUN addgroup -g 1001 appgroup && \
|
|
adduser -u 1001 -G appgroup -D appuser
|
|
|
|
# Set read-only filesystem
|
|
RUN chmod -R 755 /app && \
|
|
chown -R appuser:appgroup /app
|
|
|
|
WORKDIR /app
|
|
COPY --chown=appuser:appgroup . .
|
|
|
|
# Drop all capabilities
|
|
USER appuser
|
|
VOLUME ["/tmp"]
|
|
|
|
CMD ["node", "server.js"]
|
|
```
|
|
|
|
### Network Security
|
|
|
|
```yaml
|
|
# ✅ Good: Limited network access
|
|
services:
|
|
api:
|
|
networks:
|
|
- backend
|
|
# No ports exposed to host
|
|
|
|
db:
|
|
networks:
|
|
- backend
|
|
# Internal network only
|
|
|
|
networks:
|
|
backend:
|
|
internal: true # No internet access
|
|
```
|
|
|
|
### Resource Limits
|
|
|
|
```yaml
|
|
services:
|
|
api:
|
|
deploy:
|
|
resources:
|
|
limits:
|
|
cpus: '1.0'
|
|
memory: 1G
|
|
reservations:
|
|
cpus: '0.5'
|
|
memory: 512M
|
|
```
|
|
|
|
## Common Patterns
|
|
|
|
### Development Setup
|
|
|
|
```yaml
|
|
# docker-compose.dev.yml
|
|
version: '3.8'
|
|
services:
|
|
app:
|
|
build:
|
|
context: .
|
|
dockerfile: Dockerfile.dev
|
|
volumes:
|
|
- .:/app
|
|
- /app/node_modules
|
|
environment:
|
|
- NODE_ENV=development
|
|
ports:
|
|
- "3000:3000"
|
|
command: npm run dev
|
|
```
|
|
|
|
### Production Setup
|
|
|
|
```yaml
|
|
# docker-compose.prod.yml
|
|
version: '3.8'
|
|
services:
|
|
app:
|
|
image: myapp:${VERSION}
|
|
environment:
|
|
- NODE_ENV=production
|
|
deploy:
|
|
replicas: 3
|
|
update_config:
|
|
parallelism: 1
|
|
delay: 10s
|
|
healthcheck:
|
|
test: ["CMD", "node", "healthcheck.js"]
|
|
interval: 30s
|
|
timeout: 10s
|
|
retries: 3
|
|
```
|
|
|
|
### Multi-Environment
|
|
|
|
```bash
|
|
# Override files
|
|
docker-compose -f docker-compose.yml -f docker-compose.dev.yml up
|
|
docker-compose -f docker-compose.yml -f docker-compose.prod.yml up -d
|
|
```
|
|
|
|
### Logging
|
|
|
|
```yaml
|
|
services:
|
|
app:
|
|
logging:
|
|
driver: "json-file"
|
|
options:
|
|
max-size: "10m"
|
|
max-file: "3"
|
|
labels: "app,environment"
|
|
```
|
|
|
|
## CI/CD Integration
|
|
|
|
### Build Pipeline
|
|
|
|
```yaml
|
|
# .github/workflows/docker.yml
|
|
name: Docker Build
|
|
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
|
|
jobs:
|
|
build:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@v3
|
|
|
|
- name: Build image
|
|
run: docker build -t myapp:${{ github.sha }} .
|
|
|
|
- name: Scan image
|
|
run: trivy image myapp:${{ github.sha }}
|
|
|
|
- name: Push to registry
|
|
run: |
|
|
echo ${{ secrets.DOCKER_PASSWORD }} | docker login -u ${{ secrets.DOCKER_USER }} --password-stdin
|
|
docker push myapp:${{ github.sha }}
|
|
```
|
|
|
|
## Troubleshooting
|
|
|
|
### Common Commands
|
|
|
|
```bash
|
|
# View logs
|
|
docker-compose logs -f app
|
|
|
|
# Execute in container
|
|
docker-compose exec app sh
|
|
|
|
# Check health
|
|
docker inspect --format='{{.State.Health.Status}}' <container>
|
|
|
|
# View resource usage
|
|
docker stats
|
|
|
|
# Remove unused resources
|
|
docker system prune -a
|
|
|
|
# Debug network
|
|
docker network inspect app-network
|
|
|
|
# Swarm diagnostics
|
|
docker node ls
|
|
docker service ps mystack_api
|
|
```
|
|
|
|
## Prohibitions
|
|
|
|
- DO NOT run containers as root
|
|
- DO NOT use `latest` tag in production
|
|
- DO NOT expose unnecessary ports
|
|
- DO NOT store secrets in images
|
|
- DO NOT use privileged mode unnecessarily
|
|
- DO NOT mount host directories without restrictions
|
|
- DO NOT skip health checks in production
|
|
- DO NOT ignore vulnerability scans |