Docker Guide
This comprehensive guide covers Docker usage, containerization best practices, and deployment strategies for applications built with Trae.
Overview
Docker enables consistent, portable, and scalable application deployment through containerization. This guide covers:
- Docker fundamentals
- Dockerfile best practices
- Multi-stage builds
- Docker Compose
- Container orchestration
- Security considerations
- Performance optimization
Docker Fundamentals
Core Concepts
Images and Containers
- Image: Read-only template for creating containers
- Container: Running instance of an image
- Layer: Each instruction in Dockerfile creates a layer
- Registry: Storage and distribution system for images
Docker Architecture
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Docker CLI │ │ Docker Daemon │ │ Docker Hub │
│ │───▶│ │───▶│ │
│ docker build │ │ Image Mgmt │ │ Image Store │
│ docker run │ │ Container │ │ Public/Private│
│ docker push │ │ Management │ │ Repositories │
└─────────────────┘ └─────────────────┘ └─────────────────┘Basic Commands
bash
# Image management
docker build -t myapp:latest .
docker images
docker rmi image_id
docker pull nginx:alpine
docker push myregistry/myapp:latest
# Container management
docker run -d -p 3000:3000 --name myapp myapp:latest
docker ps
docker ps -a
docker stop container_id
docker rm container_id
docker logs container_id
docker exec -it container_id /bin/bash
# System management
docker system prune
docker volume ls
docker network lsDockerfile Best Practices
Basic Dockerfile Structure
dockerfile
# Use official base image
FROM node:18-alpine AS base
# Set working directory
WORKDIR /app
# Copy package files
COPY package*.json ./
# Install dependencies
RUN npm ci --only=production && npm cache clean --force
# Copy application code
COPY . .
# Create non-root user
RUN addgroup -g 1001 -S nodejs && \
adduser -S nextjs -u 1001
# Change ownership
RUN chown -R nextjs:nodejs /app
USER nextjs
# Expose port
EXPOSE 3000
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:3000/health || exit 1
# Start application
CMD ["npm", "start"]Multi-Stage Builds
dockerfile
# Multi-stage build for Node.js application
# Stage 1: Build dependencies
FROM node:18-alpine AS dependencies
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force
# Stage 2: Build application
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Stage 3: Production image
FROM node:18-alpine AS production
WORKDIR /app
# Install dumb-init for proper signal handling
RUN apk add --no-cache dumb-init
# Create non-root user
RUN addgroup -g 1001 -S nodejs && \
adduser -S nextjs -u 1001
# Copy built application
COPY --from=builder --chown=nextjs:nodejs /app/dist ./dist
COPY --from=dependencies --chown=nextjs:nodejs /app/node_modules ./node_modules
COPY --chown=nextjs:nodejs package*.json ./
# Switch to non-root user
USER nextjs
# Expose port
EXPOSE 3000
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1
# Use dumb-init for proper signal handling
ENTRYPOINT ["dumb-init", "--"]
CMD ["node", "dist/server.js"]Language-Specific Examples
Python Application
dockerfile
FROM python:3.11-slim AS base
# Set environment variables
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1 \
PYTHONHASHSEED=random \
PIP_NO_CACHE_DIR=1 \
PIP_DISABLE_PIP_VERSION_CHECK=1
# Install system dependencies
RUN apt-get update && apt-get install -y \
build-essential \
curl \
&& rm -rf /var/lib/apt/lists/*
# Create non-root user
RUN groupadd -r appuser && useradd -r -g appuser appuser
# Set working directory
WORKDIR /app
# Install Python dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy application code
COPY --chown=appuser:appuser . .
# Switch to non-root user
USER appuser
# Expose port
EXPOSE 8000
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8000/health || exit 1
# Start application
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "app:app"]Go Application
dockerfile
# Build stage
FROM golang:1.21-alpine AS builder
WORKDIR /app
# Copy go mod files
COPY go.mod go.sum ./
RUN go mod download
# Copy source code
COPY . .
# Build application
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main .
# Production stage
FROM alpine:latest AS production
# Install ca-certificates for HTTPS
RUN apk --no-cache add ca-certificates
# Create non-root user
RUN adduser -D -s /bin/sh appuser
WORKDIR /root/
# Copy binary from builder stage
COPY --from=builder /app/main .
# Change ownership
RUN chown appuser:appuser main
# Switch to non-root user
USER appuser
# Expose port
EXPOSE 8080
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:8080/health || exit 1
# Start application
CMD ["./main"]Docker Compose
Basic Docker Compose Setup
yaml
# docker-compose.yml
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile
target: production
ports:
- "3000:3000"
environment:
- NODE_ENV=production
- DATABASE_URL=postgresql://postgres:password@db:5432/myapp
- REDIS_URL=redis://redis:6379
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
volumes:
- app_logs:/app/logs
networks:
- app_network
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
interval: 30s
timeout: 10s
retries: 3
start_period: 40s
db:
image: postgres:15-alpine
environment:
- POSTGRES_DB=myapp
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=password
volumes:
- postgres_data:/var/lib/postgresql/data
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
networks:
- app_network
restart: unless-stopped
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
command: redis-server --appendonly yes
volumes:
- redis_data:/data
networks:
- app_network
restart: unless-stopped
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 3s
retries: 3
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
- ./ssl:/etc/nginx/ssl
depends_on:
- app
networks:
- app_network
restart: unless-stopped
volumes:
postgres_data:
redis_data:
app_logs:
networks:
app_network:
driver: bridgeDevelopment Docker Compose
yaml
# docker-compose.dev.yml
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile.dev
ports:
- "3000:3000"
- "9229:9229" # Node.js debugger
environment:
- NODE_ENV=development
- DEBUG=app:*
volumes:
- .:/app
- /app/node_modules
command: npm run dev
depends_on:
- db
- redis
db:
image: postgres:15-alpine
environment:
- POSTGRES_DB=myapp_dev
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=password
ports:
- "5432:5432"
volumes:
- postgres_dev_data:/var/lib/postgresql/data
redis:
image: redis:7-alpine
ports:
- "6379:6379"
mailhog:
image: mailhog/mailhog
ports:
- "1025:1025"
- "8025:8025"
volumes:
postgres_dev_data:Production Docker Compose
yaml
# docker-compose.prod.yml
version: '3.8'
services:
app:
image: myregistry/myapp:${APP_VERSION:-latest}
deploy:
replicas: 3
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
resources:
limits:
cpus: '0.5'
memory: 512M
reservations:
cpus: '0.25'
memory: 256M
environment:
- NODE_ENV=production
secrets:
- db_password
- jwt_secret
networks:
- app_network
- traefik_network
labels:
- "traefik.enable=true"
- "traefik.http.routers.app.rule=Host(`myapp.com`)"
- "traefik.http.routers.app.tls.certresolver=letsencrypt"
db:
image: postgres:15-alpine
environment:
- POSTGRES_DB=myapp
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD_FILE=/run/secrets/db_password
volumes:
- postgres_data:/var/lib/postgresql/data
secrets:
- db_password
networks:
- app_network
deploy:
placement:
constraints:
- node.role == manager
secrets:
db_password:
external: true
jwt_secret:
external: true
volumes:
postgres_data:
external: true
networks:
app_network:
driver: overlay
attachable: true
traefik_network:
external: trueContainer Orchestration
Docker Swarm
bash
# Initialize swarm
docker swarm init
# Join worker nodes
docker swarm join --token SWMTKN-1-... manager-ip:2377
# Deploy stack
docker stack deploy -c docker-compose.prod.yml myapp
# Scale services
docker service scale myapp_app=5
# Update service
docker service update --image myregistry/myapp:v2.0.0 myapp_app
# Monitor services
docker service ls
docker service ps myapp_appKubernetes Deployment
yaml
# k8s/deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
labels:
app: myapp
spec:
replicas: 3
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: myregistry/myapp:latest
ports:
- containerPort: 3000
env:
- name: NODE_ENV
value: "production"
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: myapp-secrets
key: database-url
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 3000
initialDelaySeconds: 5
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: myapp-service
spec:
selector:
app: myapp
ports:
- protocol: TCP
port: 80
targetPort: 3000
type: LoadBalancerSecurity Best Practices
Dockerfile Security
dockerfile
# Security-focused Dockerfile
FROM node:18-alpine AS base
# Update packages and install security updates
RUN apk update && apk upgrade && apk add --no-cache dumb-init
# Create non-root user
RUN addgroup -g 1001 -S nodejs && \
adduser -S nextjs -u 1001 -G nodejs
# Set working directory
WORKDIR /app
# Copy package files
COPY package*.json ./
# Install dependencies
RUN npm ci --only=production && \
npm cache clean --force && \
rm -rf /tmp/*
# Copy application code
COPY --chown=nextjs:nodejs . .
# Remove unnecessary files
RUN rm -rf .git .gitignore README.md docs/ tests/
# Set proper permissions
RUN chmod -R 755 /app && \
chmod -R 644 /app/package*.json
# Switch to non-root user
USER nextjs
# Expose port (non-privileged)
EXPOSE 3000
# Use dumb-init for proper signal handling
ENTRYPOINT ["dumb-init", "--"]
CMD ["node", "server.js"]Security Scanning
bash
# Scan images for vulnerabilities
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
aquasec/trivy image myapp:latest
# Scan Dockerfile
docker run --rm -i hadolint/hadolint < Dockerfile
# Use Docker Bench Security
docker run -it --net host --pid host --userns host --cap-add audit_control \
-e DOCKER_CONTENT_TRUST=$DOCKER_CONTENT_TRUST \
-v /var/lib:/var/lib \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /usr/lib/systemd:/usr/lib/systemd \
-v /etc:/etc --label docker_bench_security \
docker/docker-bench-securityRuntime Security
yaml
# docker-compose.yml with security constraints
version: '3.8'
services:
app:
image: myapp:latest
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE
read_only: true
tmpfs:
- /tmp
- /var/cache
user: "1001:1001"
ulimits:
nproc: 65535
nofile:
soft: 65535
hard: 65535Performance Optimization
Image Optimization
dockerfile
# Optimized Dockerfile
FROM node:18-alpine AS dependencies
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production && npm cache clean --force
FROM node:18-alpine AS build
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Distroless image for minimal attack surface
FROM gcr.io/distroless/nodejs18-debian11 AS production
WORKDIR /app
COPY --from=dependencies /app/node_modules ./node_modules
COPY --from=build /app/dist ./dist
COPY package*.json ./
EXPOSE 3000
CMD ["dist/server.js"]Build Optimization
bash
# Use BuildKit for faster builds
export DOCKER_BUILDKIT=1
docker build --target production -t myapp:latest .
# Use build cache
docker build --cache-from myapp:latest -t myapp:latest .
# Multi-platform builds
docker buildx build --platform linux/amd64,linux/arm64 -t myapp:latest .Resource Management
yaml
# docker-compose.yml with resource limits
version: '3.8'
services:
app:
image: myapp:latest
deploy:
resources:
limits:
cpus: '0.5'
memory: 512M
reservations:
cpus: '0.25'
memory: 256M
mem_limit: 512m
mem_reservation: 256m
cpus: 0.5
oom_kill_disable: falseMonitoring and Logging
Container Monitoring
yaml
# docker-compose.monitoring.yml
version: '3.8'
services:
prometheus:
image: prom/prometheus
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus_data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--web.console.libraries=/etc/prometheus/console_libraries'
- '--web.console.templates=/etc/prometheus/consoles'
grafana:
image: grafana/grafana
ports:
- "3001:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
volumes:
- grafana_data:/var/lib/grafana
cadvisor:
image: gcr.io/cadvisor/cadvisor
ports:
- "8080:8080"
volumes:
- /:/rootfs:ro
- /var/run:/var/run:rw
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:ro
volumes:
prometheus_data:
grafana_data:Centralized Logging
yaml
# docker-compose.logging.yml
version: '3.8'
services:
app:
image: myapp:latest
logging:
driver: "fluentd"
options:
fluentd-address: localhost:24224
tag: myapp
fluentd:
image: fluent/fluentd:v1.14-debian-1
ports:
- "24224:24224"
volumes:
- ./fluentd.conf:/fluentd/etc/fluent.conf
- fluentd_data:/var/log/fluentd
elasticsearch:
image: docker.elastic.co/elasticsearch/elasticsearch:8.5.0
environment:
- discovery.type=single-node
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
volumes:
- elasticsearch_data:/usr/share/elasticsearch/data
kibana:
image: docker.elastic.co/kibana/kibana:8.5.0
ports:
- "5601:5601"
environment:
- ELASTICSEARCH_HOSTS=http://elasticsearch:9200
volumes:
fluentd_data:
elasticsearch_data:CI/CD Integration
GitHub Actions
yaml
# .github/workflows/docker.yml
name: Docker Build and Deploy
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
- name: Log in to Container Registry
uses: docker/login-action@v2
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v4
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=sha,prefix={{branch}}-
type=raw,value=latest,enable={{is_default_branch}}
- name: Build and push Docker image
uses: docker/build-push-action@v4
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Run security scan
uses: aquasecurity/trivy-action@master
with:
image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
format: 'sarif'
output: 'trivy-results.sarif'
- name: Upload Trivy scan results
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: 'trivy-results.sarif'GitLab CI/CD
yaml
# .gitlab-ci.yml
stages:
- build
- test
- security
- deploy
variables:
DOCKER_DRIVER: overlay2
DOCKER_TLS_CERTDIR: "/certs"
IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
build:
stage: build
image: docker:20.10.16
services:
- docker:20.10.16-dind
before_script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
script:
- docker build -t $IMAGE_TAG .
- docker push $IMAGE_TAG
only:
- main
- develop
security_scan:
stage: security
image: aquasec/trivy:latest
script:
- trivy image --exit-code 0 --format template --template "@contrib/gitlab.tpl" -o gl-container-scanning-report.json $IMAGE_TAG
artifacts:
reports:
container_scanning: gl-container-scanning-report.json
only:
- main
- develop
deploy_staging:
stage: deploy
image: alpine:latest
before_script:
- apk add --no-cache curl
script:
- curl -X POST "$WEBHOOK_URL" -H "Content-Type: application/json" -d '{"image":"'$IMAGE_TAG'"}'
only:
- develop
deploy_production:
stage: deploy
image: alpine:latest
before_script:
- apk add --no-cache curl
script:
- curl -X POST "$PROD_WEBHOOK_URL" -H "Content-Type: application/json" -d '{"image":"'$IMAGE_TAG'"}'
only:
- main
when: manualTroubleshooting
Common Issues
Container Won't Start
bash
# Check container logs
docker logs container_name
# Check container configuration
docker inspect container_name
# Run container interactively
docker run -it --entrypoint /bin/sh image_namePerformance Issues
bash
# Monitor container resources
docker stats
# Check container processes
docker exec container_name ps aux
# Analyze image layers
docker history image_nameNetwork Issues
bash
# List networks
docker network ls
# Inspect network
docker network inspect network_name
# Test connectivity
docker exec container_name ping other_containerStorage Issues
bash
# Check disk usage
docker system df
# Clean up unused resources
docker system prune -a
# Check volume usage
docker volume ls
docker volume inspect volume_nameDebugging Tools
bash
# Debug container with additional tools
docker run -it --rm \
--pid container:target_container \
--net container:target_container \
--cap-add SYS_PTRACE \
nicolaka/netshoot
# Profile container performance
docker run -it --rm \
--pid container:target_container \
--cap-add SYS_PTRACE \
brendangregg/perf-toolsBest Practices Summary
Development
- Use Multi-stage Builds: Reduce image size and improve security
- Leverage Build Cache: Speed up builds with proper layer ordering
- Use .dockerignore: Exclude unnecessary files from build context
- Pin Base Images: Use specific versions for reproducible builds
- Run as Non-root: Improve security by using non-privileged users
Production
- Health Checks: Implement proper health checks for containers
- Resource Limits: Set appropriate CPU and memory limits
- Security Scanning: Regularly scan images for vulnerabilities
- Monitoring: Implement comprehensive monitoring and logging
- Backup Strategy: Ensure data persistence and backup procedures
Operations
- Container Orchestration: Use orchestration tools for production
- Rolling Updates: Implement zero-downtime deployment strategies
- Secret Management: Use proper secret management solutions
- Network Security: Implement network segmentation and policies
- Disaster Recovery: Plan for disaster recovery scenarios