Python Web Deployment | Heroku, AWS, and Docker in Practice

Python Web Deployment | Heroku, AWS, and Docker in Practice

이 글의 핵심

Take a Flask or Django app from laptop to production: process managers, containers, PaaS, and VMs with reverse proxies and secrets handled safely.

Introduction

“Development is done—time to deploy”

Deployment is the process of shipping your application to real users.


1. Deployment prep

requirements.txt

# Generate pinned dependencies
pip freeze > requirements.txt
Flask==2.3.0
gunicorn==20.1.0
python-dotenv==1.0.0

Environment variables (.env)

# .env
SECRET_KEY=your-secret-key
DATABASE_URL=postgresql://user:pass@localhost/db
DEBUG=False
# app.py
from dotenv import load_dotenv
import os

load_dotenv()

app.config['SECRET_KEY'] = os.getenv('SECRET_KEY')
app.config['DEBUG'] = os.getenv('DEBUG', 'False') == 'True'

2. Gunicorn

Running with Gunicorn

# Install
pip install gunicorn

# Run
gunicorn app:app

# Workers and bind address
gunicorn -w 4 -b 0.0.0.0:8000 app:app

gunicorn.conf.py

bind = "0.0.0.0:8000"
workers = 4
worker_class = "sync"
timeout = 30
keepalive = 2

3. Docker deployment

Dockerfile

FROM python:3.11-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

EXPOSE 8000

CMD ["gunicorn", "-w", "4", "-b", "0.0.0.0:8000", "app:app"]

docker-compose.yml

version: '3.8'

services:
  web:
    build: .
    ports:
      - "8000:8000"
    environment:
      - DATABASE_URL=postgresql://user:pass@db:5432/mydb
    depends_on:
      - db
  
  db:
    image: postgres:15
    environment:
      - POSTGRES_USER=user
      - POSTGRES_PASSWORD=pass
      - POSTGRES_DB=mydb
    volumes:
      - postgres_data:/var/lib/postgresql/data

volumes:
  postgres_data:
# Start stack
docker-compose up -d

4. Heroku deployment

Procfile

web: gunicorn app:app

runtime.txt

python-3.11.0

Deploy commands

# After installing Heroku CLI
heroku login
heroku create myapp

# Set config
heroku config:set SECRET_KEY=your-secret-key

# Deploy
git push heroku main

# Logs
heroku logs --tail

5. AWS deployment (EC2)

EC2 setup

# SSH into server
ssh -i key.pem ubuntu@ec2-instance

# Install Python tooling
sudo apt update
sudo apt install python3-pip python3-venv

# Clone project
git clone https://github.com/user/myapp.git
cd myapp

# Virtual environment
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt

# Run Gunicorn
gunicorn -w 4 -b 0.0.0.0:8000 app:app

Nginx configuration

# /etc/nginx/sites-available/myapp
server {
    listen 80;
    server_name example.com;
    
    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}
# Enable site
sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl restart nginx

6. Practical tips

Deployment checklist

# Environment variables
# SECRET_KEY, DATABASE_URL, etc.

# DEBUG = False
app.config['DEBUG'] = False

# ALLOWED_HOSTS (Django)
ALLOWED_HOSTS = ['yourdomain.com']

# Static files
python manage.py collectstatic

# Database migrations
python manage.py migrate

# HTTPS
# Let's Encrypt, Cloudflare, etc.

Going deeper

Example: Gunicorn + systemd (minimal production pattern)

Assume the app is exposed as myapp.wsgi:app. This example binds to 127.0.0.1:8000 (not socket activation).

/etc/systemd/system/myapp.service:

[Unit]
Description=My Gunicorn App
After=network.target

[Service]
User=www-data
Group=www-data
WorkingDirectory=/var/www/myapp
Environment="PATH=/var/www/myapp/venv/bin"
EnvironmentFile=/etc/myapp.env
ExecStart=/var/www/myapp/venv/bin/gunicorn -w 4 -b 127.0.0.1:8000 myapp.wsgi:app
Restart=always

[Install]
WantedBy=multi-user.target

After deployment: sudo systemctl daemon-reload && sudo systemctl enable --now myapp.

Common mistakes

  • Running with DEBUG=True in production and leaking secrets or tracebacks.
  • Hard-coding virtualenv paths so deploy scripts break on the server.
  • Relying on load balancers without health checks, so dead processes go unnoticed.

Safety notes

  • Keep secrets in environment variables or a secret manager—never commit them to Git.
  • Re-validate ALLOWED_HOSTS, CORS, and CSRF** under your production profile.

How teams usually operate

  • Put Nginx or Caddy in front for TLS termination.
  • Ship logs to journald and onward to CloudWatch, ELK, or similar.
  • For containers, prefer read-only root filesystems and non-root users.

Options at a glance

ApproachStrengths
PaaS (Heroku, etc.)Simple operations
VM + systemdControl vs. cost tradeoff
KubernetesLarge scale, many services

Further reading


Summary

Key takeaways

  1. Gunicorn: production WSGI server
  2. Docker: reproducible environments
  3. Heroku: fast path to hosted apps
  4. AWS: flexible, often cost-effective at scale
  5. Nginx: reverse proxy in front of app workers

Next steps

  • Pandas for data analysis
  • Web scraping (future topics in the series)