SSD Nodes Learn 🎉 VPS $5.50/aydan başlayan
Rehberler Matt ConnorYazan Matt Connor · Güncellendi 2026-08-13

Küçük VPS için Django ve Flask bellek karşılaştırması

1 GB ve 2 GB RAM kapasiteli sunucularda Django ve Flask performansını inceleyin. Gunicorn worker başına düşen gerçek bellek tüketimi ve sunucu kapasitesi verilerine ulaşın.

Küçük bir VPS üzerinde Django ve Flask maliyeti

Küçük bir VPS üzerinde Django ve Flask karşılaştırması, öncelikle bir bellek kullanımı meselesidir. Django; nesne ilişkisel eşleyicisini (ORM), migrasyon mekanizmasını ve etkinleştirilmişse yönetim panelini, başlattığınız her bir worker sürecine yükler. Flask ise yalnızca bir yönlendirici ve istek nesnesi yükler. 1 GB RAM kapasiteli bir sunucuda bu fark, kaç adet worker sürecinin çalışabileceğini belirler; worker sayısı ise aynı anda kaç isteğe yanıt verebileceğinizi tayin eder.

Bu maliyet, yalnızca Django'nun sunduğu özellikleri yeniden inşa etmediğiniz durumlarda bir dezavantajdır. Kullanıcı hesapları, oturum yönetimi ve yönetim paneli içeren bir uygulama Django gerektirir; her worker için harcanan RAM, yazmak zorunda kalmadığınız kodun bedelidir. Hali hazırda çalışan bir veri deposunun önünde yer alan bir JSON API için ise Flask daha uygundur, çünkü Django'nun sunduğu hazır bileşenlerin hiçbiri bu senaryoda kullanılmayacaktır. Bu bir uyum meselesidir. Aşağıdaki ölçümler, uygulamanızın hangi tarafa daha yakın olduğunu belirlemenize yardımcı olacaktır.

Bir gunicorn worker ne kadar bellek kullanır?

ChartMemory per gunicorn worker, minimal app, three workers with preload on
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
  }
]

Bunlar, Ubuntu 24.04 üzerinde Python 3.12, üç gunicorn worker ve preload açıkken çalışan bir "hello world" uygulaması için yayınlanmış tipik değerlerdir. Kendi içe aktardığınız kütüphaneler bu değerlerin üzerine ekleneceği için bunları bir taban değer olarak kabul edin. Admin paneli etkinleştirilmiş bir Django worker, 96 MB resident bellek kullanırken, bellekteki oransal payı 58 MB seviyesindedir. Bu iki sayı arasındaki fark, bir sonraki bölümün ana konusudur.

Aynı ölçümü kendi sunucunuzda gerçekleştirin.

sudo apt update && sudo apt install -y python3-venv
python3 -m venv /srv/site1/.venv
/srv/site1/.venv/bin/pip install django gunicorn setproctitle

setproctitle paketini kurun. Bu paket mevcut olduğunda, gunicorn süreçlerini gunicorn: master [site1] ve gunicorn: worker [site1] olarak yeniden adlandırır; bu sayede sonraki komutlar worker'ları tahmin yürütmek yerine isimleriyle bulabilir.

pgrep -af gunicorn
ps -o pid,rss,args -p $(pgrep -d, -f 'gunicorn: worker')

rss sütunu, kilobayt cinsinden resident set size değerini gösterir: sürecin o an RAM'de tuttuğu her bellek sayfası. Worker'lar genelinde bu değeri toplamak hatalı bir sonuç verir; çünkü çatallanan (forked) bir worker, sayfaları hem ana süreçle hem de kardeş süreçlerle paylaşır, bu nedenle aynı sayfa birden fazla kez sayılmış olur. Bunun yerine çekirdekten oransal set boyutunu (PSS) isteyin; bu değer, paylaşılan her sayfayı onu eşleyen süreçler arasında paylaştırır.

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

Komutu worker'ların sahibi olan kullanıcıyla veya sudo kullanarak çalıştırın. Bütçeleme yaparken PSS sütununu esas alın, çünkü PSS doğru şekilde toplanabilirken RSS toplanamaz.

Django, django.setup() işleminin yaptıkları nedeniyle daha büyüktür. Uygulama, INSTALLED_APPS içindeki her girdiyi içe aktarır, uygulama kayıt defterini oluşturur ve her model sınıfını, üzerindeki her alan için bir Python nesnesiyle birlikte örneklendirir. django.contrib.admin eklemek, admin otomatik keşfini çalıştırır; bu da her uygulamanın admin modülünü içe aktararak form ve şablon katmanlarını beraberinde getirir. Bir Flask worker ise Werkzeug ve Jinja2'yi içe aktarır ve durur.

Dürüst bir uyarı: Çerçeve (framework) genellikle işin küçük kısmıdır. Bir bulut SDK'sı veya sayısal işlem kütüphanesi içe aktaran bir worker, Django'nun kendisinden daha fazla bellek tüketebilir. Çerçevenin sorun olduğuna karar vermeden önce gerçek uygulamanızı ölçün.

Copy on write ve preload'un sayıyı neden değiştirdiği

Gunicorn ana süreci (master process), worker süreçlerini fork eder. fork() işleminden hemen sonra alt süreç, tüm bellek sayfalarını ebeveyn süreçle paylaşır; çekirdek, yalnızca taraflardan biri yazma işlemi yaptığında sayfayı kopyalar. Dolayısıyla Django model kayıt defterinin sunucuda bir kez mi yoksa dört kez mi var olduğu, fork işleminin hangi tarafında oluşturulduğuna bağlıdır.

preload_app kapalıyken, her worker uygulamayı fork edildikten sonra içe aktarır (import), bu nedenle her biri kendi özel kopyasını oluşturur. Bu özellik açıkken, ana süreç uygulamayı bir kez içe aktarır ve worker süreçleri bu sayfaları miras alır.

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 mantığına aykırı çalışır. Her nesne başlığı bir referans sayacı tutar ve bir nesneye dokunmak o başlığa yazma işlemi yapılmasına neden olur; bu yüzden çöp toplayıcı (garbage collector) heap üzerinde gezinirken paylaşılan sayfalar tek tek geri kopyalanır. gc.freeze(), o ana kadar ayrılmış her şeyi çöp toplayıcının artık ziyaret etmediği kalıcı bir nesil (generation) içine taşır; bu da paylaşılan sayfa sayısını korur. when_ready doğru kancadır (hook), çünkü preload işleminden sonra ve ilk worker fork edilmeden önce çalışır. PSS değerini eklemeden önce ve sonra ölçün, çünkü elde edilecek tasarruf uygulamanızın import aşamasındaki durumunun ne kadar olduğuna bağlıdır.

Preload özelliğinin dağıtım gününde insanları şaşırtan bir maliyeti vardır. systemctl reload, HUP sinyalini gönderir ve gunicorn'un HUP üzerindeki belgelenmiş davranışı, yapılandırmasını yeniden yüklemek ve yeni worker süreçleri başlatmaktır. Uygulama önceden yüklendiğinde (preloaded) kodunuzu tekrar içe aktarmaz; bu nedenle worker süreçleri yeni olsa bile yeni sürümünüz çalışmıyor olur. Kod değişikliğinden sonra systemctl restart kullanın veya eski worker süreçlerinin önce işlerini bitirmesini (drain) istiyorsanız USR2 ve ardından WINCH dizisini kullanın.

1 GB bir VPS gerçekçi olarak kaç worker çalıştırabilir?

ChartWhere a 1 GB VPS goes, typical idle figures before any traffic
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
  }
]

Bu değerler, hiçbir hizmet sunmayan boş bir sunucuya aittir. Uygulama worker'ları için geriye yaklaşık 550 MB kalır ve bu, ilk istek gelmeden önceki durumdur.

Şimdi bu değeri kötümser bir yaklaşımla bölün. Bir istek, çalıştığı süre boyunca bellek tüketir: birkaç bin satır yükleyen bir sorgu seti ve ardından bir şablon oluşturma işlemi. Worker başına tepe bellek kullanımı genellikle boşta çalışma değerinin iki katına yakındır, bu yüzden bütçenizi buna göre iki katı hesaplayın. Django admin paneli ile 58 MB boşta çalışma değeri, bu sunucuda size dört worker sağlar. SQLAlchemy kullanan Flask ile 38 MB değerinde yedi worker elde edersiniz.

Gunicorn'un (2 x cores) + 1 önerisi, CPU'nun kıt kaynak, RAM'in ise bol olduğu varsayımına dayanır. Küçük bir VPS üzerinde bu durum tam tersidir. Paylaşımlı bir vCPU, ana makine yoğun olduğunda size bir çekirdeğin tam gücünü bile vermez; kodunuzu suçlamadan önce bunu anlamak önemlidir: Gürültülü komşudan kaynaklanan CPU steal time, top içerisinde st değeri olarak görünür.

Görünümleriniz (views) çoğunlukla bir veritabanını veya bir upstream API'yi bekliyorsa, thread'ler süreçlerden (processes) daha verimlidir. --worker-class gthread --workers 2 --threads 4, iki worker'ın bellek maliyetiyle sekiz eşzamanlı istek sağlar; çünkü thread'ler, yorumlayıcının ve çerçevenin (framework) yüklenmiş tek bir kopyasını paylaşır. Global interpreter lock (GIL) nedeniyle, thread'ler CPU tüketen bir görünümde performans artışı sağlamaz.

Sunucuya swap alanı ekleyin. Swap alanı olmayan 1 GB bir VPS'te bellek kullanımı anlık yükseldiğinde süreç sonlandırılır (OOM kill), swap dosyası olduğunda ise aynı durum sadece isteğin yavaşlamasına neden olur.

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

Ardından uygulamanın kendisini sınırlayın. Gunicorn birimindeki MemoryMax=600M ayarı, çekirdeğin belleği uygulamanızın cgroup'undan geri almasını sağlar; böylece tüm sunucuda rastgele bir kurban seçilmez ve kontrolden çıkan bir istek SSH oturumunuzun kapanmasına yol açmaz.

Soğuk başlatma ve yeniden başlatma davranışı

ChartImport to ready, minimal app, one shared vCPU
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
  }
]

Başlatma maliyeti iki kez ödenir: her dağıtımda ve bir çökme sonrası gerçekleşen her otomatik yeniden başlatmada. Minimal bir Flask uygulaması yaklaşık 90 ms içinde hazır olurken, admin paneli etkinleştirilmiş bir Django uygulaması aynı paylaşımlı vCPU üzerinde yaklaşık 720 ms sürmektedir. Her iki değer de tipik yayınlanmış verilerdir. Kendi ölçümünüzü yapın, çünkü bağımlılıklarınız süreyi belirleyen ana unsurdur.

cd /srv/site1
DJANGO_SETTINGS_MODULE=site1.settings /srv/site1/.venv/bin/python -X importtime -c "import django; django.setup()" 2>&1 | tail -20

Son satırlar, kümülatif mikrosaniye cinsinden en yavaş içe aktarmaları (import) listeler. Flask için aynı bayrağı modülünüz üzerinde çalıştırın: python -X importtime -c "import app".

Preload etkinleştirildiğinde, ana süreç (master) bu maliyeti bir kez öder ve çatallanan (forked) her işçi (worker) anında başlar. Preload kapalıyken, her işçi bu maliyeti öder ve gunicorn'un timeout parametresi hem başlatma sürecini hem de isteği kapsar. timeout saniye içinde yanıt vermeyen bir işçi öldürülür ve yenisiyle değiştirilir; bu nedenle yavaş bir paylaşımlı vCPU üzerinde çalışan ağır bir uygulama, hiçbir isteğe yanıt veremeyen bir yeniden başlatma döngüsüne girebilir. Günlük kaydı şunu belirtir:

[2026-08-09 09:14:02 +0000] [981] [CRITICAL] WORKER TIMEOUT (pid:1004)

Veritabanı migrasyonları uygulama başlatma kodunda değil, birim (unit) içinde yer almalıdır. ExecStartPre, herhangi bir işçi oluşmadan önce bir kez çalışır. migrate komutunu uygulamanızın içine yerleştirmek, üç işçinin aynı şema kilidi üzerinde birbirleriyle yarışmasına neden olur.

Dağıtım yapısı neredeyse aynıdır

Süreç yöneticisi

Her iki çerçeve de gunicorn altında çalışır ve gunicorn da systemd tarafından yönetilir.

[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.target

RuntimeDirectory=site1, başlangıçta /run/site1 dosyasını oluşturur ve durdurulduğunda siler; böylece soket yolu her zaman doğru sahiplik bilgisiyle mevcut olur. Gunicorn yapılandırmasındaki umask = 0o007 satırı, bu soketin www-data grubu tarafından yazılabilir olmasını sağlar; nginx bu sayede sokete erişebilir.

Flask birimi, tek bir satırı değiştirilmiş aynı dosyadır: ExecStart=... gunicorn -c /srv/site1/gunicorn.conf.py app:app ve ExecStartPre yoktur. app:app argümanı önce modülü, sonra çağrılabilir öğeyi belirtir; bu nedenle Failed to find attribute 'app' in 'app'. hatası, modülünüzün bu isimde bir değişken tanımlamadığı anlamına gelir. Zamanlanmış görevler aynı kalıba uyar ve bir systemd zamanlayıcısı, Django yönetim komutu için cron yerine geçer ve bu boyuttaki bir sunucuya görev kuyruğu ekleme ihtiyacını ortadan kaldırır.

Statik dosyalar

DEBUG = False ile çalışan Django hiçbir statik dosya sunmaz. STATIC_ROOT ayarını yapın, python manage.py collectstatic komutunu çalıştırın ve web sunucusunu çıktı dizinine yönlendirin. Bu adımı atlarsanız, yönetici paneli stiller yüklenmeden açılır ve günlük kayıtları Not Found: /static/admin/css/base.css hatalarıyla dolar.

Bunları sunmak için iki makul yol vardır. Bir nginx alias bloğu uygulamanıza ek yük getirmez. Middleware olarak eklenen WhiteNoise, dosyaları worker üzerinden sunar ve nginx bloğu yazma zahmetinden kurtarır; ancak dosya başına küçük bir worker süresi maliyeti vardır. Flask, geliştirme aşamasında kendi static/ klasörünü sunar; üretim ortamında ise aynı nedenle proxy'yi bu klasöre yönlendirirsiniz.

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;
    }
}

Herhangi bir proxy arkasında, Django'ya orijinal isteğin HTTPS olduğu bildirilmelidir; aksi takdirde siteler arası istek sahteciliği (CSRF) kontrolleri kendi formlarınızı reddeder.

ALLOWED_HOSTS = ["example.com"]
CSRF_TRUSTED_ORIGINS = ["https://example.com"]
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")

Eğer sunucuda halihazırda container'lar çalışıyorsa, birden fazla Docker Compose uygulamasının önündeki Traefik, her site için ayrı bir dosya yerine container üzerindeki etiketleri kullanarak aynı işi yapar.

Hangi veritabanı

SQLite, saniyede birkaç yazma işleminin gerçekleştiği tek bir uygulama sunucusu için oldukça yeterlidir ve tüm bir daemon sürecini bellek bütçesinden çıkarır. Write ahead logging (WAL) özelliğini açın ve sürücüye bir meşguliyet zaman aşımı (busy timeout) değeri atayın.

DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.sqlite3",
        "NAME": BASE_DIR / "db.sqlite3",
        "OPTIONS": {
            "timeout": 20,
            "init_command": "PRAGMA journal_mode=WAL;",
        },
    }
}

Bu iki seçenek olmadan, iki worker aynı anda yazmaya çalıştığında django.db.utils.OperationalError: database is locked hatasıyla karşılaşırsınız; çünkü varsayılan günlük modu yazma sırasında okuyucuları engeller ve varsayılan zaman aşımı süresi neredeyse anında pes eder. SQLite'ın doğru seçenek olmaktan çıktığı durumları da içeren daha kapsamlı tartışma bir VPS üzerinde üretim ortamında SQLite çalıştırma başlığında yer almaktadır.

Aynı 1 GB'lık sunucuda PostgreSQL çalıştırmak, yukarıdaki bütçeden 120 MB harcar ve her kalıcı bağlantı için bir arka uç süreci ekler. Django'nun CONN_MAX_AGE ayarı, worker başına bir bağlantıyı açık tutar; yani dört worker, dört arka uç süreci demektir. Bu genellikle iyi bir takastır. Worker sayısını belirlemeden önce bunu hesaplayın. Veritabanını uygulamanın yanında bir container içinde tutmayı tercih ederseniz, bir VPS üzerinde Docker çalıştırma işlemi, bu boyutta önemli olan daemon ve her bir container ek yükü nedeniyle daha yüksek bir taban maliyetiyle aynı takası sunar.

Nelerin satın alındığı ve maliyeti

Django'nun beraberinde getirdiği ekstra megabaytlar, halihazırda var olan ve birlikte çalışan bileşenlerin bir listesidir: migration desteğine sahip ORM, oturum ve kimlik doğrulama sistemi, yetkilendirme modeli, CSRF korumalı form katmanı, şablon motoru, yönetim komutları ve admin paneli. Admin paneli, insanların değerini yeterince anlamadığı bir parçadır. Modelleriniz için arama ve filtreleme özelliklerine sahip, INSTALLED_APPS içinde tek bir satırla çalışan bir veritabanı düzenleyicisidir.

Flask bunun tam tersidir. Yönlendirme (routing), bir istek nesnesi, Jinja2 şablonları ve bir yapılandırma nesnesi elde edersiniz. Geri kalan her şey sizin seçiminizdir; uygulama küçükken bu gerçek bir değerdir çünkü hiçbir ORM'yi import etmediğiniz sürece yüklenmez.

Tehlike, orta yoldadır. Modeller için SQLAlchemy, migration işlemleri için Alembic, oturumlar için Flask-Login, formlar ve CSRF için Flask-WTF ve arka ofis için bir admin eklentisi eklediğinizde, Django'nun bellek profiline sahip ancak onun tutarlılığından yoksun bir yapı kurmuş olursunuz. Her parçanın kendi sürüm döngüsü ve uygulamanın nasıl kurgulanması gerektiğine dair kendi görüşü vardır. İşte bu nokta, RAM kullanımı ve yükseltmelere harcadığınız saatler açısından Django'nun daha ekonomik bir çözüm olduğu noktadır.

Django ve Flask: karar kuralı

Uygulamanızda kullanıcı hesapları, düzenlenebilir içerik, sürekli değişen bir şema ve birilerinin gerçekten kullanacağı bir yönetim paneli varsa Django kullanın. Uygulamanız halihazırda var olan bir veri deposu üzerinde bir JSON arayüzü ise veya içinde HTML barındırmayan bir webhook alıcısı ise Flask tercih edin.

Karar veremediğiniz durumlarda yazılı bir liste hazırlayın. Flask içerisinde ihtiyaç duyduğunuz özellik setine ulaşmak için kurmanız gereken tüm paketleri listeleyin. Eğer bu liste bir ORM ve bir veritabanı göç (migration) aracı içeriyorsa, aslında zaten Django'yu seçmişsiniz demektir; sadece oraya daha yavaş ulaşmak için ekstra çaba harcıyorsunuzdur.

Küçük donanımlar üzerinde Flask'ı gerçekten avantajlı kılan bir durum vardır: tek bir sunucuda birden fazla küçük servis çalıştırmak. Her Flask servisi, kendi birimi altında çalışan düşük maliyetli bir süreçtir. 1 GB RAM kapasiteli bir VPS üzerinde üç adet Django sitesi çalıştırmak, çerçevenin üç kopyasının aynı anda bellekte yer kaplaması demektir ve yukarıdaki hesaplama geçerliliğini yitirir. Eğer hesaplamalar sürekli yetersiz kalıyorsa, genellikle en dürüst çözüm daha üst bir plana geçmektir; bir VPS'in aylık gerçek maliyeti konusu, çalışan bir uygulamayı baştan yazmaktan çok daha kısa bir tartışmadır.

Görülecek dizgilerle ilgili hata modları

Worker'lar kayboluyor ve geri geliyor. Gunicorn [ERROR] Worker (pid:1234) was sent SIGKILL! Perhaps out of memory? çıktısı verir. Bu çıktı, hem zaman aşımı sinyalini kaçıran bir worker'ı öldürdüğünde hem de çekirdek (kernel) süreci sonlandırdığında oluşur. İkisini birbirinden ayırmak için dmesg -T | grep -i "killed process" kullanın. Oradaki bir satır bellek sorununa işaret eder; bu durumda worker sayısını azaltın veya swap alanı ekleyin.

Her sayfa 400 hatası döndürüyor ve günlükte Invalid HTTP_HOST header yazıyor. Tam mesaj Invalid HTTP_HOST header: 'example.com'. You may need to add 'example.com' to ALLOWED_HOSTS. şeklindedir. Django, isteği kodunuza ulaşmadan reddeder; çünkü ALLOWED_HOSTS boştur veya proxy'nin Host içinde ilettiği ismi içermiyordur.

Formlar Origin checking failed hatası veriyor. Sayfa, CSRF doğrulamasının başarısız olduğunu belirtir. Bu durum, TLS sonlandırma yapan bir proxy arkasında gerçekleşir: uygulama düz HTTP görür, bir http:// kaynağı oluşturur ve bunu https:// üzerinden gelen istekle karşılaştırır. SECURE_PROXY_SSL_HEADER ve CSRF_TRUSTED_ORIGINS ayarlarını yapın ve proxy'nin X-Forwarded-Proto başlığını gerçekten gönderdiğini doğrulayın.

nginx anında 502 hatası döndürüyor. Hata günlüğü nedeni belirtir: connect() to unix:/run/site1/gunicorn.sock failed (2: No such file or directory), birimin çalışmadığı anlamına gelir; (13: Permission denied) ise soketin var olduğu ancak nginx'in onu açamadığı anlamına gelir; bu durum umask ve grup ayarıyla ilgilidir.

Yönetim panelinde stil yok. collectstatic çalıştırılmamıştır veya alias yolu STATIC_ROOT ile eşleşmiyordur. Erişim günlüğü, /static/admin/ altında 404 hatalarını gösterir.

Düşük yük altında yazma işlemleri başarısız oluyor. SQLite'tan gelen database is locked hatası, WAL modunun kapalı olduğu veya iki worker aynı anda yazmaya çalıştığında busy timeout süresinin çok kısa olduğu anlamına gelir.

FAQ

Django 1 GB RAM kapasiteli bir VPS için çok mu ağır?

Hayır. Django; az sayıda worker, ön tarafta nginx ve arka tarafta SQLite ile 1 GB RAM üzerinde rahatlıkla çalışır. PostgreSQL'i varsayılan ayarlarıyla, bir önbellek mekanizmasını, bir arka plan işleyicisini ve Docker'ı aynı sunucuya eklediğinizde kaynaklar kısıtlı hale gelir. Bir worker sürecinin kapladığı bellek miktarını (proportional set size) ölçün, istek yoğunluklarını karşılamak için bu değeri ikiyle çarpın ve işletim sistemi ile veritabanı sonrası kalan toplam bellek miktarıyla karşılaştırın.

Tek bir vCPU üzerinde kaç adet gunicorn worker çalıştırmalıyım?

Üç worker ile başlayın ve performansı ölçün. Küçük bir VPS üzerinde genellikle kısıtlayıcı faktör bellektir; bu nedenle işletim sistemi ve veritabanı sonrası kalan RAM miktarını, bir worker'ın kapladığı bellek miktarının iki katına bölerek hesaplayın. Eğer view'larınız çoğunlukla bir veritabanı veya dış API yanıtı bekliyorsa, gthread worker sınıfına geçiş yapın. Bu sınıfta az sayıda worker ve her birinde birden fazla thread kullanın; çünkü thread'ler framework'ün tek bir yüklü kopyasını paylaşır ve ek süreçlere (process) kıyasla çok daha az bellek tüketir.

PostgreSQL kullanmalı mıyım, yoksa SQLite yeterli mi?

Düşük yazma hızına sahip tek bir uygulama sunucusu için SQLite yeterlidir ve veritabanı daemon'unu bellek bütçesinden çıkarmanızı sağlar. Write ahead logging özelliğini etkinleştirin ve bir busy timeout süresi belirleyin; aksi takdirde eşzamanlı yazma işlemleri database is locked hatasıyla başarısız olur. Birden fazla makinenin yazma yapması gerektiğinde veya SQLite'ın sunmadığı eşzamanlı yoğun yazma işlemleri ya da rol bazlı erişim denetimi gibi özelliklere ihtiyaç duyduğunuzda PostgreSQL'e geçiş yapın.

gunicorn yerine uvicorn çalıştırmalı mıyım?

Yalnızca async view'larınız varsa ve bekleyeceğiniz gerçek bir işlem mevcutsa bunu yapın. Flask bir WSGI uygulamasıdır; bu nedenle bir async view, worker thread'i içinde yeni bir event loop'ta çalışır ve bir sonraki istek başlamadan önce biter, bu da ek bir eşzamanlılık sağlamaz. Django async view'larının verimli çalışması için bir ASGI sunucusuna ihtiyaç vardır. Güncel uvicorn sürümleri, gunicorn worker sınıfını ayrı bir pakete taşıdı; bu yüzden eski bir eğitimden worker sınıfı bayrağı kopyalamak yerine güncel uvicorn dokümantasyonunu inceleyin.

Worker sürecim neden hiçbir hata izi (traceback) bırakmadan kayboldu?

Çekirdeğin (kernel) out of memory killer mekanizması tarafından sonlandırılan bir süreç SIGKILL sinyali alır ve kapanırken herhangi bir kayıt tutamaz; bu nedenle uygulama günlüğünüz aniden kesilir. Gunicorn bu boşluğu fark eder ve Worker (pid:1234) was sent SIGKILL! Perhaps out of memory? mesajını yazdırır. Durumu dmesg -T | grep -i "killed process" komutuyla doğrulayın. Çözüm, worker sayısını azaltmak veya bir swapfile oluşturmaktır; böylece bellek kullanımı ani yükseldiğinde süreç ölmek yerine isteği yavaşlatarak yanıt verir.

#django#flask#python#gunicorn#deployment#vps