Ortam Değişkenleri ve Konfigürasyon Yönetimi
Bir gün kodunu GitHub'a push ettin. İçinde veritabanı şifresi, API anahtarı ve SMTP kullanıcı adı var. Bir bot 15 dakika içinde repo'nu taradı, şifreleri buldu, veritabanını sildi. Bu senaryo hayal değil — GitHub'da her gün binlerce secret sızıyor ve otomatik tarama botları bunları saniyeler içinde kullanıyor.
Bu derste hardcoded değerlerin neden tehlikeli olduğunu, ortam değişkenlerini nasıl kullanacağını, .env dosyalarıyla konfigürasyon yönetimini ve production-grade config mimarisi kurmayı öğreneceksin. 12-Factor App prensiplerinden pydantic-settings'e kadar, profesyonel bir Python projesinde konfigürasyonun nasıl yapılması gerektiğini kavrayacaksın.
1. Neden Hardcoded Değer Tehlikeli?
🔑 Analoji: Evin Anahtarını Paspasın Altına Koymak
Evin anahtarını paspasın altına koyarsın — "kimse bakmaz" dersin. Ama hırsız ilk oraya bakar çünkü herkes oraya koyar. Kodun içine gömülü API anahtarı da aynı şey. Sen "kim bakacak ki?" dersin, bot ilk oraya bakar.
Gerçek Dünya Senaryosu
# ❌ Asla yapma — hardcoded credentials
import psycopg2
connection = psycopg2.connect(
host="production-db.company.com",
database="users",
user="admin",
password="SuperSecret123!" # Bu satır seni işinden edebilir
)
# ❌ API anahtarı kodda
import requests
API_KEY = "sk-proj-4f8a9b2c..." # OpenAI API key — ayda $10,000 fatura gelebilir
response = requests.get(
"https://api.openai.com/v1/models",
headers={"Authorization": f"Bearer {API_KEY}"}
)Bu kodun Git geçmişine girmesi yeterli. Repo'yu daha sonra public yapsan bile, birisi git log ile geçmişi tarayabilir. Hatta repo private olsa bile — bir takım arkadaşının laptopunun çalınması, bir CI log'unun sızması yeterli.
Hardcoded değerlerin üç temel problemi var:
Güvenlik: Credentials kod tabanına girerse, erişimi olan herkes görür.
Esneklik: Development'ta localhost, production'da gerçek sunucu kullanman gerekir. Her seferinde kodu mu değiştireceksin?
Bakım: 20 dosyada dağılmış veritabanı URL'sini değiştirmek istediğinde ne yapacaksın?
2. os.environ ile Ortam Değişkenleri
İşletim sistemlerinde ortam değişkenleri (environment variables) adlı bir mekanizma vardır. Her process kendi ortam değişkenlerine sahiptir ve bunlar key-value çiftleridir. Python'da os.environ ile erişirsin:
Temel Kullanım
import os
# Ortam değişkeni okuma
db_host = os.environ["DATABASE_HOST"] # Yoksa KeyError fırlatır
db_port = os.environ.get("DATABASE_PORT", "5432") # Yoksa default değer
api_key = os.environ.get("API_KEY") # Yoksa None döner
print(f"DB: {db_host}:{db_port}")# Terminal'den ortam değişkeni tanımlama
export DATABASE_HOST=localhost
export DATABASE_PORT=5432
export API_KEY=sk-test-12345
# Python scriptini çalıştır
python app.py
# Veya tek satırda (sadece bu komut için geçerli)
DATABASE_HOST=localhost API_KEY=sk-test python app.pyos.environ vs os.environ.get
import os
# [] notasyonu — değişken yoksa hata
try:
secret = os.environ["SECRET_KEY"]
except KeyError:
print("SECRET_KEY tanımlı değil!")
# Production'da bu kritik bir hata — uygulama başlamamalı
# .get() — değişken yoksa default
debug = os.environ.get("DEBUG", "false")
port = int(os.environ.get("PORT", "8000"))
workers = int(os.environ.get("WEB_CONCURRENCY", "4"))Kritik değişkenler için os.environ["KEY"] kullan — yoksa uygulama hata versin, sessizce yanlış değerle çalışmasın. Opsiyonel değişkenler için .get() ile default değer ver.
Tüm Ortam Değişkenlerini Listeleme
import os
# Tüm ortam değişkenleri (dict-like obje)
for key, value in os.environ.items():
if "SECRET" not in key and "KEY" not in key: # Gizli olanları atla
print(f"{key}={value}")
# Sadece belirli prefix ile başlayanlar
app_config = {
key: value
for key, value in os.environ.items()
if key.startswith("APP_")
}
print(app_config)
# {'APP_NAME': 'MyApp', 'APP_ENV': 'development', 'APP_DEBUG': 'true'}os.environ bir dictionary değildir ama dictionary gibi davranır (Mapping interface). Anahtarlar ve değerler her zaman string'dir — int, bool gibi tipleri kendin dönüştürmen gerekir.
3. python-dotenv: .env Dosyası
Her seferinde terminal'de export yazmak pratik değil. Özellikle 15-20 ortam değişkenin varsa. python-dotenv kütüphanesi bu sorunu çözer — değişkenleri bir .env dosyasına yazar, uygulama başlarken otomatik yüklersin:
Kurulum ve Kullanım
pip install python-dotenv
# veya
poetry add python-dotenv# .env dosyası — proje kök dizininde
DATABASE_HOST=localhost
DATABASE_PORT=5432
DATABASE_NAME=myapp
DATABASE_USER=dev_user
DATABASE_PASSWORD=dev_password_123
API_KEY=sk-test-1234567890
SECRET_KEY=super-secret-key-for-development
DEBUG=true
LOG_LEVEL=DEBUG
APP_ENV=development
# Yorum satırları desteklenir
# Boş satırlar da sorun değil
# Tırnak kullanabilirsin (opsiyonel)
WELCOME_MESSAGE="Hoş geldiniz!"
MULTILINE_VAR='satır bir\nsatır iki'# app.py
from dotenv import load_dotenv
import os
# .env dosyasını yükle — os.environ'a ekler
load_dotenv()
# Artık normal os.environ ile oku
db_host = os.environ.get("DATABASE_HOST", "localhost")
db_port = int(os.environ.get("DATABASE_PORT", "5432"))
debug = os.environ.get("DEBUG", "false").lower() == "true"
api_key = os.environ["API_KEY"]
print(f"Environment: {os.environ.get('APP_ENV')}")
print(f"Debug: {debug}")
print(f"DB: {db_host}:{db_port}")load_dotenv Davranışı
from dotenv import load_dotenv
# Varsayılan: mevcut dizin ve üst dizinlerde .env arar
load_dotenv()
# Belirli dosya yolu
load_dotenv("/path/to/custom.env")
# ÖNEMLİ: Mevcut ortam değişkenlerini ezmez (override=False default)
# Yani terminal'de export edilen değer .env'den önceliklidir
load_dotenv() # .env'deki DEBUG=true
# Ama: export DEBUG=false yapıldıysa → DEBUG = "false" kalır
# Override: .env'deki değerler her şeyin üstüne yazar
load_dotenv(override=True)Bu davranış bilerek böyle tasarlanmış. .env dosyası default değerleri sağlar, gerçek ortam değişkenleri bunların üstüne yazar. Böylece CI/CD'de veya Docker'da ortam değişkeni tanımlayarak .env'deki değerleri geçersiz kılabilirsin.
.env Dosyası .gitignore'a EKLENMELİ
# .gitignore
.env
.env.local
.env.production
.env.*.local
# Ama .env.example dosyasını commit'le!# .env.example — bu dosyayı commit'le (değerler boş veya örnek)
DATABASE_HOST=localhost
DATABASE_PORT=5432
DATABASE_NAME=
DATABASE_USER=
DATABASE_PASSWORD=
API_KEY=your-api-key-here
SECRET_KEY=generate-a-random-key
DEBUG=true
LOG_LEVEL=DEBUG
APP_ENV=development⚠️ Dikkat: .env dosyasını kesinlikle Git'e commit'leme. Bunun yerine .env.example dosyası oluştur — hangi değişkenlerin gerekli olduğunu gösterir, gerçek değerleri içermez. Yeni bir geliştirici projeyi clone'ladığında .env.example'ı kopyalayıp kendi değerlerini yazar.
4. Konfigürasyon Hiyerarşisi
Gerçek projelerde konfigürasyon birden fazla kaynaktan gelebilir. Hangisi öncelikli? Şu hiyerarşiyi takip et (düşükten yükseğe):
Öncelik (düşük → yüksek):
─────────────────────────
1. Kod içi default değerler → port = int(os.environ.get("PORT", "8000"))
2. .env dosyası → PORT=3000
3. Sistem ortam değişkenleri → export PORT=5000
4. CLI argümanları → python app.py --port 9000import os
import argparse
from dotenv import load_dotenv
# 1. .env'den default'ları yükle
load_dotenv()
# 2. CLI argümanlarını parse et
parser = argparse.ArgumentParser()
parser.add_argument("--port", type=int, default=None)
parser.add_argument("--debug", action="store_true", default=None)
args = parser.parse_args()
# 3. Hiyerarşi: CLI > env > default
port = args.port or int(os.environ.get("PORT", "8000"))
debug = args.debug if args.debug is not None else os.environ.get("DEBUG", "false").lower() == "true"
print(f"Port: {port}, Debug: {debug}")# Sadece .env → PORT=3000 (env'deki değer)
python app.py
# Ortam değişkeni override → PORT=5000
PORT=5000 python app.py
# CLI her şeyi ezer → PORT=9000
python app.py --port 9000Bu hiyerarşi kasıtlıdır. .env geliştirme ortamı için rahat default'lar sağlar. Production'da gerçek ortam değişkenleri bunları ezer. Acil müdahale gerektiğinde CLI argümanıyla her şeyi override edebilirsin.
5. pydantic-settings ile Typed Config
os.environ her şeyi string olarak verir. "true" mu True mu? "8000" mu 8000 mü? Her seferinde dönüşüm yazmak hem zahmetli hem hataya açık. pydantic-settings bu sorunu tip güvenli (type-safe) bir şekilde çözer:
Kurulum
pip install pydantic-settings
# veya
poetry add pydantic-settingsTemel Kullanım
from pydantic_settings import BaseSettings
from pydantic import Field
class DatabaseConfig(BaseSettings):
host: str = "localhost"
port: int = 5432
name: str = "myapp"
user: str = "postgres"
password: str
model_config = {"env_prefix": "DB_"} # DB_HOST, DB_PORT, ...
class AppConfig(BaseSettings):
app_name: str = "My Application"
debug: bool = False
port: int = 8000
secret_key: str
api_key: str
log_level: str = "INFO"
allowed_hosts: list[str] = ["localhost"]
model_config = {"env_file": ".env"}
# Kullanım
config = AppConfig()
print(f"App: {config.app_name}")
print(f"Debug: {config.debug}") # bool — otomatik dönüşüm
print(f"Port: {config.port}") # int — otomatik dönüşüm
print(f"Hosts: {config.allowed_hosts}") # list — JSON parse# .env
APP_NAME=ProductionApp
DEBUG=false
PORT=8000
SECRET_KEY=abc123def456
API_KEY=sk-prod-789
LOG_LEVEL=WARNING
ALLOWED_HOSTS=["api.example.com", "www.example.com"]
DB_HOST=db.example.com
DB_PORT=5432
DB_NAME=production
DB_USER=app_user
DB_PASSWORD=strong_password_herepydantic-settings değerleri otomatik olarak doğru tipe dönüştürür: "false" → False, "8000" → 8000, JSON string → list. Yanlış tip verilirse uygulama başlarken hata verir — yanlış konfigürasyonla çalışmaktansa hiç başlamamak daha iyidir.
Gelişmiş: Nested Config ve Validation
from pydantic_settings import BaseSettings
from pydantic import Field, field_validator
class Settings(BaseSettings):
# Zorunlu alanlar (default yok → env'de olmalı)
database_url: str
secret_key: str
# Opsiyonel alanlar (default var)
debug: bool = False
port: int = Field(default=8000, ge=1024, le=65535) # Port aralığı doğrulama
workers: int = Field(default=4, ge=1, le=32)
log_level: str = "INFO"
model_config = {
"env_file": ".env",
"env_file_encoding": "utf-8",
"case_sensitive": False, # DATABASE_URL = database_url
}
@field_validator("log_level")
@classmethod
def validate_log_level(cls, v):
allowed = {"DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"}
if v.upper() not in allowed:
raise ValueError(f"log_level must be one of {allowed}")
return v.upper()
@field_validator("database_url")
@classmethod
def validate_database_url(cls, v):
if not v.startswith(("postgresql://", "sqlite://", "mysql://")):
raise ValueError("database_url must start with a valid scheme")
return v
# Uygulama başlangıcında
try:
settings = Settings()
print(f"DB: {settings.database_url}")
print(f"Port: {settings.port} (workers: {settings.workers})")
except Exception as e:
print(f"Config hatası: {e}")
# Eksik veya hatalı config varsa uygulama başlamasın
raise SystemExit(1)Bu yaklaşımın güzelliği: uygulama başlarken tüm konfigürasyon doğrulanır. Port numarası geçersizse, veritabanı URL'si yanlış formattaysa, zorunlu bir değişken eksikse — uygulama ilk saniyede sana söyler. Runtime'da "neden bağlanamıyorum?" diye debug yapmazsın.
💡 İpucu: pydantic-settings, .env dosyasını ve gerçek ortam değişkenlerini otomatik okur. Hiyerarşi de doğru çalışır: gerçek ortam değişkenleri .env'deki değerlerin üstüne yazar.
6. 12-Factor App: Config Prensibi
12-Factor App, Heroku'nun kurucularından Adam Wiggins tarafından yazılmış bir manifesto. Modern web uygulamalarının nasıl tasarlanması gerektiğini 12 maddede anlatır. Üçüncü madde doğrudan konfigürasyonla ilgili:
III. Config: Konfigürasyonu ortam değişkenlerinde sakla.
12-Factor Config Prensipleri
1. Konfigürasyon KOD DEĞİLDİR
→ Kod repo'da, config ortam değişkenlerinde
2. Her deploy farklı config almalı
→ Dev, staging, production — aynı kod, farklı config
3. Config ortam değişkenlerinde saklanmalı
→ Dosyada değil, OS ortamında
4. Litmus test: kodun şu an open-source yapılabilir mi?
→ Evet diyorsan config doğru ayrılmış demektir# ✅ 12-Factor uyumlu uygulama
import os
from dotenv import load_dotenv
load_dotenv() # Geliştirmede .env'den, production'da ortam değişkenlerinden
# Aynı kod, farklı ortamda farklı davranır
DATABASE_URL = os.environ["DATABASE_URL"]
REDIS_URL = os.environ.get("REDIS_URL", "redis://localhost:6379")
ALLOWED_ORIGINS = os.environ.get("ALLOWED_ORIGINS", "http://localhost:3000")
# Kesinlikle yapma:
# if environment == "production":
# DATABASE_URL = "postgresql://prod-server/db"
# else:
# DATABASE_URL = "sqlite:///dev.db"
#
# Bu config'i koda gömmektir — 12-Factor ihlali12-Factor'un konfigürasyon prensibini takip ettiğinde, aynı Docker image'ı development'ta, staging'de ve production'da çalıştırabilirsin. Sadece ortam değişkenleri değişir, kod aynı kalır.
7. Development vs Production Ortam Ayrımı
Gerçek projelerde en az iki ortamın olur: development (geliştirme) ve production (canlı). Çoğu zaman bir de staging (test/QA) eklenir:
# .env.development
APP_ENV=development
DEBUG=true
LOG_LEVEL=DEBUG
DATABASE_URL=sqlite:///dev.db
SECRET_KEY=not-so-secret-dev-key
API_BASE_URL=http://localhost:8000
CORS_ORIGINS=http://localhost:3000
EMAIL_BACKEND=console# .env.production (bu dosya sunucuda ortam değişkeni olarak set edilir)
APP_ENV=production
DEBUG=false
LOG_LEVEL=WARNING
DATABASE_URL=postgresql://user:pass@db.server.com:5432/production
SECRET_KEY=<generated-64-char-random-string>
API_BASE_URL=https://api.example.com
CORS_ORIGINS=https://www.example.com
EMAIL_BACKEND=smtpfrom pydantic_settings import BaseSettings
class Settings(BaseSettings):
app_env: str = "development"
debug: bool = False
log_level: str = "INFO"
database_url: str = "sqlite:///dev.db"
secret_key: str = "dev-secret-key"
model_config = {"env_file": ".env"}
@property
def is_production(self) -> bool:
return self.app_env == "production"
@property
def is_development(self) -> bool:
return self.app_env == "development"
settings = Settings()
# Ortama göre davranış
if settings.is_development:
# Geliştirmede detaylı hata mesajları göster
print("Development mode — verbose errors enabled")
if settings.is_production:
# Production'da strict güvenlik
assert settings.secret_key != "dev-secret-key", "Production'da default secret key kullanma!"
assert not settings.debug, "Production'da debug açık olmamalı!"Ortam Bazlı .env Dosyaları
import os
from dotenv import load_dotenv
# Aktif ortamı belirle
env = os.environ.get("APP_ENV", "development")
# Ortama özel .env dosyasını yükle
# Önce genel .env, sonra ortama özel .env.{ortam}
load_dotenv(".env") # Genel default'lar
load_dotenv(f".env.{env}", override=True) # Ortam özel değerler üste yazar
print(f"Running in {env} mode")# Dosya yapısı
project/
├── .env # Genel default'lar (commit'lenir veya lenmez — tartışmalı)
├── .env.development # Development özel (commit'lenebilir — hassas veri yok)
├── .env.production # Production (KESİNLİKLE commit'lenMEZ)
├── .env.example # Şablon (commit'lenir)
├── .gitignore
└── app.pyDevelopment .env dosyası commit'lenebilir çünkü içinde gerçek credential yoktur — localhost, dev-key gibi değerler güvenlik riski taşımaz. Ama production .env asla repo'ya girmemeli.
8. Yaygın Hatalar ve Tuzaklar
Hata 1: Boolean Dönüşüm
import os
# ❌ YANLIŞ — "false" string'i truthy'dir!
debug = bool(os.environ.get("DEBUG", ""))
# DEBUG=false → bool("false") → True (!)
# ✅ DOĞRU — explicit karşılaştırma
debug = os.environ.get("DEBUG", "false").lower() in ("true", "1", "yes")
# DEBUG=false → "false" in ("true", "1", "yes") → False ✓Python'da boş string hariç her string truthy'dir. bool("false") → True klasik bir tuzaktır. pydantic-settings bu dönüşümü otomatik ve doğru yapar — elle çevirme yerine onu kullan.
Hata 2: Secrets Loglama
import logging
logger = logging.getLogger(__name__)
# ❌ YANLIŞ — secret log'a yazılır
logger.info(f"Connecting to DB with password: {db_password}")
logger.debug(f"API Key: {api_key}")
# ✅ DOĞRU — secret'ları asla loglama
logger.info(f"Connecting to DB at {db_host}:{db_port}/{db_name}")
logger.debug("API key is configured" if api_key else "API key is MISSING")Hata 3: Default Secret Key
# ❌ YANLIŞ — default secret key production'a kaçar
SECRET_KEY = os.environ.get("SECRET_KEY", "my-default-secret")
# ✅ DOĞRU — production'da zorunlu, development'ta default
import os
env = os.environ.get("APP_ENV", "development")
if env == "production":
SECRET_KEY = os.environ["SECRET_KEY"] # Yoksa hata — uygulamayı başlatma
else:
SECRET_KEY = os.environ.get("SECRET_KEY", "dev-only-not-secret")9. Secrets Yönetimi: Production Seviyesi
Ortam değişkenleri iyi bir başlangıçtır ama büyük ölçekli sistemlerde yetmez. Ortam değişkenlerinin sınırları var:
Process listesinde görünebilir (
/proc/pid/environ)Crash dump'larda ve log'larda sızabilir
Rotation (düzenli değiştirme) zor
Erişim kontrolü yok — process'in tüm ortam değişkenlerine erişimi var
Secrets Manager Çözümleri
# AWS Secrets Manager örneği
import boto3
import json
def get_secret(secret_name: str) -> dict:
client = boto3.client("secretsmanager", region_name="eu-west-1")
response = client.get_secret_value(SecretId=secret_name)
return json.loads(response["SecretString"])
# Kullanım
db_secrets = get_secret("production/database")
db_host = db_secrets["host"]
db_password = db_secrets["password"]# HashiCorp Vault örneği
import hvac
client = hvac.Client(url="https://vault.company.com:8200")
client.token = os.environ["VAULT_TOKEN"] # Vault token'ı ortam değişkeninde
# Secret oku
secret = client.secrets.kv.v2.read_secret_version(path="myapp/database")
db_password = secret["data"]["data"]["password"]Ne Zaman Ne Kullanmalı?
Proje Boyutu Yöntem
────────────────── ──────────────────────────────
Kişisel proje .env dosyası yeterli
Küçük takım .env + ortam değişkenleri
Orta ölçek Docker/K8s secrets
Büyük ölçek / Finans Vault, AWS Secrets ManagerKişisel ve küçük projelerde .env + python-dotenv gayet yeterlidir. Vault gibi araçları öğrenmek faydalı ama her projeye gerekli değil. Önemli olan temel prensibi kavramak: secret'lar kodda değil, konfigürasyonda yaşar.
10. Gerçek Dünya Örneği: Flask Uygulaması
Tüm kavramları birleştiren eksiksiz bir Flask uygulaması:
# config.py — merkezi konfigürasyon
from pydantic_settings import BaseSettings
from pydantic import Field, field_validator
class Settings(BaseSettings):
# Uygulama
app_name: str = "MyAPI"
app_env: str = "development"
debug: bool = False
port: int = Field(default=8000, ge=1024, le=65535)
secret_key: str
# Veritabanı
database_url: str = "sqlite:///dev.db"
# Redis (opsiyonel)
redis_url: str = "redis://localhost:6379/0"
# API
api_rate_limit: int = 100 # requests per minute
cors_origins: list[str] = ["http://localhost:3000"]
# Email
smtp_host: str = "localhost"
smtp_port: int = 587
smtp_user: str = ""
smtp_password: str = ""
model_config = {
"env_file": ".env",
"env_file_encoding": "utf-8",
}
@property
def is_production(self) -> bool:
return self.app_env == "production"
@field_validator("secret_key")
@classmethod
def secret_key_min_length(cls, v):
if len(v) < 16:
raise ValueError("secret_key must be at least 16 characters")
return v
# Singleton — tüm uygulama boyunca tek instance
settings = Settings()# app.py — Flask uygulaması
from flask import Flask, jsonify
from config import settings
def create_app() -> Flask:
app = Flask(__name__)
# Config'i Flask'a aktar
app.config["DEBUG"] = settings.debug
app.config["SECRET_KEY"] = settings.secret_key
app.config["SQLALCHEMY_DATABASE_URI"] = settings.database_url
@app.route("/health")
def health():
return jsonify({
"status": "healthy",
"app": settings.app_name,
"environment": settings.app_env,
# Secret bilgileri döndürme!
})
return app
if __name__ == "__main__":
app = create_app()
app.run(
host="0.0.0.0",
port=settings.port,
debug=settings.debug,
)# .env
APP_NAME=MyAPI
APP_ENV=development
DEBUG=true
PORT=8000
SECRET_KEY=dev-secret-key-minimum-16-chars
DATABASE_URL=sqlite:///dev.db
REDIS_URL=redis://localhost:6379/0
API_RATE_LIMIT=1000
CORS_ORIGINS=["http://localhost:3000"]
SMTP_HOST=localhost
SMTP_PORT=1025
SMTP_USER=
SMTP_PASSWORD=# .env.example — repo'da bu dosya commit'lenir
APP_NAME=MyAPI
APP_ENV=development
DEBUG=true
PORT=8000
SECRET_KEY=<generate-with: python -c "import secrets; print(secrets.token_hex(32))">
DATABASE_URL=sqlite:///dev.db
REDIS_URL=redis://localhost:6379/0
API_RATE_LIMIT=100
CORS_ORIGINS=["http://localhost:3000"]
SMTP_HOST=
SMTP_PORT=587
SMTP_USER=
SMTP_PASSWORD=Bu yapıda config.py tüm konfigürasyonu merkezi olarak yönetir. Flask uygulaması settings objesinden ihtiyacı olan değerleri alır. Yeni bir geliştirici .env.example'ı kopyalar, kendi değerlerini girer ve projeyi çalıştırır. Production'da .env dosyası yerine gerçek ortam değişkenleri kullanılır.
⚠️ Dikkat: Secret key üretmek için python -c "import secrets; print(secrets.token_hex(32))" komutunu kullan. "password123" gibi tahmin edilebilir key'ler kullanma.
11. Docker ile Config Yönetimi
Containerized uygulamalarda konfigürasyon ortam değişkenleriyle enjekte edilir:
# Dockerfile
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
# Default değerler — production'da override edilir
ENV APP_ENV=production \
PORT=8000 \
LOG_LEVEL=WARNING
EXPOSE 8000
CMD ["python", "app.py"]# docker-compose.yml
version: "3.8"
services:
app:
build: .
ports:
- "8000:8000"
environment:
- APP_ENV=production
- DEBUG=false
- DATABASE_URL=postgresql://user:pass@db:5432/myapp
- SECRET_KEY=${SECRET_KEY} # Host'un ortam değişkeninden
- REDIS_URL=redis://redis:6379/0
env_file:
- .env.production # Veya dosyadan yükle
depends_on:
- db
- redis
db:
image: postgres:16
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
POSTGRES_DB: myapp
redis:
image: redis:7-alpineDocker Compose'da environment ve env_file birlikte kullanılabilir. ${SECRET_KEY} syntax'ı host makinedeki ortam değişkenini container'a aktarır — böylece hassas değerler docker-compose.yml dosyasında yazılmaz.
Özet
Hardcoded credentials güvenlik ihlalidir — API key, şifre, connection string asla kodda bulunmamalıdır.
`os.environ` ile ortam değişkenlerini okursun. Kritik değişkenler için
os.environ["KEY"](KeyError fırlatsın), opsiyoneller için.get("KEY", default)kullan.python-dotenv ile
.envdosyasından ortam değişkenlerini yüklersin..envdosyasını.gitignore'a ekle,.env.exampledosyasını commit'le.Konfigürasyon hiyerarşisi: kod default'ları <
.envdosyası < ortam değişkenleri < CLI argümanları. Üst katman alt katmanı ezer.pydantic-settings ile tip güvenli, validasyonlu konfigürasyon yönetirsin — string'den int/bool/list dönüşümü otomatik, yanlış değerlerde uygulama başlamaz.
12-Factor App prensibi: konfigürasyon koddan ayrılmalı, ortam değişkenlerinde yaşamalı. Aynı kod, farklı config ile farklı ortamlarda çalışmalı.
AI Asistan
Sorularını yanıtlamaya hazır