Python WSGI #
Aplikasi Python web (Django, Flask) tidak memiliki HTTP server production-ready bawaan. Mereka butuh WSGI server sebagai jembatan antara Nginx dan kode Python. Dua pilihan paling populer adalah Gunicorn dan uWSGI.
Browser → Nginx → Gunicorn/uWSGI → Django/Flask
Nginx menangani SSL, static files, gzip, dan koneksi klien. Gunicorn/uWSGI mengelola worker process Python yang memproses request.
Nginx dengan Gunicorn #
Gunicorn adalah WSGI server yang paling mudah dikonfigurasi — pilihan default untuk kebanyakan proyek Python:
# Jalankan Gunicorn
gunicorn myapp.wsgi:application \
--workers 4 \
--bind unix:/run/gunicorn/myapp.sock \
--worker-class sync \
--timeout 30 \
--access-logfile /var/log/gunicorn/access.log \
--error-logfile /var/log/gunicorn/error.log
upstream gunicorn_app {
server unix:/run/gunicorn/myapp.sock;
keepalive 16;
}
server {
listen 443 ssl;
http2 on;
server_name myapp.com;
ssl_certificate /etc/letsencrypt/live/myapp.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/myapp.com/privkey.pem;
# Static files Django: `python manage.py collectstatic` mengumpulkan ke sini
location /static/ {
alias /var/www/myapp/staticfiles/;
expires 1y;
add_header Cache-Control "public, immutable";
access_log off;
}
location /media/ {
alias /var/www/myapp/media/;
expires 30d;
access_log off;
}
# Semua request ke Gunicorn
location / {
proxy_pass http://gunicorn_app;
proxy_http_version 1.1;
proxy_set_header Connection "";
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 5s;
proxy_read_timeout 60s;
}
}
server {
listen 80;
server_name myapp.com;
return 301 https://$host$request_uri;
}
Django: Konfigurasi settings.py #
Django perlu dikonfigurasi untuk berjalan di balik proxy:
# settings.py
# Izinkan domain ini menerima request
ALLOWED_HOSTS = ['myapp.com', 'www.myapp.com']
# Baca IP asli dari header X-Forwarded-For
USE_X_FORWARDED_HOST = True
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
# Direktori untuk file statis setelah collectstatic
STATIC_ROOT = '/var/www/myapp/staticfiles/'
STATIC_URL = '/static/'
MEDIA_ROOT = '/var/www/myapp/media/'
MEDIA_URL = '/media/'
# Kumpulkan semua static file ke STATIC_ROOT
python manage.py collectstatic --noinput
Nginx dengan uWSGI #
uWSGI menawarkan lebih banyak opsi konfigurasi dan bisa menggunakan protokol uWSGI biner (lebih efisien dari HTTP untuk komunikasi internal):
# /etc/uwsgi/apps-available/myapp.ini
[uwsgi]
chdir = /var/www/myapp
module = myapp.wsgi:application
home = /var/www/myapp/venv
master = true
processes = 4
threads = 2
socket = /run/uwsgi/myapp.sock
chmod-socket = 660
vacuum = true # Hapus socket saat shutdown
die-on-term = true
max-requests = 1000 # Restart worker setelah N request (cegah memory leak)
upstream uwsgi_app {
server unix:/run/uwsgi/myapp.sock;
}
location / {
# Gunakan protokol uwsgi (bukan http) untuk efisiensi lebih baik
uwsgi_pass uwsgi_app;
include uwsgi_params;
uwsgi_read_timeout 60;
uwsgi_connect_timeout 5;
}
Protokol uwsgi (bukan proxy_pass http://) adalah biner dan lebih efisien dari HTTP untuk komunikasi antara Nginx dan uWSGI di server yang sama.
ASGI: Django Channels dan FastAPI #
Untuk aplikasi async Python (Django Channels, FastAPI, Starlette), gunakan uvicorn sebagai ASGI server bukan Gunicorn WSGI:
# Jalankan uvicorn (contoh untuk FastAPI)
uvicorn main:app \
--workers 4 \
--uds /run/uvicorn/myapp.sock \
--proxy-headers \
--forwarded-allow-ips '*'
upstream asgi_app {
server unix:/run/uvicorn/myapp.sock;
keepalive 16;
}
server {
location / {
proxy_pass http://asgi_app;
proxy_http_version 1.1;
# Penting untuk ASGI dan WebSocket
proxy_set_header Connection $http_connection;
proxy_set_header Upgrade $http_upgrade;
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;
}
}
Jumlah Worker yang Tepat #
Pedoman umum untuk menentukan jumlah worker Gunicorn/uWSGI:
Worker sinkron (sync):
Rumus: (2 × jumlah CPU core) + 1
Server 4 core → 9 workers
Worker async (gevent/asyncio):
Bisa jauh lebih banyak karena setiap worker menangani banyak request
Server 4 core → 4 workers × 1000 coroutine = 4000 request konkuren
Untuk aplikasi yang banyak I/O waiting (query database, API call):
Pertimbangkan worker async atau uWSGI dengan threads
Ringkasan #
- Gunicorn untuk kesederhanaan, uWSGI untuk lebih banyak kontrol — keduanya berfungsi baik dengan Nginx.
- Gunakan Unix socket untuk komunikasi Nginx ↔ Gunicorn/uWSGI di server yang sama — lebih cepat dari TCP loopback.
- Django butuh
ALLOWED_HOSTSdanSECURE_PROXY_SSL_HEADERdikonfigurasi dengan benar saat di balik Nginx.- Selalu jalankan
collectstaticdan layani static files dari Nginx langsung — jangan lewatkan Python untuk request statis.- Untuk aplikasi async (FastAPI, Django Channels), gunakan uvicorn sebagai ASGI server, bukan Gunicorn WSGI.