name: docker description: "Docker - containers, Dockerfile, docker-compose, multi-stage builds, production, CI/CD" metadata: author: mte90 version: 1.0.0 tags: - docker - docker-compose - containerization - deployment - ci-cd - containers
Docker
Complete guide to containerization with Docker - images, containers, compose, and best practices.
Dockerfile
Basic Dockerfile
FROM python:3.12-slim
WORKDIR /app
# Install dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy application
COPY . .
# Run
CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]
Production Dockerfile (Multi-stage)
# Build stage
FROM python:3.12-slim AS builder
WORKDIR /app
# Install build dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
build-essential \
libpq-dev \
&& rm -rf /var/lib/apt/lists/*
# Create virtual environment
RUN python -m venv /venv
ENV PATH="/venv/bin:$PATH"
# Install dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Production stage
FROM python:3.12-slim
# Install runtime dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
libpq5 \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
# Copy virtual environment
COPY --from=builder /venv /venv
ENV PATH="/venv/bin:$PATH"
# Copy application
COPY --from=builder /app /app
# Create non-root user
RUN adduser --disabled-password --gecos '' appuser && \
chown -R appuser:appuser /app
USER appuser
# Expose port
EXPOSE 8000
# Run
CMD ["gunicorn", "myproject.wsgi:application", "--bind", "0.0.0.0:8000"]
requirements.txt
# requirements.txt
Django>=5.0
gunicorn>=21.0
psycopg2-binary>=2.9
redis>=5.0
django-redis>=0.9
celery>=5.3
docker-compose.yml
Development
version: '3.8'
services:
web:
build: .
ports:
- "8000:8000"
volumes:
- .:/app
environment:
- DEBUG=1
- DATABASE_URL=postgres://postgres:postgres@db:5432/mydb
- REDIS_URL=redis://redis:6379/0
depends_on:
- db
- redis
db:
image: postgres:16
environment:
- POSTGRES_DB=mydb
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=postgres
volumes:
- postgres_data:/var/lib/postgresql/data
ports:
- "5432:5432"
redis:
image: redis:7-alpine
ports:
- "6379:6379"
celery:
build: .
command: celery -A myproject worker -l info
volumes:
- .:/app
environment:
- DATABASE_URL=postgres://postgres:postgres@db:5432/mydb
- REDIS_URL=redis://redis:6379/0
depends_on:
- db
- redis
volumes:
postgres_data:
Production
version: '3.8'
services:
web:
build:
context: .
dockerfile: Dockerfile.prod
ports:
- "8000:8000"
environment:
- DEBUG=0
- DATABASE_URL=postgres://user:password@db:5432/mydb
- REDIS_URL=redis://redis:6379/0
depends_on:
db:
condition: service_healthy
redis:
condition: service_started
restart: unless-stopped
db:
image: postgres:16
environment:
- POSTGRES_DB=mydb
- POSTGRES_USER=user
- POSTGRES_PASSWORD=password
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U user -d mydb"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
redis:
image: redis:7-alpine
restart: unless-stopped
celery:
build:
context: .
dockerfile: Dockerfile.prod
command: celery -A myproject worker -l info
environment:
- DATABASE_URL=postgres://user:password@db:5432/mydb
- REDIS_URL=redis://redis:6379/0
depends_on:
- db
- redis
restart: unless-stopped
celery-beat:
build:
context: .
dockerfile: Dockerfile.prod
command: celery -A myproject beat -l info
environment:
- DATABASE_URL=postgres://user:password@db:5432/mydb
- REDIS_URL=redis://redis:6379/0
depends_on:
- db
- redis
restart: unless-stopped
volumes:
postgres_data:
Docker Commands
# Build
docker build -t myapp .
# Run
docker run -p 8000:8000 myapp
# With docker-compose
docker-compose up -d
docker-compose up -d --build
docker-compose logs -f web
docker-compose exec web python manage.py migrate
# Stop
docker-compose down
docker-compose down -v # Remove volumes
GitHub Actions CI/CD
# .github/workflows/docker.yml
name: Docker
on:
push:
branches: [main]
tags: ['v*']
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: myuser/myapp
tags: |
type=ref,event=branch
type=ref,event=tag
type=sha
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=registry,ref=myuser/myapp:buildcache
cache-to: type=registry,ref=myuser/myapp:buildcache,mode=max
Best Practices
- Use multi-stage builds for smaller images
- Don't run as root - use USER instruction
- Use .dockerignore to exclude unnecessary files
- Use healthchecks for dependencies
- Use volume mounts for development
- Set proper environment variables
- Use gunicorn in production, not runserver
- Scan images for vulnerabilities - docker scout
- Use specific tags - not :latest in production
- Readonly containers - use --read-only flag
Docker Compose
Basic Usage
# docker-compose.yml
version: '3.8'
services:
web:
build: .
ports:
- "8000:8000"
environment:
- DEBUG=1
depends_on:
- db
- redis
volumes:
- .:/app
restart: unless-stopped
db:
image: postgres:15-alpine
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
- POSTGRES_DB=app
- POSTGRES_USER=user
- POSTGRES_PASSWORD=secret
redis:
image: redis:7-alpine
volumes:
- redis_data:/data
volumes:
postgres_data:
redis_data:
Compose Commands
# Start all services
docker compose up -d
# View logs
docker compose logs -f web
# Scale service
docker compose up -d --scale web=3
# Stop all
docker compose down
# With volumes
docker compose down -v
BuildKit
Enable BuildKit
# Enable permanently
export DOCKER_BUILDKIT=1
# Or per-build
DOCKER_BUILDKIT=1 docker build .
BuildKit Features
# Syntax directive for BuildKit
# syntax=docker/dockerfile:1
# --mount=type=cache for caching layers
FROM node:20
RUN --mount=type=cache,target=/root/.npm \
npm ci
Multi-platform Builds
# Build for multiple platforms
docker buildx build \
--platform linux/amd64,linux/arm64 \
--tag myapp:latest \
--push .
Security
Scan images
docker scout cves myapp:latest
docker scout recommendations myapp:latest
Production Tips
# Run with resource limits
docker run -d \
--name myapp \
--memory=512m \
--cpus=1.0 \
--restart=unless-stopped \
-e PYTHONUNBUFFERED=1 \
myapp:latest
# Healthcheck
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8000/health || exit 1
References
- Docker Docs: https://docs.docker.com/
- Dockerfile Best Practices: https://docs.docker.com/develop/develop-images/dockerfile_best-practices/