Post

Docker - Deploy a Flask App with Docker Compose

Docker - Deploy a Flask App with Docker Compose

Intro

This guide expands on deploying a Flask app using Docker Compose, incorporating advanced features and best practices for production-ready applications. By the end, you’ll have a robust, scalable, and secure deployment pipeline.


Step 1: Create a Flask App

Start by creating a directory for your project:

1
mkdir flask-docker && cd flask-docker

Inside, create app.py:

1
2
3
4
5
6
7
8
9
10
from flask import Flask

app = Flask(__name__)

@app.route('/')
def home():
    return "Hello, Docker!"

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

Add Templates and Static Files (Optional)

For dynamic content, create a templates directory with an HTML file:

1
2
3
4
5
6
7
8
9
10
11
12
<!-- templates/home.html -->
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Flask App</title>
  </head>
  <body>
    <h1>Welcome to Flask with Docker!</h1>
  </body>
</html>

Update app.py to render the template:

1
2
3
4
5
6
7
from flask import Flask, render_template

app = Flask(__name__)

@app.route('/')
def home():
    return render_template('home.html')

Step 2: Write an Optimized Dockerfile

Create a Dockerfile:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# Use a lightweight base image
FROM python:3.11-slim

# Set environment variables for production
ENV PYTHONDONTWRITEBYTECODE=1 \
    PYTHONUNBUFFERED=1

# Set working directory
WORKDIR /app

# Install dependencies in one layer to optimize caching
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Copy application code
COPY . .

# Expose the application port
EXPOSE 5000

# Run the application
CMD ["gunicorn", "-w", "4", "-b", "0.0.0.0:5000", "app:app"]

Best Practices for Dockerfile:

  1. Use Multi-Stage Builds for smaller images:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
    FROM python:3.11-slim AS builder
    WORKDIR /app
    COPY requirements.txt .
    RUN pip install --no-cache-dir -r requirements.txt
    
    FROM python:3.11-slim AS final
    WORKDIR /app
    COPY --from=builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages
    COPY . .
    CMD ["gunicorn", "-w", "4", "-b", "0.0.0.0:5000", "app:app"]
    
  2. Minimize Layers by combining commands.


Step 3: Create an Advanced docker-compose.yml File

Define services and advanced configurations:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
version: "3.8"

services:
  web:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - "5000:5000"
    environment:
      FLASK_ENV: production
    depends_on:
      - redis
    healthcheck:
      test: ["CMD-SHELL", "curl -f http://localhost:5000 || exit 1"]
      interval: 30s
      timeout: 10s
      retries: 3

  redis:
    image: redis:7-alpine
    networks:
      - app-network

networks:
  app-network:
    driver: bridge

volumes:
  redis_data:

Key Features Explained:

  • Health Checks ensure services are ready before others start.
  • Custom Networks isolate communication between services.
  • Volumes persist data across container restarts.

Step 4: Build and Run the Application

Build the Docker image:

1
docker compose build --no-cache

Run the containers in detached mode:

1
docker compose up -d

Check service health:

1
docker ps --filter "health=healthy"

Access the app at http://localhost:5000.


Step 5: Add Multi-Environment Support

For different environments (e.g., development, production), use multiple Compose files:

Example Setup:

  1. docker-compose.yml (Base Configuration)
  2. docker-compose.override.yml (Development Overrides)
  3. docker-compose.prod.yml (Production Overrides)

Run with specific configurations:

1
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

Step 6: Secure and Optimize Your Deployment

Security Best Practices:

  1. Use non-root users in containers.
  2. Limit resources to prevent overuse:
    1
    2
    3
    4
    5
    
    deploy:
      resources:
        limits:
          cpus: "1"
          memory: "512M"
    
  3. Scan images for vulnerabilities using tools like trivy.

Performance Optimization:

  • Enable caching layers in Dockerfile.
  • Use lightweight base images like alpine.
  • Optimize database queries and indexing.

Advanced Features to Explore

Logging and Monitoring:

Integrate tools like ELK Stack or Prometheus/Grafana for centralized logging and monitoring.

CI/CD Pipelines:

Automate builds and deployments using GitHub Actions or GitLab CI/CD.

Scaling with Load Balancers:

Use tools like Traefik or Nginx to distribute traffic across multiple containers.


Conclusion

Congratulations! You’ve deployed a production-ready Flask app using Docker Compose with advanced configurations and best practices. This setup is scalable, secure, and ready for real-world applications.

This post is licensed under CC BY 4.0 by the author.