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
| Approach | Strengths |
|---|---|
| PaaS (Heroku, etc.) | Simple operations |
| VM + systemd | Control vs. cost tradeoff |
| Kubernetes | Large scale, many services |
Further reading
Summary
Key takeaways
- Gunicorn: production WSGI server
- Docker: reproducible environments
- Heroku: fast path to hosted apps
- AWS: flexible, often cost-effective at scale
- Nginx: reverse proxy in front of app workers
Next steps
- Pandas for data analysis
- Web scraping (future topics in the series)