Mendeploy aplikasi Flask ke produksi dengan Gunicorn dan uWSGI: memahami worker model, menjalankan server WSGI dengan benar, mengelola proses lewat systemd, dan menyusun Nginx reverse proxy lengkap dengan proxy headers dan serve static files.

Server development Flask (flask run) jelas-jelas bukan untuk produksi: ia single-threaded, tanpa manajemen worker, dan rawan overload. Episode 22 mendeploy aplikasi dengan benar: Gunicorn (dan uWSGI) sebagai server WSGI produksi, systemd untuk mengelola prosesnya, dan Nginx sebagai reverse proxy di depannya.
Setelah episode ini, aplikasi kalian siap melayani lalu lintas nyata: multi-worker, auto-restart, static files cepat, dan TLS dari episode 20.
flask run adalah server development yang: hanya satu proses, tanpa buffering yang baik, dan menampilkan debugger di produksi (berbahaya — episode 3). Server WSGI produksi seperti Gunicorn menyelesaikan ini dengan worker model:
Master Gunicorn mengelola worker — tiap worker menjalankan instance aplikasi Flask. Beberapa worker = beberapa request diproses bersamaan.
Install dan jalankan:
pip install gunicorn
gunicorn --workers 3 --bind 127.0.0.1:8000 "app:create_app()"--workers 3 — jumlah worker (aturan praktis: 2 × CPU cores + 1).--bind 127.0.0.1:8000 — hanya di localhost; Nginx yang menerima lalu lintas publik."app:create_app()" — memanggil application factory (episode 9) — inilah alasan utama mengapa factory pattern penting.Bersamaan dengan episode 20, Gunicorn dan Nginx tersusun end-to-end: browser → Nginx (TLS) → Gunicorn (WSGI) → Flask.
Gunicorn punya beberapa tipe worker. Untuk Flask (WSGI, sync), yang relevan:
| Worker | Cocok untuk | Catatan |
|---|---|---|
sync (default) | Aplikasi sync biasa | Satu worker = satu request; butuh banyak worker untuk I/O lambat |
gevent / eventlet | Banyak I/O (jaringan, DB lambat) | Green thread — hemat resource untuk workload I/O-bound |
gthread | I/O campuran | Threads per worker — simpel dan stabil |
Untuk mayoritas aplikasi Flask, sync dengan jumlah worker yang pas sudah cukup. Jika aplikasi banyak menunggu I/O (menghubungi API eksternal), pertimbangkan gthread atau gevent:
gunicorn --worker-class gthread --threads 4 --workers 3 \
--bind 127.0.0.1:8000 "app:create_app()"Kita bandingkan tipe worker ini lebih dalam saat tuning performa di episode 24.
Warning
Jika memakai gevent/eventlet, perhatikan patching: library jaringan harus kompatibel dengan green threads. Dan jangan campur sync dengan aplikasi yang memakai konteks per-thread tanpa persiapan. Mulai dari sync yang paling bisa diprediksi, lalu ukur sebelum menambah kompleksitas.
systemd mengelola Gunicorn: memulai saat boot, restart saat crash, dan mencatat log. Buat unit file:
[Unit]
Description=Flask App - Gunicorn
After=network.target
[Service]
User=www-data
Group=www-data
WorkingDirectory=/opt/flask-app
Environment="FLASK_ENV=production"
EnvironmentFile=/etc/flask-app.env
ExecStart=/opt/flask-app/.venv/bin/gunicorn \
--workers 3 \
--bind 127.0.0.1:8000 \
--access-logfile - \
--error-logfile - \
"app:create_app()"
Restart=always
RestartSec=3
[Install]
WantedBy=multi-user.targetAktifkan dan jalankan:
sudo systemctl daemon-reload
sudo systemctl enable --now flask-app
systemctl status flask-appRestart=always berarti Gunicorn dibangkitkan ulang otomatis jika crash. EnvironmentFile=/etc/flask-app.env memuat secret (episode 8) tanpa di-commit. Untuk deploy update, cukup restart: sudo systemctl restart flask-app.
Kita sempurnakan konfigurasi dari episode 20 dengan static files dan healthcheck:
server {
listen 443 ssl http2;
server_name flask.example.com;
# TLS (episode 20)
ssl_certificate /etc/letsencrypt/live/flask.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/flask.example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
# static files langsung dari Nginx - cepat, tanpa menyentuh Python
location /static/ {
alias /opt/flask-app/static/;
expires 7d;
add_header Cache-Control "public";
}
location / {
proxy_pass http://127.0.0.1:8000;
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_read_timeout 60s;
}
}Dua hal penting: location /static/ membuat Nginx melayani CSS/JS/gambar langsung dari disk — Python tidak terlibat sama sekali, jauh lebih cepat (episode 7). Dan healthcheck sederhana bisa ditambahkan lewat endpoint /healthz yang dipantau monitoring.
uWSGI adalah alternatif yang lebih tua dan lebih "berat":
pip install uwsgi
uwsgi --http 127.0.0.1:8000 --module app:create_app() --master \
--processes 4 --threads 2Keunggulan uWSGI: konfigurasi sangat fleksibel dan performa tinggi untuk workload khusus. Kekurangannya: kompleksitas setup lebih tinggi dan binary native perlu dikompilasi. Untuk mayoritas proyek Flask, Gunicorn lebih mudah dan cukup — pilih uWSGI hanya jika kebutuhan spesifiknya terpenuhi (misal integrasi khusus, embedded config).
Setelah semua tersusun, uji dari luar:
curl -I https://flask.example.com
curl https://flask.example.com/healthz
systemctl status flask-appPeriksa tiga hal: TLS berfungsi (200 + HTTPS), aplikasi merespons via Nginx, dan service systemd aktif. Jika aplikasi tidak muncul, lihat log: journalctl -u flask-app -f dan sudo tail -f /var/log/nginx/error.log.
--bind 0.0.0.0 di Gunicorn tanpa Nginx: aplikasi terpapar langsung ke publik tanpa TLS/filter — selalu 127.0.0.1 + Nginx.ProxyFix: IP client salah, url_for menghasilkan http:// (episode 20).2×CPU+1, lalu ukur./static/.EnvironmentFile yang tidak di-commit.Pada episode 22 ini, kalian telah mendeploy Flask ke produksi.
Inti yang harus dibawa pulang:
127.0.0.1, panggil factory "app:create_app()".sync cukup untuk mayoritas; gthread/gevent untuk workload I/O-heavy.Restart=always) dan memuat env secret.Di episode 23 selanjutnya, kita otomatiskan seluruh alur: Docker & CI/CD — multi-stage image, docker-compose, healthcheck, dan pipeline GitHub Actions dari test sampai deploy. Sampai jumpa di episode 23!