185 lines
3.9 KiB
Markdown
185 lines
3.9 KiB
Markdown
---
|
|
name: docker-patterns
|
|
description: Docker and Docker Compose best practices — multi-stage builds, networking, volumes, container security, optimization. Use when writing Dockerfiles, docker-compose.yml, or containerizing applications.
|
|
---
|
|
|
|
# Docker Patterns
|
|
|
|
Based on ECC docker-patterns skill.
|
|
|
|
## Dockerfile Best Practices
|
|
|
|
### Pin base image versions
|
|
```dockerfile
|
|
# Good
|
|
FROM node:20.11.0-alpine3.19
|
|
|
|
# Bad — version changes can break builds
|
|
FROM node:latest
|
|
```
|
|
|
|
### Multi-Stage Builds for minimal images
|
|
```dockerfile
|
|
# Stage 1: Build
|
|
FROM golang:1.22-bookworm AS builder
|
|
WORKDIR /app
|
|
COPY go.mod go.sum ./
|
|
RUN go mod download
|
|
COPY . .
|
|
RUN CGO_ENABLED=0 go build -o myapp -ldflags="-s -w"
|
|
|
|
# Stage 2: Runtime (small image)
|
|
FROM scratch
|
|
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
|
COPY --from=builder /app/myapp /myapp
|
|
USER 1000
|
|
CMD ["/myapp"]
|
|
```
|
|
|
|
### Layer caching optimization
|
|
```dockerfile
|
|
# Copy dependency files first, install, then copy rest
|
|
COPY package.json package-lock.json ./
|
|
RUN npm ci --production=false
|
|
COPY . .
|
|
```
|
|
|
|
### .dockerignore
|
|
```
|
|
node_modules/
|
|
.git/
|
|
.env
|
|
dist/
|
|
*.log
|
|
__pycache__
|
|
*.pyc
|
|
```
|
|
|
|
## Docker Compose Patterns
|
|
|
|
### Production-ready compose
|
|
```yaml
|
|
name: myapp
|
|
services:
|
|
app:
|
|
image: myapp:${APP_VERSION:-latest}
|
|
restart: unless-stopped
|
|
environment:
|
|
DATABASE_URL: postgresql://${DB_USER}:${DB_PASSWORD}@db:5432/myapp
|
|
NODE_ENV: production
|
|
depends_on:
|
|
db:
|
|
condition: service_healthy
|
|
healthcheck:
|
|
test: ["CMD", "wget", "--spider", "http://localhost:3000/healthz"]
|
|
interval: 30s
|
|
timeout: 10s
|
|
retries: 3
|
|
networks: [app-net]
|
|
deploy:
|
|
resources:
|
|
limits:
|
|
memory: 512M
|
|
cpus: "0.5"
|
|
|
|
db:
|
|
image: postgres:16-alpine
|
|
volumes:
|
|
- pgdata:/var/lib/postgresql/data
|
|
environment:
|
|
POSTGRES_DB: myapp
|
|
POSTGRES_USER: ${DB_USER}
|
|
POSTGRES_PASSWORD: ${DB_PASSWORD}
|
|
healthcheck:
|
|
test: ["CMD-SHELL", "pg_isready -U ${DB_USER}"]
|
|
interval: 10s
|
|
timeout: 5s
|
|
retries: 5
|
|
|
|
networks:
|
|
app-net:
|
|
driver: bridge
|
|
|
|
volumes:
|
|
pgdata:
|
|
```
|
|
|
|
## Container Security
|
|
|
|
### Run as non-root
|
|
```dockerfile
|
|
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
|
|
USER appuser
|
|
```
|
|
|
|
### No sensitive data in images
|
|
- Never COPY .env files
|
|
- Never hardcode credentials in Dockerfile
|
|
- Use docker secrets, env vars at runtime
|
|
|
|
### Minimize attack surface
|
|
- Use Alpine or distroless base images
|
|
- Remove build tools from final image (multi-stage)
|
|
- Only expose needed ports
|
|
|
|
### Scan images
|
|
```bash
|
|
# Scan for vulnerabilities
|
|
docker scout cve myapp:latest
|
|
|
|
# Scan Dockerfile for issues
|
|
hadolint Dockerfile
|
|
```
|
|
|
|
## Networking
|
|
|
|
### Service-to-service communication
|
|
```yaml
|
|
# Compose: services can reach each other by name
|
|
# app can reach db at hostname "db" on port 5432
|
|
```
|
|
|
|
### Isolating networks
|
|
```yaml
|
|
networks:
|
|
frontend: # Public-facing services
|
|
backend: # DB, cache (not exposed to outside)
|
|
```
|
|
|
|
## Volumes
|
|
|
|
### Named volumes (data persistence)
|
|
```yaml
|
|
volumes:
|
|
- pgdata:/var/lib/postgresql/data # Persisted data
|
|
- redis-data:/data
|
|
```
|
|
|
|
### Bind mounts (development)
|
|
```yaml
|
|
volumes:
|
|
- ./src:/app/src:ro # Read-only code mount
|
|
- /data:/app/logs # Log output
|
|
```
|
|
|
|
## Optimization Tips
|
|
|
|
1. **Smallest base image**: `scratch` > `alpine` > `slim` > `full`
|
|
2. **Layer count**: Fewer layers = smaller image
|
|
3. **COPY order**: Dependencies first, then application code
|
|
4. **Combined RUN commands**: `RUN apt-get update && apt-get install -y x && rm -rf /var/lib/apt/lists/*`
|
|
5. **Multi-stage**: Build tools in builder, runtime only in final
|
|
6. **Distroless for Go/Rust**: `FROM scratch` with just the binary
|
|
|
|
## Checklist
|
|
|
|
- [ ] Base image pinned to specific version
|
|
- [ ] Multi-stage build for production
|
|
- [ ] Non-root user
|
|
- [ ] .dockerignore complete
|
|
- [ ] Health checks configured
|
|
- [ ] No secrets in image
|
|
- [ ] Minimal base image
|
|
- [ ] Volumes for persistent data
|
|
- [ ] Network isolation
|