Əvvəlki mövzular hissələri ayrıca izah edirdi: Dockerfile, PostgreSQL və Redis ilə compose, gunicorn və statik fayllar və HTTPS ilə Nginx. Bu mövzu onları bir təkrarlanan buraxılış prosesində birləşdirir: mühitdən konfiqurasiya, proxy arxasında təhlükəsizlik parametrləri, WhiteNoise, çoxmərhələli image, miqrasiyaların buraxılışda bir dəfə işlədiyi compose faylı, health probları, təhlükəsiz sxem dəyişiklikləri və geri qaytarma ilə CI/CD konveyeri.

Dev-dən prod-a

Brauzer, Nginx, web konteyneri, PostgreSQL və birdəfəlik migrate işi
Brauzer, Nginx, web konteyneri, PostgreSQL və birdəfəlik migrate işi
MəsələİnkişafProduction
DEBUGTrueFalse
Sirlərsettings.py-damühit dəyişənləri, heç vaxt image-də yox
ServerrunserverNginx arxasında gunicorn
Statik fayllarrunserver verircollectstatic + WhiteNoise
BazaSQLite və ya lokal konteyneradlandırılmış volume ilə PostgreSQL
HTTPSyoxdurTLS-i Nginx (və ya başqa proxy) bağlayır
Miqrasiyalarəl iləburaxılışda bir iş

Qərarların çoxunun arxasındakı qayda: image kodu, mühit konfiqurasiyanı, volume isə datanı saxlayır. Eyni image staging-də və production-da işləməlidir, yalnız mühit dəyişir.

Mühitdən konfiqurasiya

Mühitdən asılı bütün dəyərləri mühit dəyişənlərindən oxuyun. Çatışmayan sirr prosesi dərhal dayandırmalıdır: os.environ["NAME"] KeyError verir və production-da məhz bu lazımdır. İstəyə bağlı dəyərlər üçün kiçik köməkçilər yazın, çünki bool("False") True-dur və "0" sətri də doğru sayılır:

python
import os

def env_bool(name: str, default: bool = False) -> bool:
    raw = os.environ.get(name, str(default))
    return raw.strip().lower() in {"1", "true", "yes", "on"}

def env_list(name: str, default: str = "") -> list[str]:
    raw = os.environ.get(name, default)
    return [x.strip() for x in raw.split(",") if x.strip()]

os.environ["DJANGO_DEBUG"] = "0"
os.environ["DJANGO_ALLOWED_HOSTS"] = "example.com, www.example.com"
print("DEBUG =", env_bool("DJANGO_DEBUG"))
print("ALLOWED_HOSTS =", env_list("DJANGO_ALLOWED_HOSTS"))
print("USE_TLS =", env_bool("USE_TLS", True))
text
DEBUG = False
ALLOWED_HOSTS = ['example.com', 'www.example.com']
USE_TLS = True

Parametrləri base.py, dev.py və prod.py-ə bölün (parametrlər və mühitlər); production bazanı import edib üstündən yazır:

python
# config/settings/prod.py
import os

import dj_database_url

from .base import *  # noqa: F401,F403
from .env import env_bool, env_list

SECRET_KEY = os.environ["DJANGO_SECRET_KEY"]  # KeyError = fail fast
DEBUG = env_bool("DJANGO_DEBUG", False)
ALLOWED_HOSTS = env_list("DJANGO_ALLOWED_HOSTS")
CSRF_TRUSTED_ORIGINS = env_list("DJANGO_CSRF_TRUSTED_ORIGINS")

DATABASES = {
    "default": dj_database_url.config(
        conn_max_age=600,  # reuse connections
        conn_health_checks=True,  # drop dead ones
    )
}

.env faylı yalnız serverdə durur (icazə 600, git-də və image-də yox) və konteynerlərə env_file ilə verilir:

bash
# .env (on the server, chmod 600, never in git or in the image)
DJANGO_SECRET_KEY=replace-with-a-long-random-value
DJANGO_DEBUG=0
DJANGO_ALLOWED_HOSTS=shop.example.com,localhost
DJANGO_CSRF_TRUSTED_ORIGINS=https://shop.example.com
DATABASE_URL=postgres://shop:change-me@db:5432/shop
POSTGRES_USER=shop
POSTGRES_PASSWORD=change-me
POSTGRES_DB=shop

⚠️ Konteynerin health check-i http://localhost:8000/... çağırırsa, localhost-u DJANGO_ALLOWED_HOSTS-a əlavə edin; əks halda Django öz probunuza 400 DisallowedHost cavab verir.

HTTPS və deploy yoxlamaları