छोटे VPS पर Django बनाम Flask: RAM और performance तुलना
1 GB या 2 GB RAM वाले VPS पर Django और Flask की वास्तविक लागत जानें। प्रति Gunicorn worker मेमोरी खपत और एक छोटे सर्वर पर अधिकतम workers की संख्या का सटीक विश्लेषण यहाँ देखें।
छोटे VPS पर Django और Flask की लागत
छोटे VPS पर Django बनाम Flask का चुनाव मुख्य रूप से memory का प्रश्न है। Django अपने object relational mapper (ORM), migration machinery और, यदि आप इसे सक्षम करते हैं, तो admin site को हर उस worker process में लोड करता है जिसे आप शुरू करते हैं। Flask केवल एक router और एक request object लोड करता है। 1 GB के सर्वर पर यह अंतर तय करता है कि कितने workers चल सकते हैं, और worker की संख्या यह तय करती है कि आप एक साथ कितनी requests को serve कर सकते हैं।
यह लागत Django के लिए केवल तभी नुकसानदेह है यदि आप उन सुविधाओं का उपयोग नहीं करते जो वह प्रदान करता है। user accounts, sessions और admin panel वाले app के लिए Django बेहतर है: प्रति worker RAM की खपत उस कोड की कीमत है जिसे आपको खुद नहीं लिखना पड़ता। यदि आप किसी मौजूदा datastore के सामने केवल एक JSON API चला रहे हैं, तो Flask बेहतर है, क्योंकि वहां Django की अधिकांश सुविधाएं अनावश्यक रूप से लोड होंगी। यह उपयोग के आधार पर चुनाव का प्रश्न है। नीचे दिए गए माप आपको यह समझने में मदद करेंगे कि आपका app किस श्रेणी में आता है।
एक gunicorn worker कितनी memory का उपयोग करता है?
The data behind this chart
[
{
"label": "Bare Python 3.12 process",
"rss_mb": 14,
"pss_mb": 9
},
{
"label": "Flask, one route",
"rss_mb": 42,
"pss_mb": 26
},
{
"label": "Flask + SQLAlchemy",
"rss_mb": 58,
"pss_mb": 38
},
{
"label": "Django, admin disabled",
"rss_mb": 78,
"pss_mb": 47
},
{
"label": "Django, admin enabled",
"rss_mb": 96,
"pss_mb": 58
}
]ये Ubuntu 24.04 और Python 3.12 पर, तीन gunicorn workers और preload चालू होने की स्थिति में, प्रत्येक प्रकार के 'hello world' app के लिए प्रकाशित सामान्य आंकड़े हैं। इन्हें न्यूनतम सीमा (floor) मानें, क्योंकि आपके अपने imports इनके ऊपर जुड़ते हैं। Django का एक worker, जिसमें admin सक्षम है, 96 MB resident memory दिखाता है, जबकि memory में उसका आनुपातिक हिस्सा (proportional share) 58 MB है। इन दो संख्याओं के बीच का अंतर ही अगले भाग का मुख्य विषय है।
अपने स्वयं के सर्वर पर इसी प्रकार का मापन करें।
sudo apt update && sudo apt install -y python3-venv
python3 -m venv /srv/site1/.venv
/srv/site1/.venv/bin/pip install django gunicorn setproctitlesetproctitle को install करें। इसके मौजूद होने पर, gunicorn अपनी प्रक्रियाओं (processes) का नाम बदलकर gunicorn: master [site1] और gunicorn: worker [site1] कर देता है, जिससे अगली commands workers को नाम से ढूँढ पाती हैं, न कि अनुमान लगाकर।
pgrep -af gunicorn
ps -o pid,rss,args -p $(pgrep -d, -f 'gunicorn: worker')rss कॉलम किलोबाइट में resident set size है: यह वह memory है जिसे process वर्तमान में RAM में रखती है। सभी workers के लिए इसे जोड़ने पर एक बहुत बड़ी संख्या प्राप्त होती है, क्योंकि एक forked worker अपने parent और siblings के साथ pages साझा करता है, इसलिए एक ही page कई बार गिना जाता है। इसके बजाय kernel से proportional set size (PSS) मांगें, जो प्रत्येक साझा page को उसे map करने वाली प्रक्रियाओं के बीच विभाजित कर देता है।
for pid in $(pgrep -f 'gunicorn: worker'); do awk -v p="$pid" '/^Pss:/ {printf "%s %d MB\n", p, $2/1024}' /proc/$pid/smaps_rollup; doneइसे उस user के रूप में चलाएं जो workers का स्वामी है, या sudo के साथ चलाएं। PSS वह कॉलम है जिसके आधार पर बजट बनाना चाहिए, क्योंकि PSS का योग सही होता है जबकि RSS का नहीं।
Django का आकार बड़ा होता है क्योंकि django.setup() ऐसा करता है। यह INSTALLED_APPS में प्रत्येक entry को import करता है, application registry बनाता है, और प्रत्येक model class को उसके हर field के लिए एक Python object के साथ instantiate करता है। django.contrib.admin को जोड़ने पर admin autodiscovery चलता है, जो प्रत्येक app के admin module को import करता है और उसके पीछे forms तथा template layers को भी खींच लाता है। एक Flask worker केवल Werkzeug और Jinja2 को import करता है और रुक जाता है।
एक ईमानदार चेतावनी: framework अक्सर छोटा हिस्सा होता है। जो worker cloud SDK या किसी numeric library को import करता है, उसका भार Django से कहीं अधिक होता है। यह तय करने से पहले कि समस्या framework में है, अपने वास्तविक app को मापें।
Copy on write, और preload संख्या को क्यों बदलता है
Gunicorn का master process workers को fork करता है। fork() के तुरंत बाद, child process parent के साथ हर memory page साझा करता है, और kernel केवल तभी page की copy बनाता है जब कोई एक पक्ष उस पर write करता है। इसलिए, Django का model registry सर्वर पर एक बार मौजूद है या चार बार, यह इस बात पर निर्भर करता है कि fork के किस तरफ उसे build किया गया था।
जब preload_app बंद (off) होता है, तो प्रत्येक worker fork होने के बाद आपके application को import करता है, इसलिए हर एक अपनी निजी copy बनाता है। जब यह चालू (on) होता है, तो master application को एक बार import करता है और workers उन pages को inherit कर लेते हैं।
import gc
bind = "unix:/run/site1/gunicorn.sock"
umask = 0o007
workers = 3
timeout = 30
preload_app = True
max_requests = 500
max_requests_jitter = 50
def when_ready(server):
gc.freeze()CPython, copy on write के विपरीत काम करता है। हर object header में एक reference count होता है, और किसी object को छूने पर उस header में write होता है, इसलिए जैसे-जैसे garbage collector heap को scan करता है, साझा किए गए pages एक-एक करके copy होते जाते हैं। gc.freeze() अब तक allocate की गई हर चीज़ को एक permanent generation में ले जाता है जिसे collector अब visit नहीं करता, जिससे उन pages में से अधिक साझा (shared) बने रहते हैं। when_ready सही hook है क्योंकि यह preload के बाद और पहले worker के fork होने से पहले चलता है। इसे जोड़ने से पहले और बाद में PSS को मापें, क्योंकि बचत इस बात पर निर्भर करती है कि आपके app का कितना हिस्सा import-time state है।
Preload की एक कीमत ऐसी है जो deploy के दिन लोगों को हैरान कर देती है। systemctl reload, HUP भेजता है, और HUP पर gunicorn का documented व्यवहार अपने configuration को reload करना और नए workers शुरू करना है। जब app preloaded होता है, तो यह आपके code को फिर से import नहीं करता है, इसलिए आपका नया release तब भी नहीं चल रहा होता है भले ही worker processes नए हों। code में बदलाव के बाद systemctl restart का उपयोग करें, या यदि आप चाहते हैं कि पुराने workers पहले drain हों, तो USR2 के बाद WINCH sequence का उपयोग करें।
1 GB VPS पर वास्तव में कितने workers चलाए जा सकते हैं?
The data behind this chart
[
{
"label": "Ubuntu 24.04 base",
"ram_mb": 190
},
{
"label": "nginx",
"ram_mb": 12
},
{
"label": "PostgreSQL, default config",
"ram_mb": 120
},
{
"label": "Headroom you must leave",
"ram_mb": 150
},
{
"label": "Left for gunicorn workers",
"ram_mb": 550
}
]ये आंकड़े बिना किसी traffic वाले idle server के हैं। application workers के लिए लगभग 550 MB RAM शेष बचती है, और यह भी तब है जब पहली request अभी आई भी न हो।
अब विभाजन करें, और निराशावादी (pessimistic) दृष्टिकोण अपनाएं। एक request चलते समय memory का उपयोग करती है: जैसे कि कुछ हजार rows load करना और फिर template render करना। प्रति worker peak memory उपयोग अक्सर idle figure के दोगुने के करीब होता है, इसलिए उसी के अनुसार बजट बनाएं। Django (admin के साथ) 58 MB idle पर इस box पर आपको चार workers देता है। Flask (SQLAlchemy के साथ) 38 MB पर आपको सात workers देता है।
Gunicorn का (2 x cores) + 1 सुझाव यह मानकर चलता है कि CPU दुर्लभ संसाधन है और RAM नहीं। एक छोटे VPS पर यह स्थिति विपरीत होती है। जब host व्यस्त होता है, तो एक shared vCPU आपको एक core से कम काम करने की क्षमता देता है। अपने code को दोष देने से पहले यह समझना जरूरी है: noisy neighbour से CPU steal time top में st figure के रूप में दिखाई देता है।
यदि आपके views मुख्य रूप से database या upstream API पर निर्भर हैं, तो processes के बजाय threads का उपयोग बेहतर है। --worker-class gthread --workers 2 --threads 4 दो workers की memory लागत पर आठ concurrent requests की सुविधा देता है, क्योंकि threads interpreter और framework की एक ही loaded copy साझा करते हैं। Global interpreter lock का अर्थ है कि threads उन views में मदद नहीं करते जो CPU का अधिक उपयोग करते हैं।
Box पर swap जरूर रखें। बिना swap वाला 1 GB VPS memory spike आने पर process को kill कर देता है, जबकि swapfile होने पर वही spike केवल request को धीमा करता है।
sudo fallocate -l 1G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
echo 'vm.swappiness = 10' | sudo tee /etc/sysctl.d/99-swappiness.conf
sudo sysctl --systemइसके बाद application को ही सीमित (cap) करें। Gunicorn unit पर MemoryMax=600M का अर्थ है कि kernel आपके app के cgroup से memory वापस ले लेगा, बजाय इसके कि वह पूरे box में से किसी अन्य process को victim बनाए। इससे एक runaway request के कारण आपका SSH session बंद नहीं होगा।
Cold start और restart का व्यवहार
The data behind this chart
[
{
"label": "Flask, one route",
"cold_start_ms": 90
},
{
"label": "Flask + SQLAlchemy",
"cold_start_ms": 260
},
{
"label": "Django, admin disabled",
"cold_start_ms": 480
},
{
"label": "Django, admin enabled",
"cold_start_ms": 720
}
]Boot की लागत दो बार चुकानी पड़ती है: हर deploy पर, और crash के बाद हर automatic restart पर। एक minimal Flask app लगभग 90 ms में तैयार हो जाता है, और admin enabled के साथ Django उसी shared vCPU पर लगभग 720 ms लेता है। ये दोनों ही सामान्य प्रकाशित आंकड़े हैं। अपने स्वयं के आंकड़े मापें, क्योंकि आपकी dependencies का प्रभाव सबसे अधिक होता है।
cd /srv/site1
DJANGO_SETTINGS_MODULE=site1.settings /srv/site1/.venv/bin/python -X importtime -c "import django; django.setup()" 2>&1 | tail -20अंतिम पंक्तियाँ cumulative microseconds के साथ सबसे धीमे imports को सूचीबद्ध करती हैं। Flask के लिए, अपने module के विरुद्ध यही flag चलाएँ: python -X importtime -c "import app"।
Preload on होने पर, master वह लागत एक बार चुकाता है और हर forked worker तुरंत start हो जाता है। Preload off होने पर, प्रत्येक worker यह लागत चुकाता है, और gunicorn का timeout boot के साथ-साथ request को भी कवर करता है। जो worker timeout seconds के भीतर check-in नहीं करता है, उसे kill करके replace कर दिया जाता है, इसलिए slow shared vCPU पर एक भारी app restart loop में फँस सकती है और कभी कोई request serve नहीं कर पाती। Log में यह दिखता है:
[2026-08-09 09:14:02 +0000] [981] [CRITICAL] WORKER TIMEOUT (pid:1004)Migrations unit में होनी चाहिए, न कि application startup code में। ExecStartPre किसी भी worker के अस्तित्व में आने से पहले एक बार चलता है। अपनी app के अंदर migrate रखने का मतलब है कि तीन workers एक ही schema lock के लिए आपस में प्रतिस्पर्धा (race) कर रहे हैं।
Deployment का ढांचा लगभग एक जैसा है
Process manager
दोनों frameworks gunicorn के अंतर्गत चलते हैं, और gunicorn को systemd चलाता है।
[Unit]
Description=gunicorn for site1
After=network.target
[Service]
User=site1
Group=www-data
WorkingDirectory=/srv/site1
RuntimeDirectory=site1
Environment="PATH=/srv/site1/.venv/bin"
ExecStartPre=/srv/site1/.venv/bin/python manage.py migrate --noinput
ExecStart=/srv/site1/.venv/bin/gunicorn -c /srv/site1/gunicorn.conf.py site1.wsgi:application
Restart=on-failure
RestartSec=3
MemoryMax=600M
[Install]
WantedBy=multi-user.targetRuntimeDirectory=site1 शुरू होने पर /run/site1 बनाता है और बंद होने पर उसे हटा देता है, इसलिए socket path हमेशा सही owner के साथ मौजूद रहता है। gunicorn config में umask = 0o007 लाइन यह सुनिश्चित करती है कि उस socket को www-data group द्वारा लिखा जा सके, जिससे nginx उस तक पहुँच पाता है।
Flask unit वही file है जिसमें केवल एक लाइन बदली गई है: ExecStart=... gunicorn -c /srv/site1/gunicorn.conf.py app:app, और इसमें कोई ExecStartPre नहीं है। app:app argument का अर्थ है module और फिर callable, इसलिए Failed to find attribute 'app' in 'app'. error का मतलब है कि आपके module में उस नाम का कोई variable परिभाषित नहीं है। Scheduled work भी इसी pattern का पालन करती है, और एक systemd timer, Django management command के लिए cron की जगह ले लेता है बिना इस आकार के सर्वर पर task queue जोड़े।
Static files
DEBUG = False के साथ Django कोई static file serve नहीं करता है। STATIC_ROOT सेट करें, python manage.py collectstatic चलाएँ, और web server को output directory की ओर point करें। यदि आप यह step छोड़ देते हैं, तो admin panel बिना styling के load होगा और log Not Found: /static/admin/css/base.css से भर जाएगा।
इन्हें serve करने के दो उचित तरीके हैं। एक nginx alias block आपके app पर कोई भार नहीं डालता। WhiteNoise को middleware के रूप में जोड़ने पर, यह worker से ही file serve करता है और आपको nginx block बनाने की आवश्यकता नहीं पड़ती, लेकिन इसके बदले प्रत्येक file के लिए थोड़ा worker time खर्च होता है। Flask development के दौरान अपना खुद का static/ folder serve करता है, और production में भी आप proxy को उसी कारण से उस folder की ओर point करते हैं।
Reverse proxy
server {
listen 80;
server_name example.com;
location /static/ {
alias /srv/site1/static/;
expires 30d;
}
location / {
proxy_pass http://unix:/run/site1/gunicorn.sock;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}किसी भी proxy के पीछे, Django को यह बताना आवश्यक है कि मूल request HTTPS थी, अन्यथा cross site request forgery (CSRF) checks आपके ही forms को reject कर देंगे।
ALLOWED_HOSTS = ["example.com"]
CSRF_TRUSTED_ORIGINS = ["https://example.com"]
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")यदि सर्वर पर पहले से ही containers चल रहे हैं, तो कई Docker Compose apps के सामने Traefik वही काम करता है, जिसमें प्रति site एक file के बजाय container पर labels का उपयोग होता है।
कौन सा database चुनें
एक single application server के लिए SQLite पूरी तरह से ठीक है, जहाँ write rate प्रति सेकंड कुछ ही हो, और यह memory budget से एक पूरे daemon को हटा देता है। Write ahead logging (WAL) को चालू करें और driver को एक busy timeout दें।
DATABASES = {
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": BASE_DIR / "db.sqlite3",
"OPTIONS": {
"timeout": 20,
"init_command": "PRAGMA journal_mode=WAL;",
},
}
}इन दो विकल्पों के बिना, जब दो workers एक साथ write करने की कोशिश करते हैं तो आपको django.db.utils.OperationalError: database is locked का सामना करना पड़ता है, क्योंकि default journal mode write के दौरान readers को block कर देता है और default timeout लगभग तुरंत हार मान लेता है। विस्तृत चर्चा, जिसमें यह भी शामिल है कि SQLite कब सही विकल्प नहीं रहता, VPS पर production में SQLite चलाना में दी गई है।
उसी 1 GB वाले सर्वर पर PostgreSQL चलाने का खर्च ऊपर दिए गए budget में 120 MB है, साथ ही प्रत्येक persistent connection के लिए एक backend process भी। Django का CONN_MAX_AGE प्रति worker एक connection खुला रखता है, इसलिए चार workers का मतलब चार backends है। यह आमतौर पर एक अच्छा सौदा है। worker की संख्या तय करने से पहले इसकी गणना अवश्य कर लें। यदि आप database को app के साथ एक container में रखना पसंद करते हैं, तो VPS पर Docker चलाना समान सौदा है लेकिन इसमें आधारभूत खर्च अधिक है, क्योंकि daemon और प्रत्येक container का overhead इस आकार के सर्वर पर मायने रखता है।
बैटरी (libraries) क्या देती हैं और उनकी कीमत क्या है
Django के अतिरिक्त megabytes उन चीजों की सूची हैं जो पहले से मौजूद हैं और एक साथ काम करती हैं: migrations के साथ ORM, session और authentication system, permission model, CSRF protection के साथ form layer, template engine, management commands, और admin। Admin वह हिस्सा है जिसे लोग कम आंकते हैं। यह आपके models के लिए एक कार्यशील database editor है, जिसमें search और filters की सुविधा है, और यह सब INSTALLED_APPS में केवल एक line लिखने से मिलता है।
Flask इसका विपरीत है। इसमें आपको routing, एक request object, Jinja2 templates और एक config object मिलता है। बाकी सब कुछ आपका चुनाव है, जो तब बहुत उपयोगी होता है जब app छोटा हो, क्योंकि यदि आप ORM import नहीं करते हैं तो वह load भी नहीं होता।
फँसाव तब होता है जब आप बीच का रास्ता चुनते हैं। models के लिए SQLAlchemy, migrations के लिए Alembic, sessions के लिए Flask-Login, forms और CSRF के लिए Flask-WTF, और back office के लिए एक admin extension जोड़ें, तो आपने Django जैसा memory profile वाला एक ऐसा ढांचा तैयार कर लिया है जिसमें Django जैसी एकरूपता (coherence) नहीं है। हर हिस्से का अपना release cycle होता है और अपनी राय होती है कि app को कैसे जोड़ा जाना चाहिए। यही वह बिंदु है जहाँ Django सस्ता विकल्प साबित होता है, RAM के मामले में भी और upgrades पर खर्च होने वाले घंटों के मामले में भी।
Django बनाम Flask: निर्णय का नियम
Django का उपयोग तब करें जब application में accounts, editable content, बार-बार बदलने वाला schema और एक back office हो जिसे वास्तव में कोई इस्तेमाल करेगा। Flask का उपयोग तब करें जब application किसी मौजूदा datastore के ऊपर एक JSON interface हो, या फिर बिना HTML वाला कोई webhook receiver हो।
निर्णय लेने का अंतिम आधार एक लिखित सूची है। उन सभी packages को लिखें जिन्हें आप अपनी जरूरत के feature set तक पहुँचने के लिए Flask में install करेंगे। यदि उस सूची में एक ORM और एक migration tool शामिल है, तो आपने पहले ही Django को चुन लिया है और आप वहाँ धीरे-धीरे पहुँचने के लिए अतिरिक्त मेहनत कर रहे हैं।
एक स्थिति वास्तव में छोटे hardware पर Flask के पक्ष में होती है: एक ही box पर कई छोटी services चलाना। प्रत्येक Flask service अपनी खुद की unit के अंतर्गत एक सस्ती process होती है। 1 GB VPS पर तीन Django sites का मतलब है framework की तीन प्रतियाँ एक साथ memory में रहना, और ऊपर दिया गया गणित काम करना बंद कर देता है। यदि परिणाम बार-बार कम पड़ रहा है, तो एक बड़ा plan अक्सर सही समाधान होता है, और एक VPS की वास्तविक मासिक लागत पर चर्चा करना एक काम कर रहे application को फिर से लिखने से कहीं छोटा काम है।
दिखाई देने वाली स्ट्रिंग्स के साथ विफलता के मोड
Workers गायब हो जाते हैं और वापस आ जाते हैं। Gunicorn [ERROR] Worker (pid:1234) was sent SIGKILL! Perhaps out of memory? प्रिंट करता है। यह इसे तब प्रिंट करता है जब वह ऐसे worker को मार देता है जिसने अपना timeout heartbeat मिस कर दिया हो, और तब भी जब kernel ने process को मार दिया हो। dmesg -T | grep -i "killed process" के साथ दोनों के बीच अंतर करें। वहां एक लाइन का मतलब memory है, इसलिए worker count कम करें या swap जोड़ें।
हर पेज 400 return करता है और लॉग में Invalid HTTP_HOST header लिखा होता है। पूरा संदेश Invalid HTTP_HOST header: 'example.com'. You may need to add 'example.com' to ALLOWED_HOSTS. है। Django आपके कोड तक पहुँचने से पहले ही request को अस्वीकार कर देता है, क्योंकि ALLOWED_HOSTS खाली है या इसमें वह नाम शामिल नहीं है जिसे proxy ने Host में पास किया था।
Forms Origin checking failed के साथ विफल हो जाते हैं। पेज कहता है कि CSRF verification विफल रहा। यह TLS terminating proxy के पीछे होता है: app को plain HTTP दिखाई देता है, वह एक http:// origin बनाता है, और उसकी तुलना उस request से करता है जो https:// पर आई थी। SECURE_PROXY_SSL_HEADER और CSRF_TRUSTED_ORIGINS सेट करें, और पुष्टि करें कि proxy वास्तव में X-Forwarded-Proto भेज रहा है।
nginx तुरंत 502 return करता है। error log कारण बताता है: connect() to unix:/run/site1/gunicorn.sock failed (2: No such file or directory) का मतलब है कि unit चल नहीं रही है, और (13: Permission denied) का मतलब है कि socket मौजूद है लेकिन nginx उसे खोल नहीं सकता, जो कि umask और group सेटिंग है।
admin में कोई styling नहीं है। collectstatic नहीं चला है, या alias पाथ STATIC_ROOT से मेल नहीं खाता है। access log में /static/admin/ के तहत 404s दिखाई देते हैं।
कम लोड पर भी Writes विफल हो जाते हैं। SQLite से database is locked का मतलब है कि WAL बंद है या busy timeout इतना कम है कि दो workers एक ही समय पर write नहीं कर पा रहे हैं।
FAQ
क्या 1 GB VPS के लिए Django बहुत भारी है?
नहीं। कम संख्या में workers, सामने nginx और पीछे SQLite के साथ Django 1 GB RAM पर आसानी से चलता है। जब आप उसी सर्वर पर डिफ़ॉल्ट सेटिंग्स के साथ PostgreSQL, एक cache, एक background worker और Docker जोड़ते हैं, तो संसाधन कम पड़ने लगते हैं। एक worker के proportional set size को मापें, request peaks के लिए इसे दोगुना करें, और ऑपरेटिंग सिस्टम तथा डेटाबेस के बाद बची हुई RAM से इसकी तुलना करें।
मुझे एक vCPU पर कितने gunicorn workers चलाने चाहिए?
तीन से शुरुआत करें और मापें। एक छोटे VPS पर आमतौर पर RAM की कमी मुख्य बाधा होती है, इसलिए ऑपरेटिंग सिस्टम और डेटाबेस के बाद बची हुई RAM को एक worker के proportional set size के दोगुने से विभाजित करें। यदि आपके views मुख्य रूप से डेटाबेस या upstream API पर निर्भर हैं, तो कम workers और प्रत्येक में कई threads के साथ gthread worker class का उपयोग करें, क्योंकि threads framework की एक ही loaded copy साझा करते हैं और अतिरिक्त processes की तुलना में बहुत कम RAM लेते हैं।
क्या मुझे PostgreSQL की आवश्यकता है, या SQLite ठीक है?
कम write rate वाले एक application server के लिए SQLite ठीक है, और यह memory budget से एक पूरे daemon का भार कम कर देता है। Write ahead logging को सक्षम करें और एक busy timeout सेट करें, अन्यथा concurrent writes database is locked के साथ विफल हो जाएंगे। जब एक से अधिक मशीनों को write करने की आवश्यकता हो, या जब आपको ऐसी सुविधाओं की आवश्यकता हो जो SQLite में नहीं हैं जैसे कि concurrent heavy writers या per-role access control, तब PostgreSQL पर स्विच करें।
क्या मुझे gunicorn के बजाय uvicorn चलाना चाहिए?
केवल तभी जब आपके पास async views हों और प्रतीक्षा करने के लिए कोई वास्तविक कार्य हो। Flask एक WSGI application है, इसलिए एक async view worker thread के भीतर एक नए event loop में चलता है और अगली request शुरू होने से पहले समाप्त हो जाता है, जिससे कोई अतिरिक्त concurrency नहीं मिलती। Django async views को लाभ के लिए एक ASGI server की आवश्यकता होती है। uvicorn के हालिया releases ने अपनी gunicorn worker class को एक अलग package में स्थानांतरित कर दिया है, इसलिए किसी पुराने ट्यूटोरियल से पुरानी worker class flag कॉपी करने के बजाय वर्तमान uvicorn documentation पढ़ें।
मेरा worker बिना किसी traceback के गायब क्यों हो गया?
Kernel के out of memory killer द्वारा kill की गई process को SIGKILL प्राप्त होता है और वह बाहर निकलते समय कुछ भी log नहीं कर सकती, इसलिए आपका application log अचानक रुक जाता है। Gunicorn इस अंतराल को नोटिस करता है और Worker (pid:1234) was sent SIGKILL! Perhaps out of memory? प्रिंट करता है। इसकी पुष्टि dmesg -T | grep -i "killed process" से करें। इसका समाधान कम workers रखना है, या एक swapfile बनाना है ताकि memory spike होने पर process dead होने के बजाय request धीमी हो जाए।