# 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}}' # 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