HostiServer
2026-02-23 14:53:00
Co je WSGI: snadný průvodce Python webovými projekty
Proč je konfigurace serveru kritická pro Python aplikace
Django a Flask patří mezi nejpopulárnější frameworky pro webový vývoj. Instagram, Pinterest, Spotify, Dropbox jsou postaveny na Django. Netflix, LinkedIn, Reddit používají Flask pro mikroslužby a API. Ale i dokonale napsaný kód bude zpomalovat na špatně nakonfigurovaném serveru.
Špatný počet workerů, chybějící connection pooling, běh pod rootem, DEBUG=True v produkci — typické chyby, které změní rychlou aplikaci na pomalou a zranitelnou. Viděli jsme projekty, kde doba odezvy klesla z 1,2 sekundy na 150 ms jednoduše po správné konfiguraci databáze.
V tomto průvodci probereme production-ready nastavení: od výběru mezi Gunicorn a uWSGI po systemd služby s automatickým restartem, od connection poolingu po monitoring. Všechna doporučení vycházejí z reálných zkušeností s nasazením stovek Python projektů.
Django vs Flask: kdy co vybrat
Django — „batteries included" framework s vestavěnou administrací, ORM, autentizací, formuláři, middleware, systémem migrací. Ideální pro velké projekty: eCommerce, CRM, SaaS platformy, systémy správy obsahu, sociální sítě. Více kódu „z krabice", méně rozhodnutí dělat sami, rychlejší start pro typické úlohy.
Flask — minimalistický mikroframework. Dává plnou svobodu ve výběru komponent: ORM (SQLAlchemy), formuláře (WTForms), autentizace (Flask-Login), admin (Flask-Admin). Vhodný pro API, mikroslužby, malé aplikace, prototypy, Machine Learning služby. Snadnější na naučení, ale vyžaduje více ruční práce pro velké projekty.
Oba frameworky jsou production-ready a používají se ve velkých společnostech. Rozdíl je v přístupu: Django — konvence nad konfigurací (Django Way), Flask — svoboda výběru (micro framework).
Gunicorn vs uWSGI: co vybrat
Oba jsou WSGI servery pro spouštění Python aplikací. Fungují jako prostředník mezi webovým serverem (Nginx) a vaším kódem (Django/Flask). Ale v praxi volíme Gunicorn v 90 % případů.
Proč Gunicorn
- Jednodušší konfigurace — méně parametrů, srozumitelnější dokumentace, rychlejší start
- Standard pro Docker — většina tutoriálů, Dockerfile příkladů a best practices je napsána pro něj
- „Čistší" kód — snadnější ladění problémů, méně „magie"
- Dostatečná funkcionalita — pro 90 % projektů není potřeba víc
- Aktivní komunita — rychlé odpovědi na otázky, pravidelné aktualizace
Kdy uWSGI
uWSGI je výkonnější: má vlastní binární protokol (uwsgi), vestavěné cachování, emperor mode pro správu více aplikací, podporu WebSocket z krabice. Ale konfigurace je příliš složitá pro typické projekty — desítky parametrů, komplexní syntaxe. Vyberte uWSGI pouze pokud:
- Potřebujete emperor mode pro správu mnoha aplikací na jednom serveru
- Používáte specifické funkce: vestavěné cachování, spooler pro fronty
- Už máte zkušenosti s uWSGI
Počet workerů
Klasický vzorec (2 × CPU jader) + 1 je dobrý výchozí bod, ale ne axiom. Více o výběru počtu jader a vláken pro server:
| Typ úloh | Doporučení | Proč |
|---|---|---|
| CPU-bound | ≈ počet jader | Zpracování obrázků, náročné výpočty — workery soutěží o CPU |
| I/O-bound | (2–4) × jader | Mnoho dotazů do DB, externí API — workery často čekají |
| Smíšené | (2 × jader) + 1 | Typická webová aplikace — vyvážení |
Async workery (gevent, eventlet)
Pro I/O-bound aplikace s mnoha současnými požadavky můžete použít asynchronní workery:
# Instalace
pip install gunicorn gevent
# Spuštění s gevent workery
gunicorn myproject.wsgi:application \
--worker-class gevent \
--workers 4 \
--worker-connections 1000 \
--bind unix:/run/gunicorn.sock
Jeden gevent worker může obsluhovat tisíce současných spojení díky kooperativnímu multitaskingu. Ale je tu nuance: veškerý kód musí být „gevent-friendly" — blokující operace zablokují celý worker.
Kompletní příklad konfigurace Gunicorn
# /etc/gunicorn/gunicorn.conf.py
# Počet workerů pro 4jádrový server
workers = 9 # (2×4)+1
# Bind na Unix socket (rychlejší než TCP)
bind = 'unix:/run/gunicorn.sock'
# Timeouty
timeout = 30 # Pokud déle — přesuňte do Celery
graceful_timeout = 30 # Čas na dokončení aktuálního požadavku
keepalive = 5 # Keep-alive spojení s Nginx
# Restart workerů pro prevenci úniků paměti
max_requests = 1000
max_requests_jitter = 100 # Náhodná odchylka
# Logování
accesslog = '/var/log/gunicorn/access.log'
errorlog = '/var/log/gunicorn/error.log'
loglevel = 'warning'
# Proces
daemon = False # Systemd řídí proces
user = 'www-data'
group = 'www-data'
Důležité parametry:
timeout = 30— pokud požadavek trvá déle než 30 sekund, architekturu je třeba změnit na fronty (Celery). Nezvyšujte timeout — skrývá to problémgraceful_timeout = 30— čas na dokončení aktuálního požadavku před restartem workerumax_requests = 1000— restart workeru po 1000 požadavcích zabraňuje hromadění úniků paměti ze špatně napsaných knihoven
Production Checklist pro Django
Před uvedením do produkce nezapomeňte zkontrolovat tato nastavení:
settings.py
# KRITICKÉ: vypnout debug mode
DEBUG = False
# Jasný seznam povolených domén
ALLOWED_HOSTS = ['example.com', 'www.example.com']
# Pokud je Django za Nginx/proxy
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
# Bezpečnost
SECURE_SSL_REDIRECT = True
SESSION_COOKIE_SECURE = True
CSRF_COOKIE_SECURE = True
SECURE_HSTS_SECONDS = 31536000
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
⚠️ DEBUG = False je kritické! Se zapnutým DEBUG při chybách Django zobrazuje celý traceback s kódem, cestami k souborům, proměnnými prostředí. To je přímá bezpečnostní hrozba.
Tajemství a proměnné prostředí
Nikdy necommitujte tajemství do Gitu. Použijte:
- Environment Variables přes .env soubor (django-environ nebo python-dotenv)
- HashiCorp Vault nebo AWS Secrets Manager pro velké projekty
# .env soubor (NECOMMITOVAT do Gitu!)
SECRET_KEY=your-super-secret-key
DATABASE_URL=postgres://user:pass@localhost/dbname
DEBUG=False
Middleware
Povinné middleware pro produkci:
- SecurityMiddleware — přesměrování na HTTPS, ochrana před XSS/Clickjacking
- WhiteNoise — servírování statických souborů bez samostatného webového serveru (pokud není CDN)
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'whitenoise.middleware.WhiteNoiseMiddleware', # Za SecurityMiddleware
# ... další middleware
]
# Konfigurace WhiteNoise
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
Systemd: automatické spuštění a monitoring
Systemd zajišťuje automatické spuštění aplikace při startu serveru a restart při selhání.
Typický soubor služby
Vytvořte soubor /etc/systemd/system/gunicorn.service:
[Unit]
Description=Gunicorn instance to serve myproject
After=network.target
[Service]
User=www-data
Group=www-data
WorkingDirectory=/var/www/myproject
# Cesta ke gunicorn ve virtualenv
ExecStart=/var/www/myproject/venv/bin/gunicorn \
--workers 3 \
--bind unix:/run/gunicorn.sock \
myproject.wsgi:application
# Automatický restart při selhání
Restart=always
RestartSec=5
# Proměnné prostředí
EnvironmentFile=/var/www/myproject/.env
[Install]
WantedBy=multi-user.target
Správa služby
# Znovu načíst konfiguraci systemd
sudo systemctl daemon-reload
# Povolit automatické spuštění
sudo systemctl enable gunicorn
# Spustit službu
sudo systemctl start gunicorn
# Zkontrolovat stav
sudo systemctl status gunicorn
# Zobrazit logy
sudo journalctl -u gunicorn -f
Parametr Restart=always spolu s RestartSec=5 zaručuje, že služba se automaticky spustí 5 sekund po jakémkoliv selhání.
Nginx jako reverse proxy
Nginx přijímá požadavky od klientů a předává je Gunicornu. Také servíruje statické soubory a zajišťuje SSL šifrování.
Konfigurace pro Django
# /etc/nginx/sites-available/myproject
upstream gunicorn {
server unix:/run/gunicorn.sock fail_timeout=0;
}
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name example.com www.example.com;
# SSL certifikáty (Let's Encrypt)
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# Statické soubory
location /static/ {
alias /var/www/myproject/staticfiles/;
expires 30d;
add_header Cache-Control "public, immutable";
}
# Media soubory (uploads)
location /media/ {
alias /var/www/myproject/media/;
expires 7d;
}
# Proxy na Gunicorn
location / {
proxy_pass http://gunicorn;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 30s;
proxy_read_timeout 30s;
}
}
# Aktivovat konfiguraci
sudo ln -s /etc/nginx/sites-available/myproject /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
Konfigurace pro Flask
Pro Flask je konfigurace Nginx téměř identická. Hlavní rozdíl je v příkazu Gunicorn:
# Django
gunicorn myproject.wsgi:application --workers 3 --bind unix:/run/gunicorn.sock
# Flask
gunicorn app:app --workers 3 --bind unix:/run/gunicorn.sock
# Flask s factory pattern (create_app)
gunicorn "app:create_app()" --workers 3 --bind unix:/run/gunicorn.sock
Kde app:app znamená: soubor app.py, proměnná app (Flask instance).
PostgreSQL vs MySQL
Pro Django doporučujeme PostgreSQL v 95 % případů. Důvody:
- Nejlepší podpora v Django — JSONB pole, fulltextové vyhledávání, ArrayField, práce s datovými typy
- Spolehlivost — lepší zpracování souběžných transakcí, MVCC
- Rozšiřitelnost — PostGIS pro geodata, TimescaleDB pro time-series
- Průmyslový standard — většina Python projektů používá Postgres
MySQL používáme pouze pokud je to požadavek klienta nebo dědictví starého kódu.
Connection Pooling
Každý požadavek do Django vytváří spojení s databází. Při vysokém provozu se to stává vážným bottleneck — databáze nezvládá počet současných spojení.
Na úrovni Django (základní řešení):
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'mydb',
'USER': 'myuser',
'PASSWORD': 'mypassword',
'HOST': 'localhost',
'CONN_MAX_AGE': 60, # Držet spojení 60 sekund
'CONN_HEALTH_CHECKS': True, # Django 4.1+
}
}
CONN_MAX_AGE obvykle nastavujeme na 60–300 sekund. To umožňuje znovu používat spojení mezi požadavky.
Při vysokém provozu — PgBouncer:
Pokud CONN_MAX_AGE nestačí nebo vidíte chyby „too many connections", nastavte PgBouncer mezi Django a databázi. Udržuje pool spojení a rozděluje je mezi workery.
# Instalace PgBouncer
sudo apt install pgbouncer
# Konfigurace /etc/pgbouncer/pgbouncer.ini
[databases]
mydb = host=localhost dbname=mydb
[pgbouncer]
listen_addr = 127.0.0.1
listen_port = 6432
pool_mode = transaction
max_client_conn = 1000
default_pool_size = 20
💡 Reálný případ: Projekt na Flask, kde každý požadavek vytvářel nové připojení k DB. Výsledek: 504 Gateway Timeout pod zátěží, doba odezvy ~1,2 sekundy. Po implementaci PgBouncer (connection pooling) doba odezvy klesla na 150 ms — zlepšení 8×.
Docker v produkci
Rozhodně doporučujeme. Docker zajišťuje identické prostředí vývoje a produkce — „works on my machine" přestává být problémem. Nasazení se stává předvídatelným a opakovatelným.
Výběr základního obrazu
Doporučujeme python:3.12-slim:
- alpine — často vytváří problémy s kompilací C knihoven (psycopg2, numpy, Pillow, cryptography). Build může trvat mnohem déle kvůli kompilaci s musl libc
- slim — ideální vyvážení mezi velikostí (~150 MB) a stabilitou. Používá glibc, všechny knihovny se instalují bez problémů
- plný obraz — pouze pokud potřebujete specifické systémové balíčky (ffmpeg, imagemagick atd.)
Příklad Dockerfile
FROM python:3.12-slim
# Proměnné prostředí pro Python
ENV PYTHONDONTWRITEBYTECODE=1
ENV PYTHONUNBUFFERED=1
# Systémové závislosti
RUN apt-get update && apt-get install -y \
libpq-dev \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
# Nejdříve zkopírujeme requirements pro cachování vrstev
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Pak kód aplikace
COPY . .
# Vytvoříme neprivilegovaného uživatele
RUN useradd -m appuser && chown -R appuser:appuser /app
USER appuser
# Sběr statiky
RUN python manage.py collectstatic --noinput
# Spuštění
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "--workers", "3", "myproject.wsgi:application"]
Docker Compose pro produkci
version: '3.8'
services:
web:
build: .
restart: always
env_file: .env
depends_on:
- db
- redis
networks:
- backend
db:
image: postgres:16
restart: always
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
POSTGRES_DB: mydb
POSTGRES_USER: myuser
POSTGRES_PASSWORD: ${DB_PASSWORD}
networks:
- backend
redis:
image: redis:7-alpine
restart: always
networks:
- backend
nginx:
image: nginx:alpine
restart: always
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./staticfiles:/var/www/static:ro
- ./certbot/conf:/etc/letsencrypt:ro
depends_on:
- web
networks:
- backend
networks:
backend:
volumes:
postgres_data:
Důležité praktiky pro Docker
- Multi-stage builds — pro zmenšení velikosti finálního obrazu
- Health checks — pro automatický restart nezdravých kontejnerů
- .dockerignore — vylučte .git, __pycache__, .env, venv
- Neukládejte data v kontejneru — používejte volumes pro DB a media soubory
Celery pro úlohy na pozadí
Pokud požadavek do Django trvá déle než 30 sekund — je to signál přesunout úlohu do Celery. Gunicorn worker je blokován po dobu vykonávání požadavku, a při dlouhých úlohách rychle dojdou volné workery.
Typické kandidáty pro Celery:
- Odesílání e-mailů (zejména hromadné rozesílky)
- Zpracování obrázků (resize, watermark, optimalizace)
- Generování reportů (PDF, Excel)
- Volání externích API (platební systémy, doručování)
- Import/export dat (CSV, XML)
- Video/audio konverze
- Synchronizace s CRM/ERP
Základní nastavení
# settings.py
CELERY_BROKER_URL = 'redis://localhost:6379/0'
CELERY_RESULT_BACKEND = 'redis://localhost:6379/0'
CELERY_ACCEPT_CONTENT = ['json']
CELERY_TASK_SERIALIZER = 'json'
CELERY_RESULT_SERIALIZER = 'json'
CELERY_TIMEZONE = 'Europe/Prague'
# Limity pro prevenci přetížení
CELERY_WORKER_PREFETCH_MULTIPLIER = 1
CELERY_TASK_ACKS_LATE = True
Příklad úlohy
# tasks.py
from celery import shared_task
from django.core.mail import send_mail
@shared_task
def send_welcome_email(user_id):
from users.models import User
user = User.objects.get(id=user_id)
send_mail(
'Vítejte!',
'Děkujeme za registraci.',
'noreply@example.com',
[user.email],
)
# Volání ve view
send_welcome_email.delay(user.id) # Asynchronně
Systemd služba pro Celery worker
# /etc/systemd/system/celery.service
[Unit]
Description=Celery Worker
After=network.target
[Service]
User=www-data
Group=www-data
WorkingDirectory=/var/www/myproject
EnvironmentFile=/var/www/myproject/.env
ExecStart=/var/www/myproject/venv/bin/celery \
-A myproject worker \
--loglevel=info \
--concurrency=4
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
Pro periodické úlohy (cron-like) přidejte Celery Beat jako samostatnou službu s celery -A myproject beat.
Monitoring a logování
Produkce bez monitoringu je létání naslepo. Když něco spadne, dozvíte se to od uživatelů, ne z alertů.
Logování v Django
# settings.py
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'verbose': {
'format': '{levelname} {asctime} {module} {message}',
'style': '{',
},
},
'handlers': {
'file': {
'level': 'WARNING',
'class': 'logging.FileHandler',
'filename': '/var/log/django/app.log',
'formatter': 'verbose',
},
'console': {
'class': 'logging.StreamHandler',
},
},
'root': {
'handlers': ['file', 'console'],
'level': 'INFO',
},
}
Sentry pro sledování chyb
Sentry automaticky sbírá exceptions z produkce a posílá alerty. Neocenitelný nástroj pro ladění.
# Instalace
pip install sentry-sdk
# settings.py
import sentry_sdk
sentry_sdk.init(
dsn="https://xxx@sentry.io/xxx",
traces_sample_rate=0.1, # 10 % transakcí pro performance monitoring
environment="production",
)
Health checks
Endpoint pro kontrolu stavu aplikace — užitečné pro load balancery a monitoring:
# Django urls.py
from django.http import JsonResponse
def health_check(request):
# Kontrola DB
from django.db import connection
try:
connection.ensure_connection()
except Exception as e:
return JsonResponse({'status': 'error', 'db': str(e)}, status=500)
return JsonResponse({'status': 'ok'})
urlpatterns = [
path('health/', health_check),
# ...
]
Metriky pro Prometheus/Grafana
Pro seriózní monitoring použijte django-prometheus nebo statsd:
- Počet požadavků za sekundu
- Doba odezvy (p50, p95, p99)
- Počet chyb
- Využití paměti workerů
Časté chyby
- ❌ Spuštění Django pod rootem
-
Nikdy nespouštějte aplikaci jako root. Vytvořte samostatného uživatele (www-data nebo django) s minimálními právy. Pokud je aplikace kompromitována, útočník získá pouze omezená práva, ne plnou kontrolu nad serverem.
- ❌ DEBUG = True v produkci
-
Při chybách Django zobrazuje veškerý kód, cesty k souborům, hodnoty proměnných, SQL dotazy. To je přímá bezpečnostní hrozba — útočník vidí vnitřní strukturu aplikace. Vždy zkontrolujte DEBUG před nasazením.
- ❌ Chybějící logování
-
Když něco spadne v produkci a v lozích je prázdno — ladění se stává noční můrou. Nakonfigurujte LOGGING v settings.py, logujte chyby do souboru nebo centralizovaného systému. Použijte Sentry pro sledování exceptions v reálném čase.
- ❌ Media soubory ve složce s kódem
-
Ukládání uploads ve složce s kódem znemožňuje horizontální škálování a komplikuje nasazení v Docker. Použijte S3-kompatibilní úložiště (AWS S3, MinIO, DigitalOcean Spaces) nebo samostatný volume.
- ❌ Tajemství v kódu
-
SECRET_KEY, hesla DB, API klíče v settings.py nebo commitnuté do Gitu — klasická chyba. Historii Gitu nelze úplně vyčistit, tajemství zůstanou přístupná. Používejte environment variables od prvního dne projektu.
- ❌ Chybějící migrace v produkci
-
Spuštění
python manage.py migratev produkci bez testování — riziko ztráty dat nebo výpadku. Vždy testujte migrace na staging prostředí, dělejte backup databáze před migrací.
🐍 Připraveni spustit Python projekt?
VPS pro start nebo Dedicated pro vysokou zátěž — řešení, která rostou s vámi.
💻 Cloud (VPS) Hosting
- Od 19,95 $/měs — Začněte v malém, škálujte okamžitě
- KVM virtualizace — Garantované zdroje bez oversellingu
- Okamžité upgrady — Bez výpadku
- NVMe úložiště — Rychlý výkon
- 24/7 podpora — Odpověď do 10 min
🖥️ Dedikované servery
- Od 200 $/měs — Moderní konfigurace
- Vlastní konfigurace — Intel nebo AMD, nejnovější modely
- Více lokalit — EU + USA
- 99,9% uptime — Spolehlivost
- DDoS ochrana — Zahrnuto
- Bezplatná migrace — Pomůžeme
- Private Cloud — Proxmox, VMware, OpenStack
💬 Nejste si jisti, kterou variantu potřebujete?
💬 Napište nám — pomůžeme se vším!
Často kladené otázky
- Gunicorn nebo uWSGI pro produkci?
-
Pro 90 % projektů doporučujeme Gunicorn — jednodušší konfigurace, je standardem pro Docker kontejnery, má srozumitelnou dokumentaci. uWSGI vyberte pouze pokud potřebujete jeho specifické funkce: vlastní binární protokol uwsgi, vestavěné cachování, emperor mode pro správu více aplikací.
- PostgreSQL nebo MySQL pro Django?
-
PostgreSQL v 95 % případů. Django má nejlepší podporu právě pro Postgres: JSONB pole pro ukládání JSON bez samostatných tabulek, fulltextové vyhledávání bez Elasticsearch, ArrayField, HStoreField. MySQL pouze pokud je to požadavek klienta nebo migrace existujícího projektu.
- Kolik workerů pro Gunicorn?
-
Výchozí vzorec: (2 × CPU jader) + 1. Pro 4jádrový server to je 9 workerů. Pro I/O-bound úlohy (mnoho dotazů do DB) můžete zvýšit. Pro CPU-bound (zpracování obrázků) — držte se blíže počtu jader. Vždy testujte pod reálnou zátěží.
- Který Docker obraz vybrat?
-
python:3.12-slim— optimální vyvážení mezi velikostí (~150 MB) a stabilitou. Alpine často vytváří problémy s kompilací C knihoven kvůli musl libc — psycopg2, numpy, Pillow se nemusí sestavit nebo sestavení trvá hodiny.
- Co dělat při 504 Gateway Timeout?
-
Obvykle problém v dlouhých dotazech do DB nebo externích služeb. Zkontrolujte PostgreSQL slow query log, implementujte connection pooling (PgBouncer), přesuňte náročné úlohy do Celery, použijte Redis pro cachování častých dotazů.
- Jak bezpečně aktualizovat závislosti?
-
Neaktualizujte vše najednou. Použijte
pip-auditpro kontrolu zranitelností, aktualizujte jeden balíček najednou, testujte na staging. Pro kritické bezpečnostní záplaty — aktualizujte okamžitě po testování.
- Jak škálovat Django/Flask aplikaci?
-
Horizontálně: přidejte více serverů za load balancer. Vertikálně: zvyšte prostředky serveru. Povinně: přesuňte sessions do Redis, media soubory do S3, databázi na samostatný server. Použijte CDN pro statiku.