Docker and Containerization: Deploying Python Applications
Docker enables packaging Python applications with all dependencies into containers, ensuring consistency across development, testing, and production environments.
Docker Fundamentals
Installation
# Install Docker
# macOS: brew install docker
# Ubuntu: sudo apt-get install docker.io
# Windows: Download Docker Desktop
# Verify installation
docker --version
docker run hello-world
Basic Concepts
- Image: Blueprint for containers (like a class)
- Container: Running instance of an image (like an object)
- Dockerfile: Instructions to build an image
- Registry: Repository for storing images (Docker Hub, etc.)
Creating Docker Images
Simple Dockerfile
# Use official Python runtime as base image
FROM python:3.11-slim
# Set working directory
WORKDIR /app
# Copy requirements
COPY requirements.txt .
# Install dependencies
RUN pip install --no-cache-dir -r requirements.txt
# Copy application code
COPY . .
# Expose port
EXPOSE 8000
# Set environment variables
ENV PYTHONUNBUFFERED=1
# Run application
CMD ["python", "app.py"]
Multi-stage Dockerfile
# Build stage
FROM python:3.11 as builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --user --no-cache-dir -r requirements.txt
# Runtime stage
FROM python:3.11-slim
WORKDIR /app
# Copy only necessary files from builder
COPY --from=builder /root/.local /root/.local
COPY . .
ENV PATH=/root/.local/bin:$PATH
ENV PYTHONUNBUFFERED=1
EXPOSE 8000
CMD ["python", "app.py"]
Dockerfile for Flask Application
FROM python:3.11-slim
WORKDIR /app
# Install system dependencies
RUN apt-get update && apt-get install -y \
gcc \
&& rm -rf /var/lib/apt/lists/*
# Copy and install Python dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy application
COPY . .
# Create non-root user
RUN useradd -m appuser && chown -R appuser:appuser /app
USER appuser
EXPOSE 5000
# Health check
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD python -c "import requests; requests.get('http://localhost:5000/health')"
CMD ["gunicorn", "--bind", "0.0.0.0:5000", "--workers", "4", "app:app"]
requirements.txt
Flask==2.3.0
requests==2.31.0
python-dotenv==1.0.0
gunicorn==21.0.0
Building and Running Containers
Build Image
# Build image
docker build -t my-python-app:1.0 .
# Build with build arguments
docker build -t my-app:latest --build-arg PYTHON_VERSION=3.11 .
# List images
docker images
# Remove image
docker rmi my-python-app:1.0
Run Container
# Run container
docker run -d --name my-container my-python-app:1.0
# Run with port mapping
docker run -d -p 8000:8000 --name my-app my-python-app:1.0
# Run with environment variables
docker run -d -e DATABASE_URL=postgres://... my-python-app:1.0
# Run with volume mount
docker run -d -v /host/path:/container/path my-python-app:1.0
# Run with resource limits
docker run -d --memory=512m --cpus=1 my-python-app:1.0
# Run interactively
docker run -it my-python-app:1.0 /bin/bash
# List running containers
docker ps
# Stop container
docker stop my-container
# Remove container
docker rm my-container
Docker Compose
docker-compose.yml
version: '3.8'
services:
web:
build: .
ports:
- "8000:8000"
environment:
- DATABASE_URL=postgresql://user:password@db:5432/mydb
- REDIS_URL=redis://redis:6379
depends_on:
- db
- redis
volumes:
- .:/app
command: python app.py
db:
image: postgres:15
environment:
- POSTGRES_USER=user
- POSTGRES_PASSWORD=password
- POSTGRES_DB=mydb
volumes:
- postgres_data:/var/lib/postgresql/data
ports:
- "5432:5432"
redis:
image: redis:7-alpine
ports:
- "6379:6379"
celery:
build: .
command: celery -A tasks worker --loglevel=info
environment:
- DATABASE_URL=postgresql://user:password@db:5432/mydb
- REDIS_URL=redis://redis:6379
depends_on:
- db
- redis
volumes:
postgres_data:
Docker Compose Commands
# Start services
docker-compose up
# Start in background
docker-compose up -d
# Stop services
docker-compose down
# View logs
docker-compose logs -f web
# Execute command in service
docker-compose exec web python manage.py migrate
# Rebuild images
docker-compose build
# Scale service
docker-compose up -d --scale celery=3
Best Practices
Optimize Image Size
# Bad: Large image
FROM python:3.11
RUN apt-get update && apt-get install -y build-essential
COPY requirements.txt .
RUN pip install -r requirements.txt
# Good: Smaller image
FROM python:3.11-slim
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
Security
# Create non-root user
RUN useradd -m -u 1000 appuser
USER appuser
# Use specific versions
FROM python:3.11.0-slim
# Don't run as root
# Don't include secrets in image
# Use .dockerignore to exclude unnecessary files
.dockerignore
__pycache__
*.pyc
*.pyo
*.pyd
.Python
env/
venv/
.git
.gitignore
.dockerignore
.env
.env.local
*.log
.pytest_cache
.coverage
dist/
build/
*.egg-info/
Debugging Containers
# View container logs
docker logs my-container
docker logs -f my-container # Follow logs
# Inspect container
docker inspect my-container
# Execute command in running container
docker exec -it my-container /bin/bash
# View resource usage
docker stats
# View processes in container
docker top my-container
# Copy files from container
docker cp my-container:/app/file.txt ./file.txt
Registry and Deployment
Push to Docker Hub
# Login to Docker Hub
docker login
# Tag image
docker tag my-python-app:1.0 username/my-python-app:1.0
# Push image
docker push username/my-python-app:1.0
# Pull image
docker pull username/my-python-app:1.0
Private Registry
# Tag for private registry
docker tag my-app:1.0 registry.example.com/my-app:1.0
# Push to private registry
docker push registry.example.com/my-app:1.0
# Pull from private registry
docker pull registry.example.com/my-app:1.0
Common Pitfalls
Bad Practice:
# Don't: Large base image
FROM python:3.11
# Don't: Cache pip packages
RUN pip install -r requirements.txt
# Don't: Run as root
# (no USER directive)
# Don't: Copy entire directory
COPY . .
Good Practice:
# Do: Use slim image
FROM python:3.11-slim
# Do: Don't cache pip
RUN pip install --no-cache-dir -r requirements.txt
# Do: Run as non-root
RUN useradd -m appuser
USER appuser
# Do: Use .dockerignore
COPY app/ /app/
Conclusion
Docker enables consistent, reproducible deployments of Python applications. Master Dockerfile creation, Docker Compose for multi-container applications, and best practices for security and efficiency. Containerization is essential for modern application deployment.
Comments